| author | |
| committer | |
| log | 099af0e008162adf5cb7dc08946bd19b20db817b |
| tree | 56be882a2939f034f56f099023f1ff20af408d79 |
| parent | aef3e534f5bc59b2572afdb74178d8c8b3fa4481 |
4 files changed, 3871 insertions(+), 3871 deletions(-)
CMakeLists.txt+1-1| ... | @@ -583,7 +583,7 @@ set(ZIG_STAGE2_SOURCES | ... | @@ -583,7 +583,7 @@ set(ZIG_STAGE2_SOURCES |
| 583 | "${CMAKE_SOURCE_DIR}/src/value.zig" | 583 | "${CMAKE_SOURCE_DIR}/src/value.zig" |
| 584 | "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig" | 584 | "${CMAKE_SOURCE_DIR}/src/windows_sdk.zig" |
| 585 | "${CMAKE_SOURCE_DIR}/src/zir.zig" | 585 | "${CMAKE_SOURCE_DIR}/src/zir.zig" |
| 586 | "${CMAKE_SOURCE_DIR}/src/zir_sema.zig" | 586 | "${CMAKE_SOURCE_DIR}/src/Sema.zig" |
| 587 | ) | 587 | ) |
| 588 | 588 | ||
| 589 | if(MSVC) | 589 | if(MSVC) |
src/Module.zig+1-1| ... | @@ -24,7 +24,7 @@ const ir = @import("ir.zig"); | ... | @@ -24,7 +24,7 @@ const ir = @import("ir.zig"); |
| 24 | const zir = @import("zir.zig"); | 24 | const zir = @import("zir.zig"); |
| 25 | const trace = @import("tracy.zig").trace; | 25 | const trace = @import("tracy.zig").trace; |
| 26 | const astgen = @import("astgen.zig"); | 26 | const astgen = @import("astgen.zig"); |
| 27 | const Sema = @import("zir_sema.zig"); // TODO rename this file | 27 | const Sema = @import("Sema.zig"); |
| 28 | const target_util = @import("target.zig"); | 28 | const target_util = @import("target.zig"); |
| 29 | 29 | ||
| 30 | /// General-purpose allocator. Used for both temporary and long-term storage. | 30 | /// General-purpose allocator. Used for both temporary and long-term storage. |
src/Sema.zig created+3869| ... | @@ -0,0 +1,3869 @@ | ||
| 1 | //! Semantic analysis of ZIR instructions. | ||
| 2 | //! Shared to every Block. Stored on the stack. | ||
| 3 | //! State used for compiling a `zir.Code` into TZIR. | ||
| 4 | //! Transforms untyped ZIR instructions into semantically-analyzed TZIR instructions. | ||
| 5 | //! Does type checking, comptime control flow, and safety-check generation. | ||
| 6 | //! This is the the heart of the Zig compiler. | ||
| 7 | |||
| 8 | mod: *Module, | ||
| 9 | /// Same as `mod.gpa`. | ||
| 10 | gpa: *Allocator, | ||
| 11 | /// Points to the arena allocator of the Decl. | ||
| 12 | arena: *Allocator, | ||
| 13 | code: zir.Code, | ||
| 14 | /// Maps ZIR to TZIR. | ||
| 15 | inst_map: []*const Inst, | ||
| 16 | /// When analyzing an inline function call, owner_decl is the Decl of the caller | ||
| 17 | /// and `src_decl` of `Scope.Block` is the `Decl` of the callee. | ||
| 18 | /// This `Decl` owns the arena memory of this `Sema`. | ||
| 19 | owner_decl: *Decl, | ||
| 20 | func: ?*Module.Fn, | ||
| 21 | /// For now, TZIR requires arg instructions to be the first N instructions in the | ||
| 22 | /// TZIR code. We store references here for the purpose of `resolveInst`. | ||
| 23 | /// This can get reworked with TZIR memory layout changes, into simply: | ||
| 24 | /// > Denormalized data to make `resolveInst` faster. This is 0 if not inside a function, | ||
| 25 | /// > otherwise it is the number of parameters of the function. | ||
| 26 | /// > param_count: u32 | ||
| 27 | param_inst_list: []const *ir.Inst, | ||
| 28 | branch_quota: u32 = 1000, | ||
| 29 | /// This field is updated when a new source location becomes active, so that | ||
| 30 | /// instructions which do not have explicitly mapped source locations still have | ||
| 31 | /// access to the source location set by the previous instruction which did | ||
| 32 | /// contain a mapped source location. | ||
| 33 | src: LazySrcLoc = .{ .token_offset = 0 }, | ||
| 34 | |||
| 35 | const std = @import("std"); | ||
| 36 | const mem = std.mem; | ||
| 37 | const Allocator = std.mem.Allocator; | ||
| 38 | const assert = std.debug.assert; | ||
| 39 | const log = std.log.scoped(.sema); | ||
| 40 | |||
| 41 | const Sema = @This(); | ||
| 42 | const Value = @import("value.zig").Value; | ||
| 43 | const Type = @import("type.zig").Type; | ||
| 44 | const TypedValue = @import("TypedValue.zig"); | ||
| 45 | const ir = @import("ir.zig"); | ||
| 46 | const zir = @import("zir.zig"); | ||
| 47 | const Module = @import("Module.zig"); | ||
| 48 | const Inst = ir.Inst; | ||
| 49 | const Body = ir.Body; | ||
| 50 | const trace = @import("tracy.zig").trace; | ||
| 51 | const Scope = Module.Scope; | ||
| 52 | const InnerError = Module.InnerError; | ||
| 53 | const Decl = Module.Decl; | ||
| 54 | const LazySrcLoc = Module.LazySrcLoc; | ||
| 55 | |||
| 56 | // TODO when memory layout of TZIR is reworked, this can be simplified. | ||
| 57 | const const_tzir_inst_list = blk: { | ||
| 58 | var result: [zir.const_inst_list.len]ir.Inst.Const = undefined; | ||
| 59 | for (result) |*tzir_const, i| { | ||
| 60 | tzir_const.* = .{ | ||
| 61 | .base = .{ | ||
| 62 | .tag = .constant, | ||
| 63 | .ty = zir.const_inst_list[i].ty, | ||
| 64 | .src = 0, | ||
| 65 | }, | ||
| 66 | .val = zir.const_inst_list[i].val, | ||
| 67 | }; | ||
| 68 | } | ||
| 69 | break :blk result; | ||
| 70 | }; | ||
| 71 | |||
| 72 | pub fn root(sema: *Sema, root_block: *Scope.Block) !void { | ||
| 73 | const root_body = sema.code.extra[sema.code.root_start..][0..sema.code.root_len]; | ||
| 74 | return sema.body(root_block, root_body); | ||
| 75 | } | ||
| 76 | |||
| 77 | pub fn rootAsType( | ||
| 78 | sema: *Sema, | ||
| 79 | root_block: *Scope.Block, | ||
| 80 | zir_result_inst: zir.Inst.Index, | ||
| 81 | body: zir.Body, | ||
| 82 | ) !Type { | ||
| 83 | const root_body = sema.code.extra[sema.code.root_start..][0..sema.code.root_len]; | ||
| 84 | try sema.body(root_block, root_body); | ||
| 85 | |||
| 86 | const result_inst = sema.inst_map[zir_result_inst]; | ||
| 87 | // Source location is unneeded because resolveConstValue must have already | ||
| 88 | // been successfully called when coercing the value to a type, from the | ||
| 89 | // result location. | ||
| 90 | const val = try sema.resolveConstValue(root_block, .unneeded, result_inst); | ||
| 91 | return val.toType(root_block.arena); | ||
| 92 | } | ||
| 93 | |||
| 94 | pub fn body(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Index) !void { | ||
| 95 | const tracy = trace(@src()); | ||
| 96 | defer tracy.end(); | ||
| 97 | |||
| 98 | const map = block.sema.inst_map; | ||
| 99 | const tags = block.sema.code.instructions.items(.tag); | ||
| 100 | |||
| 101 | // TODO: As an optimization, look into making these switch prongs directly jump | ||
| 102 | // to the next one, rather than detouring through the loop condition. | ||
| 103 | // Also, look into leaving only the "noreturn" loop break condition, and removing | ||
| 104 | // the iteration based one. Better yet, have an extra entry in the tags array as a | ||
| 105 | // sentinel, so that exiting the loop is just another jump table prong. | ||
| 106 | // Related: https://github.com/ziglang/zig/issues/8220 | ||
| 107 | for (body) |zir_inst| { | ||
| 108 | map[zir_inst] = switch (tags[zir_inst]) { | ||
| 109 | .alloc => try sema.zirAlloc(block, zir_inst), | ||
| 110 | .alloc_mut => try sema.zirAllocMut(block, zir_inst), | ||
| 111 | .alloc_inferred => try sema.zirAllocInferred(block, zir_inst, Type.initTag(.inferred_alloc_const)), | ||
| 112 | .alloc_inferred_mut => try sema.zirAllocInferred(block, zir_inst, Type.initTag(.inferred_alloc_mut)), | ||
| 113 | .bitcast_ref => try sema.zirBitcastRef(block, zir_inst), | ||
| 114 | .bitcast_result_ptr => try sema.zirBitcastResultPtr(block, zir_inst), | ||
| 115 | .block => try sema.zirBlock(block, zir_inst, false), | ||
| 116 | .block_comptime => try sema.zirBlock(block, zir_inst, true), | ||
| 117 | .block_flat => try sema.zirBlockFlat(block, zir_inst, false), | ||
| 118 | .block_comptime_flat => try sema.zirBlockFlat(block, zir_inst, true), | ||
| 119 | .@"break" => try sema.zirBreak(block, zir_inst), | ||
| 120 | .break_void_tok => try sema.zirBreakVoidTok(block, zir_inst), | ||
| 121 | .breakpoint => try sema.zirBreakpoint(block, zir_inst), | ||
| 122 | .call => try sema.zirCall(block, zir_inst, .auto), | ||
| 123 | .call_async_kw => try sema.zirCall(block, zir_inst, .async_kw), | ||
| 124 | .call_no_async => try sema.zirCall(block, zir_inst, .no_async), | ||
| 125 | .call_compile_time => try sema.zirCall(block, zir_inst, .compile_time), | ||
| 126 | .call_none => try sema.zirCallNone(block, zir_inst), | ||
| 127 | .coerce_result_ptr => try sema.zirCoerceResultPtr(block, zir_inst), | ||
| 128 | .compile_error => try sema.zirCompileError(block, zir_inst), | ||
| 129 | .compile_log => try sema.zirCompileLog(block, zir_inst), | ||
| 130 | .@"const" => try sema.zirConst(block, zir_inst), | ||
| 131 | .dbg_stmt_node => try sema.zirDbgStmtNode(block, zir_inst), | ||
| 132 | .decl_ref => try sema.zirDeclRef(block, zir_inst), | ||
| 133 | .decl_val => try sema.zirDeclVal(block, zir_inst), | ||
| 134 | .ensure_result_used => try sema.zirEnsureResultUsed(block, zir_inst), | ||
| 135 | .ensure_result_non_error => try sema.zirEnsureResultNonError(block, zir_inst), | ||
| 136 | .indexable_ptr_len => try sema.zirIndexablePtrLen(block, zir_inst), | ||
| 137 | .ref => try sema.zirRef(block, zir_inst), | ||
| 138 | .resolve_inferred_alloc => try sema.zirResolveInferredAlloc(block, zir_inst), | ||
| 139 | .ret_ptr => try sema.zirRetPtr(block, zir_inst), | ||
| 140 | .ret_type => try sema.zirRetType(block, zir_inst), | ||
| 141 | .store_to_block_ptr => try sema.zirStoreToBlockPtr(block, zir_inst), | ||
| 142 | .store_to_inferred_ptr => try sema.zirStoreToInferredPtr(block, zir_inst), | ||
| 143 | .ptr_type_simple => try sema.zirPtrTypeSimple(block, zir_inst), | ||
| 144 | .ptr_type => try sema.zirPtrType(block, zir_inst), | ||
| 145 | .store => try sema.zirStore(block, zir_inst), | ||
| 146 | .set_eval_branch_quota => try sema.zirSetEvalBranchQuota(block, zir_inst), | ||
| 147 | .str => try sema.zirStr(block, zir_inst), | ||
| 148 | .int => try sema.zirInt(block, zir_inst), | ||
| 149 | .int_type => try sema.zirIntType(block, zir_inst), | ||
| 150 | .loop => try sema.zirLoop(block, zir_inst), | ||
| 151 | .param_type => try sema.zirParamType(block, zir_inst), | ||
| 152 | .ptrtoint => try sema.zirPtrtoint(block, zir_inst), | ||
| 153 | .field_ptr => try sema.zirFieldPtr(block, zir_inst), | ||
| 154 | .field_val => try sema.zirFieldVal(block, zir_inst), | ||
| 155 | .field_ptr_named => try sema.zirFieldPtrNamed(block, zir_inst), | ||
| 156 | .field_val_named => try sema.zirFieldValNamed(block, zir_inst), | ||
| 157 | .deref => try sema.zirDeref(block, zir_inst), | ||
| 158 | .as => try sema.zirAs(block, zir_inst), | ||
| 159 | .@"asm" => try sema.zirAsm(block, zir_inst, false), | ||
| 160 | .asm_volatile => try sema.zirAsm(block, zir_inst, true), | ||
| 161 | .unreachable_safe => try sema.zirUnreachable(block, zir_inst, true), | ||
| 162 | .unreachable_unsafe => try sema.zirUnreachable(block, zir_inst, false), | ||
| 163 | .ret_tok => try sema.zirRetTok(block, zir_inst), | ||
| 164 | .ret_node => try sema.zirRetNode(block, zir_inst), | ||
| 165 | .fn_type => try sema.zirFnType(block, zir_inst), | ||
| 166 | .fn_type_cc => try sema.zirFnTypeCc(block, zir_inst), | ||
| 167 | .intcast => try sema.zirIntcast(block, zir_inst), | ||
| 168 | .bitcast => try sema.zirBitcast(block, zir_inst), | ||
| 169 | .floatcast => try sema.zirFloatcast(block, zir_inst), | ||
| 170 | .elem_ptr => try sema.zirElemPtr(block, zir_inst), | ||
| 171 | .elem_ptr_node => try sema.zirElemPtrNode(block, zir_inst), | ||
| 172 | .elem_val => try sema.zirElemVal(block, zir_inst), | ||
| 173 | .elem_val_node => try sema.zirElemValNode(block, zir_inst), | ||
| 174 | .add => try sema.zirArithmetic(block, zir_inst), | ||
| 175 | .addwrap => try sema.zirArithmetic(block, zir_inst), | ||
| 176 | .sub => try sema.zirArithmetic(block, zir_inst), | ||
| 177 | .subwrap => try sema.zirArithmetic(block, zir_inst), | ||
| 178 | .mul => try sema.zirArithmetic(block, zir_inst), | ||
| 179 | .mulwrap => try sema.zirArithmetic(block, zir_inst), | ||
| 180 | .div => try sema.zirArithmetic(block, zir_inst), | ||
| 181 | .mod_rem => try sema.zirArithmetic(block, zir_inst), | ||
| 182 | .array_cat => try sema.zirArrayCat(block, zir_inst), | ||
| 183 | .array_mul => try sema.zirArrayMul(block, zir_inst), | ||
| 184 | .bit_and => try sema.zirBitwise(block, zir_inst), | ||
| 185 | .bit_not => try sema.zirBitNot(block, zir_inst), | ||
| 186 | .bit_or => try sema.zirBitwise(block, zir_inst), | ||
| 187 | .xor => try sema.zirBitwise(block, zir_inst), | ||
| 188 | .shl => try sema.zirShl(block, zir_inst), | ||
| 189 | .shr => try sema.zirShr(block, zir_inst), | ||
| 190 | .cmp_lt => try sema.zirCmp(block, zir_inst, .lt), | ||
| 191 | .cmp_lte => try sema.zirCmp(block, zir_inst, .lte), | ||
| 192 | .cmp_eq => try sema.zirCmp(block, zir_inst, .eq), | ||
| 193 | .cmp_gte => try sema.zirCmp(block, zir_inst, .gte), | ||
| 194 | .cmp_gt => try sema.zirCmp(block, zir_inst, .gt), | ||
| 195 | .cmp_neq => try sema.zirCmp(block, zir_inst, .neq), | ||
| 196 | .condbr => try sema.zirCondbr(block, zir_inst), | ||
| 197 | .is_null => try sema.zirIsNull(block, zir_inst, false), | ||
| 198 | .is_non_null => try sema.zirIsNull(block, zir_inst, true), | ||
| 199 | .is_null_ptr => try sema.zirIsNullPtr(block, zir_inst, false), | ||
| 200 | .is_non_null_ptr => try sema.zirIsNullPtr(block, zir_inst, true), | ||
| 201 | .is_err => try sema.zirIsErr(block, zir_inst), | ||
| 202 | .is_err_ptr => try sema.zirIsErrPtr(block, zir_inst), | ||
| 203 | .bool_not => try sema.zirBoolNot(block, zir_inst), | ||
| 204 | .typeof => try sema.zirTypeof(block, zir_inst), | ||
| 205 | .typeof_peer => try sema.zirTypeofPeer(block, zir_inst), | ||
| 206 | .optional_type => try sema.zirOptionalType(block, zir_inst), | ||
| 207 | .optional_type_from_ptr_elem => try sema.zirOptionalTypeFromPtrElem(block, zir_inst), | ||
| 208 | .optional_payload_safe => try sema.zirOptionalPayload(block, zir_inst, true), | ||
| 209 | .optional_payload_unsafe => try sema.zirOptionalPayload(block, zir_inst, false), | ||
| 210 | .optional_payload_safe_ptr => try sema.zirOptionalPayloadPtr(block, zir_inst, true), | ||
| 211 | .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, zir_inst, false), | ||
| 212 | .err_union_payload_safe => try sema.zirErrUnionPayload(block, zir_inst, true), | ||
| 213 | .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, zir_inst, false), | ||
| 214 | .err_union_payload_safe_ptr => try sema.zirErrUnionPayloadPtr(block, zir_inst, true), | ||
| 215 | .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, zir_inst, false), | ||
| 216 | .err_union_code => try sema.zirErrUnionCode(block, zir_inst), | ||
| 217 | .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, zir_inst), | ||
| 218 | .ensure_err_payload_void => try sema.zirEnsureErrPayloadVoid(block, zir_inst), | ||
| 219 | .array_type => try sema.zirArrayType(block, zir_inst), | ||
| 220 | .array_type_sentinel => try sema.zirArrayTypeSentinel(block, zir_inst), | ||
| 221 | .enum_literal => try sema.zirEnumLiteral(block, zir_inst), | ||
| 222 | .merge_error_sets => try sema.zirMergeErrorSets(block, zir_inst), | ||
| 223 | .error_union_type => try sema.zirErrorUnionType(block, zir_inst), | ||
| 224 | .anyframe_type => try sema.zirAnyframeType(block, zir_inst), | ||
| 225 | .error_set => try sema.zirErrorSet(block, zir_inst), | ||
| 226 | .error_value => try sema.zirErrorValue(block, zir_inst), | ||
| 227 | .slice_start => try sema.zirSliceStart(block, zir_inst), | ||
| 228 | .slice_end => try sema.zirSliceEnd(block, zir_inst), | ||
| 229 | .slice_sentinel => try sema.zirSliceSentinel(block, zir_inst), | ||
| 230 | .import => try sema.zirImport(block, zir_inst), | ||
| 231 | .bool_and => try sema.zirBoolOp(block, zir_inst, false), | ||
| 232 | .bool_or => try sema.zirBoolOp(block, zir_inst, true), | ||
| 233 | .void_value => try sema.mod.constVoid(block.arena, .unneeded), | ||
| 234 | .switchbr => try sema.zirSwitchBr(block, zir_inst, false), | ||
| 235 | .switchbr_ref => try sema.zirSwitchBr(block, zir_inst, true), | ||
| 236 | .switch_range => try sema.zirSwitchRange(block, zir_inst), | ||
| 237 | }; | ||
| 238 | if (map[zir_inst].ty.isNoReturn()) { | ||
| 239 | break; | ||
| 240 | } | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | fn resolveInst(sema: *Sema, block: *Scope.Block, zir_ref: zir.Inst.Ref) *const ir.Inst { | ||
| 245 | var i = zir_ref; | ||
| 246 | |||
| 247 | // First section of indexes correspond to a set number of constant values. | ||
| 248 | if (i < const_tzir_inst_list.len) { | ||
| 249 | return &const_tzir_inst_list[i]; | ||
| 250 | } | ||
| 251 | i -= const_tzir_inst_list.len; | ||
| 252 | |||
| 253 | // Next section of indexes correspond to function parameters, if any. | ||
| 254 | if (block.inlining) |inlining| { | ||
| 255 | if (i < inlining.casted_args.len) { | ||
| 256 | return inlining.casted_args[i]; | ||
| 257 | } | ||
| 258 | i -= inlining.casted_args.len; | ||
| 259 | } else { | ||
| 260 | if (i < sema.param_inst_list.len) { | ||
| 261 | return sema.param_inst_list[i]; | ||
| 262 | } | ||
| 263 | i -= sema.param_inst_list.len; | ||
| 264 | } | ||
| 265 | |||
| 266 | // Finally, the last section of indexes refers to the map of ZIR=>TZIR. | ||
| 267 | return sema.inst_map[i]; | ||
| 268 | } | ||
| 269 | |||
| 270 | fn resolveConstString( | ||
| 271 | sema: *Sema, | ||
| 272 | block: *Scope.Block, | ||
| 273 | src: LazySrcLoc, | ||
| 274 | zir_ref: zir.Inst.Ref, | ||
| 275 | ) ![]u8 { | ||
| 276 | const tzir_inst = sema.resolveInst(block, zir_ref); | ||
| 277 | const wanted_type = Type.initTag(.const_slice_u8); | ||
| 278 | const coerced_inst = try sema.coerce(block, wanted_type, tzir_inst); | ||
| 279 | const val = try sema.resolveConstValue(block, src, coerced_inst); | ||
| 280 | return val.toAllocatedBytes(block.arena); | ||
| 281 | } | ||
| 282 | |||
| 283 | fn resolveType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, zir_ref: zir.Inst.Ref) !Type { | ||
| 284 | const tzir_inst = sema.resolveInt(block, zir_ref); | ||
| 285 | const wanted_type = Type.initTag(.@"type"); | ||
| 286 | const coerced_inst = try sema.coerce(block, wanted_type, tzir_inst); | ||
| 287 | const val = try sema.resolveConstValue(block, src, coerced_inst); | ||
| 288 | return val.toType(sema.arena); | ||
| 289 | } | ||
| 290 | |||
| 291 | fn resolveConstValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base: *ir.Inst) !Value { | ||
| 292 | return (try sema.resolveDefinedValue(block, src, base)) orelse | ||
| 293 | return sema.mod.fail(&block.base, src, "unable to resolve comptime value", .{}); | ||
| 294 | } | ||
| 295 | |||
| 296 | fn resolveDefinedValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base: *ir.Inst) !?Value { | ||
| 297 | if (base.value()) |val| { | ||
| 298 | if (val.isUndef()) { | ||
| 299 | return sema.mod.fail(&block.base, src, "use of undefined value here causes undefined behavior", .{}); | ||
| 300 | } | ||
| 301 | return val; | ||
| 302 | } | ||
| 303 | return null; | ||
| 304 | } | ||
| 305 | |||
| 306 | /// Appropriate to call when the coercion has already been done by result | ||
| 307 | /// location semantics. Asserts the value fits in the provided `Int` type. | ||
| 308 | /// Only supports `Int` types 64 bits or less. | ||
| 309 | fn resolveAlreadyCoercedInt( | ||
| 310 | sema: *Sema, | ||
| 311 | block: *Scope.Block, | ||
| 312 | src: LazySrcLoc, | ||
| 313 | zir_ref: zir.Inst.Ref, | ||
| 314 | comptime Int: type, | ||
| 315 | ) !Int { | ||
| 316 | comptime assert(@typeInfo(Int).Int.bits <= 64); | ||
| 317 | const tzir_inst = sema.resolveInst(block, zir_ref); | ||
| 318 | const val = try sema.resolveConstValue(block, src, tzir_inst); | ||
| 319 | switch (@typeInfo(Int).Int.signedness) { | ||
| 320 | .signed => return @intCast(Int, val.toSignedInt()), | ||
| 321 | .unsigned => return @intCast(Int, val.toUnsignedInt()), | ||
| 322 | } | ||
| 323 | } | ||
| 324 | |||
| 325 | fn resolveInt( | ||
| 326 | sema: *Sema, | ||
| 327 | block: *Scope.Block, | ||
| 328 | src: LazySrcLoc, | ||
| 329 | zir_ref: zir.Inst.Ref, | ||
| 330 | dest_type: Type, | ||
| 331 | ) !u64 { | ||
| 332 | const tzir_inst = sema.resolveInst(block, zir_ref); | ||
| 333 | const coerced = try sema.coerce(scope, dest_type, tzir_inst); | ||
| 334 | const val = try sema.resolveConstValue(block, src, coerced); | ||
| 335 | |||
| 336 | return val.toUnsignedInt(); | ||
| 337 | } | ||
| 338 | |||
| 339 | fn resolveInstConst( | ||
| 340 | sema: *Sema, | ||
| 341 | block: *Scope.Block, | ||
| 342 | src: LazySrcLoc, | ||
| 343 | zir_ref: zir.Inst.Ref, | ||
| 344 | ) InnerError!TypedValue { | ||
| 345 | const tzir_inst = sema.resolveInst(block, zir_ref); | ||
| 346 | const val = try sema.resolveConstValue(block, src, tzir_inst); | ||
| 347 | return TypedValue{ | ||
| 348 | .ty = tzir_inst.ty, | ||
| 349 | .val = val, | ||
| 350 | }; | ||
| 351 | } | ||
| 352 | |||
| 353 | fn zirConst(sema: *Sema, block: *Scope.Block, const_inst: zir.Inst.Index) InnerError!*Inst { | ||
| 354 | const tracy = trace(@src()); | ||
| 355 | defer tracy.end(); | ||
| 356 | // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions | ||
| 357 | // after analysis. | ||
| 358 | const typed_value_copy = try const_inst.positionals.typed_value.copy(block.arena); | ||
| 359 | return sema.mod.constInst(scope, const_inst.base.src, typed_value_copy); | ||
| 360 | } | ||
| 361 | |||
| 362 | fn zirBitcastRef(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 363 | const tracy = trace(@src()); | ||
| 364 | defer tracy.end(); | ||
| 365 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement zir_sema.zirBitcastRef", .{}); | ||
| 366 | } | ||
| 367 | |||
| 368 | fn zirBitcastResultPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 369 | const tracy = trace(@src()); | ||
| 370 | defer tracy.end(); | ||
| 371 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement zir_sema.zirBitcastResultPtr", .{}); | ||
| 372 | } | ||
| 373 | |||
| 374 | fn zirCoerceResultPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 375 | const tracy = trace(@src()); | ||
| 376 | defer tracy.end(); | ||
| 377 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement zirCoerceResultPtr", .{}); | ||
| 378 | } | ||
| 379 | |||
| 380 | fn zirRetPtr(sema: *Module, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 381 | const tracy = trace(@src()); | ||
| 382 | defer tracy.end(); | ||
| 383 | |||
| 384 | try sema.requireFunctionBlock(block, inst.base.src); | ||
| 385 | const fn_ty = block.func.?.owner_decl.typed_value.most_recent.typed_value.ty; | ||
| 386 | const ret_type = fn_ty.fnReturnType(); | ||
| 387 | const ptr_type = try sema.mod.simplePtrType(block.arena, ret_type, true, .One); | ||
| 388 | return block.addNoOp(inst.base.src, ptr_type, .alloc); | ||
| 389 | } | ||
| 390 | |||
| 391 | fn zirRef(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 392 | const tracy = trace(@src()); | ||
| 393 | defer tracy.end(); | ||
| 394 | |||
| 395 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 396 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 397 | return sema.analyzeRef(block, inst_data.src(), operand); | ||
| 398 | } | ||
| 399 | |||
| 400 | fn zirRetType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 401 | const tracy = trace(@src()); | ||
| 402 | defer tracy.end(); | ||
| 403 | try sema.requireFunctionBlock(block, inst.base.src); | ||
| 404 | const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty; | ||
| 405 | const ret_type = fn_ty.fnReturnType(); | ||
| 406 | return sema.mod.constType(block.arena, inst.base.src, ret_type); | ||
| 407 | } | ||
| 408 | |||
| 409 | fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 410 | const tracy = trace(@src()); | ||
| 411 | defer tracy.end(); | ||
| 412 | |||
| 413 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 414 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 415 | const src = inst_data.src(); | ||
| 416 | switch (operand.ty.zigTypeTag()) { | ||
| 417 | .Void, .NoReturn => return sema.mod.constVoid(block.arena, .unneeded), | ||
| 418 | else => return sema.mod.fail(&block.base, src, "expression value is ignored", .{}), | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 422 | fn zirEnsureResultNonError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 423 | const tracy = trace(@src()); | ||
| 424 | defer tracy.end(); | ||
| 425 | |||
| 426 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 427 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 428 | const src = inst_data.src(); | ||
| 429 | switch (operand.ty.zigTypeTag()) { | ||
| 430 | .ErrorSet, .ErrorUnion => return sema.mod.fail(&block.base, src, "error is discarded", .{}), | ||
| 431 | else => return sema.mod.constVoid(block.arena, .unneeded), | ||
| 432 | } | ||
| 433 | } | ||
| 434 | |||
| 435 | fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 436 | const tracy = trace(@src()); | ||
| 437 | defer tracy.end(); | ||
| 438 | |||
| 439 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 440 | const array_ptr = sema.resolveInst(block, inst_data.operand); | ||
| 441 | |||
| 442 | const elem_ty = array_ptr.ty.elemType(); | ||
| 443 | if (!elem_ty.isIndexable()) { | ||
| 444 | const cond_src: LazySrcLoc = .{ .node_offset_for_cond = inst_data.src_node }; | ||
| 445 | const msg = msg: { | ||
| 446 | const msg = try sema.mod.errMsg( | ||
| 447 | &block.base, | ||
| 448 | cond_src, | ||
| 449 | "type '{}' does not support indexing", | ||
| 450 | .{elem_ty}, | ||
| 451 | ); | ||
| 452 | errdefer msg.destroy(mod.gpa); | ||
| 453 | try sema.mod.errNote( | ||
| 454 | &block.base, | ||
| 455 | cond_src, | ||
| 456 | msg, | ||
| 457 | "for loop operand must be an array, slice, tuple, or vector", | ||
| 458 | .{}, | ||
| 459 | ); | ||
| 460 | break :msg msg; | ||
| 461 | }; | ||
| 462 | return mod.failWithOwnedErrorMsg(scope, msg); | ||
| 463 | } | ||
| 464 | const result_ptr = try sema.namedFieldPtr(block, inst.base.src, array_ptr, "len", inst.base.src); | ||
| 465 | return sema.analyzeDeref(block, inst.base.src, result_ptr, result_ptr.src); | ||
| 466 | } | ||
| 467 | |||
| 468 | fn zirAlloc(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 469 | const tracy = trace(@src()); | ||
| 470 | defer tracy.end(); | ||
| 471 | |||
| 472 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 473 | const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; | ||
| 474 | const var_decl_src = inst_data.src(); | ||
| 475 | const var_type = try sema.resolveType(block, ty_src, inst_data.operand); | ||
| 476 | const ptr_type = try sema.mod.simplePtrType(block.arena, var_type, true, .One); | ||
| 477 | try sema.requireRuntimeBlock(block, var_decl_src); | ||
| 478 | return block.addNoOp(var_decl_src, ptr_type, .alloc); | ||
| 479 | } | ||
| 480 | |||
| 481 | fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 482 | const tracy = trace(@src()); | ||
| 483 | defer tracy.end(); | ||
| 484 | |||
| 485 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 486 | const var_decl_src = inst_data.src(); | ||
| 487 | const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; | ||
| 488 | const var_type = try sema.resolveType(block, ty_src, inst_data.operand); | ||
| 489 | try sema.validateVarType(block, ty_src, var_type); | ||
| 490 | const ptr_type = try sema.mod.simplePtrType(block.arena, var_type, true, .One); | ||
| 491 | try sema.requireRuntimeBlock(block, var_decl_src); | ||
| 492 | return block.addNoOp(var_decl_src, ptr_type, .alloc); | ||
| 493 | } | ||
| 494 | |||
| 495 | fn zirAllocInferred( | ||
| 496 | sema: *Sema, | ||
| 497 | block: *Scope.Block, | ||
| 498 | inst: zir.Inst.Index, | ||
| 499 | inferred_alloc_ty: Type, | ||
| 500 | ) InnerError!*Inst { | ||
| 501 | const tracy = trace(@src()); | ||
| 502 | defer tracy.end(); | ||
| 503 | const val_payload = try block.arena.create(Value.Payload.InferredAlloc); | ||
| 504 | val_payload.* = .{ | ||
| 505 | .data = .{}, | ||
| 506 | }; | ||
| 507 | // `Module.constInst` does not add the instruction to the block because it is | ||
| 508 | // not needed in the case of constant values. However here, we plan to "downgrade" | ||
| 509 | // to a normal instruction when we hit `resolve_inferred_alloc`. So we append | ||
| 510 | // to the block even though it is currently a `.constant`. | ||
| 511 | const result = try sema.mod.constInst(scope, inst.base.src, .{ | ||
| 512 | .ty = inferred_alloc_ty, | ||
| 513 | .val = Value.initPayload(&val_payload.base), | ||
| 514 | }); | ||
| 515 | try sema.requireFunctionBlock(block, inst.base.src); | ||
| 516 | try block.instructions.append(sema.gpa, result); | ||
| 517 | return result; | ||
| 518 | } | ||
| 519 | |||
| 520 | fn zirResolveInferredAlloc( | ||
| 521 | sema: *Sema, | ||
| 522 | block: *Scope.Block, | ||
| 523 | inst: zir.Inst.Index, | ||
| 524 | ) InnerError!*Inst { | ||
| 525 | const tracy = trace(@src()); | ||
| 526 | defer tracy.end(); | ||
| 527 | |||
| 528 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 529 | const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; | ||
| 530 | const ptr = sema.resolveInst(block, inst_data.operand); | ||
| 531 | const ptr_val = ptr.castTag(.constant).?.val; | ||
| 532 | const inferred_alloc = ptr_val.castTag(.inferred_alloc).?; | ||
| 533 | const peer_inst_list = inferred_alloc.data.stored_inst_list.items; | ||
| 534 | const final_elem_ty = try sema.resolvePeerTypes(block, peer_inst_list); | ||
| 535 | const var_is_mut = switch (ptr.ty.tag()) { | ||
| 536 | .inferred_alloc_const => false, | ||
| 537 | .inferred_alloc_mut => true, | ||
| 538 | else => unreachable, | ||
| 539 | }; | ||
| 540 | if (var_is_mut) { | ||
| 541 | try sema.validateVarType(block, ty_src, final_elem_ty); | ||
| 542 | } | ||
| 543 | const final_ptr_ty = try sema.mod.simplePtrType(block.arena, final_elem_ty, true, .One); | ||
| 544 | |||
| 545 | // Change it to a normal alloc. | ||
| 546 | ptr.ty = final_ptr_ty; | ||
| 547 | ptr.tag = .alloc; | ||
| 548 | |||
| 549 | return sema.mod.constVoid(block.arena, .unneeded); | ||
| 550 | } | ||
| 551 | |||
| 552 | fn zirStoreToBlockPtr( | ||
| 553 | sema: *Sema, | ||
| 554 | block: *Scope.Block, | ||
| 555 | inst: zir.Inst.Index, | ||
| 556 | ) InnerError!*Inst { | ||
| 557 | const tracy = trace(@src()); | ||
| 558 | defer tracy.end(); | ||
| 559 | |||
| 560 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 561 | const ptr = sema.resolveInst(bin_inst.lhs); | ||
| 562 | const value = sema.resolveInst(bin_inst.rhs); | ||
| 563 | const ptr_ty = try sema.mod.simplePtrType(block.arena, value.ty, true, .One); | ||
| 564 | // TODO detect when this store should be done at compile-time. For example, | ||
| 565 | // if expressions should force it when the condition is compile-time known. | ||
| 566 | try sema.requireRuntimeBlock(block, src); | ||
| 567 | const bitcasted_ptr = try block.addUnOp(inst.base.src, ptr_ty, .bitcast, ptr); | ||
| 568 | return mod.storePtr(scope, inst.base.src, bitcasted_ptr, value); | ||
| 569 | } | ||
| 570 | |||
| 571 | fn zirStoreToInferredPtr( | ||
| 572 | sema: *Sema, | ||
| 573 | block: *Scope.Block, | ||
| 574 | inst: zir.Inst.Index, | ||
| 575 | ) InnerError!*Inst { | ||
| 576 | const tracy = trace(@src()); | ||
| 577 | defer tracy.end(); | ||
| 578 | |||
| 579 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 580 | const ptr = sema.resolveInst(bin_inst.lhs); | ||
| 581 | const value = sema.resolveInst(bin_inst.rhs); | ||
| 582 | const inferred_alloc = ptr.castTag(.constant).?.val.castTag(.inferred_alloc).?; | ||
| 583 | // Add the stored instruction to the set we will use to resolve peer types | ||
| 584 | // for the inferred allocation. | ||
| 585 | try inferred_alloc.data.stored_inst_list.append(block.arena, value); | ||
| 586 | // Create a runtime bitcast instruction with exactly the type the pointer wants. | ||
| 587 | const ptr_ty = try sema.mod.simplePtrType(block.arena, value.ty, true, .One); | ||
| 588 | try sema.requireRuntimeBlock(block, src); | ||
| 589 | const bitcasted_ptr = try block.addUnOp(inst.base.src, ptr_ty, .bitcast, ptr); | ||
| 590 | return mod.storePtr(scope, inst.base.src, bitcasted_ptr, value); | ||
| 591 | } | ||
| 592 | |||
| 593 | fn zirSetEvalBranchQuota( | ||
| 594 | sema: *Sema, | ||
| 595 | block: *Scope.Block, | ||
| 596 | inst: zir.Inst.Index, | ||
| 597 | ) InnerError!*Inst { | ||
| 598 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 599 | const src = inst_data.src(); | ||
| 600 | try sema.requireFunctionBlock(block, src); | ||
| 601 | const quota = try sema.resolveAlreadyCoercedInt(block, src, inst_data.operand, u32); | ||
| 602 | if (b.branch_quota.* < quota) | ||
| 603 | b.branch_quota.* = quota; | ||
| 604 | return sema.mod.constVoid(block.arena, .unneeded); | ||
| 605 | } | ||
| 606 | |||
| 607 | fn zirStore(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 608 | const tracy = trace(@src()); | ||
| 609 | defer tracy.end(); | ||
| 610 | |||
| 611 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 612 | const ptr = sema.resolveInst(bin_inst.lhs); | ||
| 613 | const value = sema.resolveInst(bin_inst.rhs); | ||
| 614 | return mod.storePtr(scope, inst.base.src, ptr, value); | ||
| 615 | } | ||
| 616 | |||
| 617 | fn zirParamType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 618 | const tracy = trace(@src()); | ||
| 619 | defer tracy.end(); | ||
| 620 | |||
| 621 | const inst_data = sema.code.instructions.items(.data)[inst].param_type; | ||
| 622 | const fn_inst = sema.resolveInst(inst_data.callee); | ||
| 623 | const param_index = inst_data.param_index; | ||
| 624 | |||
| 625 | const fn_ty: Type = switch (fn_inst.ty.zigTypeTag()) { | ||
| 626 | .Fn => fn_inst.ty, | ||
| 627 | .BoundFn => { | ||
| 628 | return sema.mod.fail(&block.base, fn_inst.src, "TODO implement zirParamType for method call syntax", .{}); | ||
| 629 | }, | ||
| 630 | else => { | ||
| 631 | return sema.mod.fail(&block.base, fn_inst.src, "expected function, found '{}'", .{fn_inst.ty}); | ||
| 632 | }, | ||
| 633 | }; | ||
| 634 | |||
| 635 | const param_count = fn_ty.fnParamLen(); | ||
| 636 | if (param_index >= param_count) { | ||
| 637 | if (fn_ty.fnIsVarArgs()) { | ||
| 638 | return sema.mod.constType(block.arena, inst.base.src, Type.initTag(.var_args_param)); | ||
| 639 | } | ||
| 640 | return sema.mod.fail(&block.base, inst.base.src, "arg index {d} out of bounds; '{}' has {d} argument(s)", .{ | ||
| 641 | param_index, | ||
| 642 | fn_ty, | ||
| 643 | param_count, | ||
| 644 | }); | ||
| 645 | } | ||
| 646 | |||
| 647 | // TODO support generic functions | ||
| 648 | const param_type = fn_ty.fnParamType(param_index); | ||
| 649 | return sema.mod.constType(block.arena, inst.base.src, param_type); | ||
| 650 | } | ||
| 651 | |||
| 652 | fn zirStr(sema: *Sema, block: *Scope.Block, str_inst: zir.Inst.Index) InnerError!*Inst { | ||
| 653 | const tracy = trace(@src()); | ||
| 654 | defer tracy.end(); | ||
| 655 | |||
| 656 | // The bytes references memory inside the ZIR module, which is fine. Multiple | ||
| 657 | // anonymous Decls may have strings which point to within the same ZIR module. | ||
| 658 | const bytes = sema.code.instructions.items(.data)[inst].str.get(sema.code); | ||
| 659 | |||
| 660 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); | ||
| 661 | errdefer new_decl_arena.deinit(); | ||
| 662 | |||
| 663 | const decl_ty = try Type.Tag.array_u8_sentinel_0.create(&new_decl_arena.allocator, bytes.len); | ||
| 664 | const decl_val = try Value.Tag.bytes.create(&new_decl_arena.allocator, bytes); | ||
| 665 | |||
| 666 | const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{ | ||
| 667 | .ty = decl_ty, | ||
| 668 | .val = decl_val, | ||
| 669 | }); | ||
| 670 | return sema.analyzeDeclRef(block, .unneeded, new_decl); | ||
| 671 | } | ||
| 672 | |||
| 673 | fn zirInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 674 | const tracy = trace(@src()); | ||
| 675 | defer tracy.end(); | ||
| 676 | |||
| 677 | return mod.constIntBig(scope, inst.base.src, Type.initTag(.comptime_int), inst.positionals.int); | ||
| 678 | } | ||
| 679 | |||
| 680 | fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 681 | const tracy = trace(@src()); | ||
| 682 | defer tracy.end(); | ||
| 683 | |||
| 684 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 685 | const src = inst_data.src(); | ||
| 686 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | ||
| 687 | const msg = try sema.resolveConstString(block, operand_src, inst_data.operand); | ||
| 688 | return sema.mod.fail(&block.base, src, "{s}", .{msg}); | ||
| 689 | } | ||
| 690 | |||
| 691 | fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 692 | var managed = mod.compile_log_text.toManaged(mod.gpa); | ||
| 693 | defer mod.compile_log_text = managed.moveToUnmanaged(); | ||
| 694 | const writer = managed.writer(); | ||
| 695 | |||
| 696 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 697 | const extra = sema.code.extraData(zir.Inst.MultiOp, inst_data.payload_index); | ||
| 698 | for (sema.code.extra[extra.end..][0..extra.data.operands_len]) |arg_ref, i| { | ||
| 699 | if (i != 0) try writer.print(", ", .{}); | ||
| 700 | |||
| 701 | const arg = sema.resolveInst(block, arg_ref); | ||
| 702 | if (arg.value()) |val| { | ||
| 703 | try writer.print("@as({}, {})", .{ arg.ty, val }); | ||
| 704 | } else { | ||
| 705 | try writer.print("@as({}, [runtime value])", .{arg.ty}); | ||
| 706 | } | ||
| 707 | } | ||
| 708 | try writer.print("\n", .{}); | ||
| 709 | |||
| 710 | const gop = try mod.compile_log_decls.getOrPut(mod.gpa, scope.ownerDecl().?); | ||
| 711 | if (!gop.found_existing) { | ||
| 712 | gop.entry.value = .{ | ||
| 713 | .file_scope = block.getFileScope(), | ||
| 714 | .lazy = inst_data.src(), | ||
| 715 | }; | ||
| 716 | } | ||
| 717 | return sema.mod.constVoid(block.arena, .unneeded); | ||
| 718 | } | ||
| 719 | |||
| 720 | fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 721 | const tracy = trace(@src()); | ||
| 722 | defer tracy.end(); | ||
| 723 | |||
| 724 | // Reserve space for a Loop instruction so that generated Break instructions can | ||
| 725 | // point to it, even if it doesn't end up getting used because the code ends up being | ||
| 726 | // comptime evaluated. | ||
| 727 | const loop_inst = try parent_block.arena.create(Inst.Loop); | ||
| 728 | loop_inst.* = .{ | ||
| 729 | .base = .{ | ||
| 730 | .tag = Inst.Loop.base_tag, | ||
| 731 | .ty = Type.initTag(.noreturn), | ||
| 732 | .src = inst.base.src, | ||
| 733 | }, | ||
| 734 | .body = undefined, | ||
| 735 | }; | ||
| 736 | |||
| 737 | var child_block: Scope.Block = .{ | ||
| 738 | .parent = parent_block, | ||
| 739 | .inst_table = parent_block.inst_table, | ||
| 740 | .func = parent_block.func, | ||
| 741 | .owner_decl = parent_block.owner_decl, | ||
| 742 | .src_decl = parent_block.src_decl, | ||
| 743 | .instructions = .{}, | ||
| 744 | .arena = parent_block.arena, | ||
| 745 | .inlining = parent_block.inlining, | ||
| 746 | .is_comptime = parent_block.is_comptime, | ||
| 747 | .branch_quota = parent_block.branch_quota, | ||
| 748 | }; | ||
| 749 | defer child_block.instructions.deinit(mod.gpa); | ||
| 750 | |||
| 751 | try sema.body(&child_block, inst.positionals.body); | ||
| 752 | |||
| 753 | // Loop repetition is implied so the last instruction may or may not be a noreturn instruction. | ||
| 754 | |||
| 755 | try parent_block.instructions.append(mod.gpa, &loop_inst.base); | ||
| 756 | loop_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) }; | ||
| 757 | return &loop_inst.base; | ||
| 758 | } | ||
| 759 | |||
| 760 | fn zirBlockFlat(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index, is_comptime: bool) InnerError!*Inst { | ||
| 761 | const tracy = trace(@src()); | ||
| 762 | defer tracy.end(); | ||
| 763 | |||
| 764 | var child_block = parent_block.makeSubBlock(); | ||
| 765 | defer child_block.instructions.deinit(mod.gpa); | ||
| 766 | child_block.is_comptime = child_block.is_comptime or is_comptime; | ||
| 767 | |||
| 768 | try sema.body(&child_block, inst.positionals.body); | ||
| 769 | |||
| 770 | // Move the analyzed instructions into the parent block arena. | ||
| 771 | const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items); | ||
| 772 | try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); | ||
| 773 | |||
| 774 | // The result of a flat block is the last instruction. | ||
| 775 | const zir_inst_list = inst.positionals.body.instructions; | ||
| 776 | const last_zir_inst = zir_inst_list[zir_inst_list.len - 1]; | ||
| 777 | return sema.inst_map[last_zir_inst]; | ||
| 778 | } | ||
| 779 | |||
| 780 | fn zirBlock( | ||
| 781 | sema: *Sema, | ||
| 782 | parent_block: *Scope.Block, | ||
| 783 | inst: zir.Inst.Index, | ||
| 784 | is_comptime: bool, | ||
| 785 | ) InnerError!*Inst { | ||
| 786 | const tracy = trace(@src()); | ||
| 787 | defer tracy.end(); | ||
| 788 | |||
| 789 | // Reserve space for a Block instruction so that generated Break instructions can | ||
| 790 | // point to it, even if it doesn't end up getting used because the code ends up being | ||
| 791 | // comptime evaluated. | ||
| 792 | const block_inst = try parent_block.arena.create(Inst.Block); | ||
| 793 | block_inst.* = .{ | ||
| 794 | .base = .{ | ||
| 795 | .tag = Inst.Block.base_tag, | ||
| 796 | .ty = undefined, // Set after analysis. | ||
| 797 | .src = inst.base.src, | ||
| 798 | }, | ||
| 799 | .body = undefined, | ||
| 800 | }; | ||
| 801 | |||
| 802 | var child_block: Scope.Block = .{ | ||
| 803 | .parent = parent_block, | ||
| 804 | .inst_table = parent_block.inst_table, | ||
| 805 | .func = parent_block.func, | ||
| 806 | .owner_decl = parent_block.owner_decl, | ||
| 807 | .src_decl = parent_block.src_decl, | ||
| 808 | .instructions = .{}, | ||
| 809 | .arena = parent_block.arena, | ||
| 810 | // TODO @as here is working around a stage1 miscompilation bug :( | ||
| 811 | .label = @as(?Scope.Block.Label, Scope.Block.Label{ | ||
| 812 | .zir_block = inst, | ||
| 813 | .merges = .{ | ||
| 814 | .results = .{}, | ||
| 815 | .br_list = .{}, | ||
| 816 | .block_inst = block_inst, | ||
| 817 | }, | ||
| 818 | }), | ||
| 819 | .inlining = parent_block.inlining, | ||
| 820 | .is_comptime = is_comptime or parent_block.is_comptime, | ||
| 821 | .branch_quota = parent_block.branch_quota, | ||
| 822 | }; | ||
| 823 | const merges = &child_block.label.?.merges; | ||
| 824 | |||
| 825 | defer child_block.instructions.deinit(mod.gpa); | ||
| 826 | defer merges.results.deinit(mod.gpa); | ||
| 827 | defer merges.br_list.deinit(mod.gpa); | ||
| 828 | |||
| 829 | try sema.body(&child_block, inst.positionals.body); | ||
| 830 | |||
| 831 | return analyzeBlockBody(mod, scope, &child_block, merges); | ||
| 832 | } | ||
| 833 | |||
| 834 | fn analyzeBlockBody( | ||
| 835 | sema: *Sema, | ||
| 836 | parent_block: *Scope.Block, | ||
| 837 | child_block: *Scope.Block, | ||
| 838 | merges: *Scope.Block.Merges, | ||
| 839 | ) InnerError!*Inst { | ||
| 840 | const tracy = trace(@src()); | ||
| 841 | defer tracy.end(); | ||
| 842 | |||
| 843 | // Blocks must terminate with noreturn instruction. | ||
| 844 | assert(child_block.instructions.items.len != 0); | ||
| 845 | assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn()); | ||
| 846 | |||
| 847 | if (merges.results.items.len == 0) { | ||
| 848 | // No need for a block instruction. We can put the new instructions | ||
| 849 | // directly into the parent block. | ||
| 850 | const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items); | ||
| 851 | try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); | ||
| 852 | return copied_instructions[copied_instructions.len - 1]; | ||
| 853 | } | ||
| 854 | if (merges.results.items.len == 1) { | ||
| 855 | const last_inst_index = child_block.instructions.items.len - 1; | ||
| 856 | const last_inst = child_block.instructions.items[last_inst_index]; | ||
| 857 | if (last_inst.breakBlock()) |br_block| { | ||
| 858 | if (br_block == merges.block_inst) { | ||
| 859 | // No need for a block instruction. We can put the new instructions directly | ||
| 860 | // into the parent block. Here we omit the break instruction. | ||
| 861 | const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]); | ||
| 862 | try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); | ||
| 863 | return merges.results.items[0]; | ||
| 864 | } | ||
| 865 | } | ||
| 866 | } | ||
| 867 | // It is impossible to have the number of results be > 1 in a comptime scope. | ||
| 868 | assert(!child_block.is_comptime); // Should already got a compile error in the condbr condition. | ||
| 869 | |||
| 870 | // Need to set the type and emit the Block instruction. This allows machine code generation | ||
| 871 | // to emit a jump instruction to after the block when it encounters the break. | ||
| 872 | try parent_block.instructions.append(mod.gpa, &merges.block_inst.base); | ||
| 873 | const resolved_ty = try sema.resolvePeerTypes(parent_block, merges.results.items); | ||
| 874 | merges.block_inst.base.ty = resolved_ty; | ||
| 875 | merges.block_inst.body = .{ | ||
| 876 | .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items), | ||
| 877 | }; | ||
| 878 | // Now that the block has its type resolved, we need to go back into all the break | ||
| 879 | // instructions, and insert type coercion on the operands. | ||
| 880 | for (merges.br_list.items) |br| { | ||
| 881 | if (br.operand.ty.eql(resolved_ty)) { | ||
| 882 | // No type coercion needed. | ||
| 883 | continue; | ||
| 884 | } | ||
| 885 | var coerce_block = parent_block.makeSubBlock(); | ||
| 886 | defer coerce_block.instructions.deinit(mod.gpa); | ||
| 887 | const coerced_operand = try sema.coerce(&coerce_block.base, resolved_ty, br.operand); | ||
| 888 | // If no instructions were produced, such as in the case of a coercion of a | ||
| 889 | // constant value to a new type, we can simply point the br operand to it. | ||
| 890 | if (coerce_block.instructions.items.len == 0) { | ||
| 891 | br.operand = coerced_operand; | ||
| 892 | continue; | ||
| 893 | } | ||
| 894 | assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand); | ||
| 895 | // Here we depend on the br instruction having been over-allocated (if necessary) | ||
| 896 | // inide analyzeBreak so that it can be converted into a br_block_flat instruction. | ||
| 897 | const br_src = br.base.src; | ||
| 898 | const br_ty = br.base.ty; | ||
| 899 | const br_block_flat = @ptrCast(*Inst.BrBlockFlat, br); | ||
| 900 | br_block_flat.* = .{ | ||
| 901 | .base = .{ | ||
| 902 | .src = br_src, | ||
| 903 | .ty = br_ty, | ||
| 904 | .tag = .br_block_flat, | ||
| 905 | }, | ||
| 906 | .block = merges.block_inst, | ||
| 907 | .body = .{ | ||
| 908 | .instructions = try parent_block.arena.dupe(*Inst, coerce_block.instructions.items), | ||
| 909 | }, | ||
| 910 | }; | ||
| 911 | } | ||
| 912 | return &merges.block_inst.base; | ||
| 913 | } | ||
| 914 | |||
| 915 | fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 916 | const tracy = trace(@src()); | ||
| 917 | defer tracy.end(); | ||
| 918 | |||
| 919 | try sema.requireRuntimeBlock(block, src); | ||
| 920 | return block.addNoOp(inst.base.src, Type.initTag(.void), .breakpoint); | ||
| 921 | } | ||
| 922 | |||
| 923 | fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 924 | const tracy = trace(@src()); | ||
| 925 | defer tracy.end(); | ||
| 926 | |||
| 927 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 928 | const operand = sema.resolveInst(block, bin_inst.rhs); | ||
| 929 | const zir_block = bin_inst.lhs; | ||
| 930 | return analyzeBreak(mod, block, sema.src, zir_block, operand); | ||
| 931 | } | ||
| 932 | |||
| 933 | fn zirBreakVoidTok(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 934 | const tracy = trace(@src()); | ||
| 935 | defer tracy.end(); | ||
| 936 | |||
| 937 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 938 | const zir_block = inst_data.operand; | ||
| 939 | const void_inst = try sema.mod.constVoid(block.arena, .unneeded); | ||
| 940 | return analyzeBreak(mod, block, inst_data.src(), zir_block, void_inst); | ||
| 941 | } | ||
| 942 | |||
| 943 | fn analyzeBreak( | ||
| 944 | sema: *Sema, | ||
| 945 | block: *Scope.Block, | ||
| 946 | src: LazySrcLoc, | ||
| 947 | zir_block: zir.Inst.Index, | ||
| 948 | operand: *Inst, | ||
| 949 | ) InnerError!*Inst { | ||
| 950 | var opt_block = scope.cast(Scope.Block); | ||
| 951 | while (opt_block) |block| { | ||
| 952 | if (block.label) |*label| { | ||
| 953 | if (label.zir_block == zir_block) { | ||
| 954 | try sema.requireFunctionBlock(block, src); | ||
| 955 | // Here we add a br instruction, but we over-allocate a little bit | ||
| 956 | // (if necessary) to make it possible to convert the instruction into | ||
| 957 | // a br_block_flat instruction later. | ||
| 958 | const br = @ptrCast(*Inst.Br, try b.arena.alignedAlloc( | ||
| 959 | u8, | ||
| 960 | Inst.convertable_br_align, | ||
| 961 | Inst.convertable_br_size, | ||
| 962 | )); | ||
| 963 | br.* = .{ | ||
| 964 | .base = .{ | ||
| 965 | .tag = .br, | ||
| 966 | .ty = Type.initTag(.noreturn), | ||
| 967 | .src = src, | ||
| 968 | }, | ||
| 969 | .operand = operand, | ||
| 970 | .block = label.merges.block_inst, | ||
| 971 | }; | ||
| 972 | try b.instructions.append(mod.gpa, &br.base); | ||
| 973 | try label.merges.results.append(mod.gpa, operand); | ||
| 974 | try label.merges.br_list.append(mod.gpa, br); | ||
| 975 | return &br.base; | ||
| 976 | } | ||
| 977 | } | ||
| 978 | opt_block = block.parent; | ||
| 979 | } else unreachable; | ||
| 980 | } | ||
| 981 | |||
| 982 | fn zirDbgStmtNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 983 | const tracy = trace(@src()); | ||
| 984 | defer tracy.end(); | ||
| 985 | |||
| 986 | if (b.is_comptime) { | ||
| 987 | return sema.mod.constVoid(block.arena, .unneeded); | ||
| 988 | } | ||
| 989 | |||
| 990 | const src_node = sema.code.instructions.items(.data)[inst].node; | ||
| 991 | const src: LazySrcLoc = .{ .node_offset = src_node }; | ||
| 992 | return block.addNoOp(src, Type.initTag(.void), .dbg_stmt); | ||
| 993 | } | ||
| 994 | |||
| 995 | fn zirDeclRef(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 996 | const tracy = trace(@src()); | ||
| 997 | defer tracy.end(); | ||
| 998 | |||
| 999 | const decl = sema.code.instructions.items(.data)[inst].decl; | ||
| 1000 | return sema.analyzeDeclRef(block, .unneeded, decl); | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | fn zirDeclVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1004 | const tracy = trace(@src()); | ||
| 1005 | defer tracy.end(); | ||
| 1006 | |||
| 1007 | const decl = sema.code.instructions.items(.data)[inst].decl; | ||
| 1008 | return sema.analyzeDeclVal(block, .unneeded, decl); | ||
| 1009 | } | ||
| 1010 | |||
| 1011 | fn zirCallNone(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1012 | const tracy = trace(@src()); | ||
| 1013 | defer tracy.end(); | ||
| 1014 | |||
| 1015 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 1016 | const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node }; | ||
| 1017 | |||
| 1018 | return sema.analyzeCall(block, inst_data.operand, func_src, inst_data.src(), .auto, &.{}); | ||
| 1019 | } | ||
| 1020 | |||
| 1021 | fn zirCall( | ||
| 1022 | sema: *Sema, | ||
| 1023 | block: *Scope.Block, | ||
| 1024 | inst: zir.Inst.Index, | ||
| 1025 | modifier: std.builtin.CallOptions.Modifier, | ||
| 1026 | ) InnerError!*Inst { | ||
| 1027 | const tracy = trace(@src()); | ||
| 1028 | defer tracy.end(); | ||
| 1029 | |||
| 1030 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1031 | const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node }; | ||
| 1032 | const call_src = inst_data.src(); | ||
| 1033 | const extra = sema.code.extraData(zir.Inst.Call, inst_data.payload_index); | ||
| 1034 | const args = sema.code.extra[extra.end..][0..extra.data.args_len]; | ||
| 1035 | |||
| 1036 | return sema.analyzeCall(block, extra.data.callee, func_src, call_src, modifier, args); | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | fn analyzeCall( | ||
| 1040 | sema: *Sema, | ||
| 1041 | block: *Scope.Block, | ||
| 1042 | zir_func: zir.Inst.Ref, | ||
| 1043 | func_src: LazySrcLoc, | ||
| 1044 | call_src: LazySrcLoc, | ||
| 1045 | modifier: std.builtin.CallOptions.Modifier, | ||
| 1046 | zir_args: []const Ref, | ||
| 1047 | ) InnerError!*ir.Inst { | ||
| 1048 | const func = sema.resolveInst(zir_func); | ||
| 1049 | |||
| 1050 | if (func.ty.zigTypeTag() != .Fn) | ||
| 1051 | return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func.ty}); | ||
| 1052 | |||
| 1053 | const cc = func.ty.fnCallingConvention(); | ||
| 1054 | if (cc == .Naked) { | ||
| 1055 | // TODO add error note: declared here | ||
| 1056 | return sema.mod.fail( | ||
| 1057 | &block.base, | ||
| 1058 | func_src, | ||
| 1059 | "unable to call function with naked calling convention", | ||
| 1060 | .{}, | ||
| 1061 | ); | ||
| 1062 | } | ||
| 1063 | const fn_params_len = func.ty.fnParamLen(); | ||
| 1064 | if (func.ty.fnIsVarArgs()) { | ||
| 1065 | assert(cc == .C); | ||
| 1066 | if (zir_args.len < fn_params_len) { | ||
| 1067 | // TODO add error note: declared here | ||
| 1068 | return sema.mod.fail( | ||
| 1069 | &block.base, | ||
| 1070 | func_src, | ||
| 1071 | "expected at least {d} argument(s), found {d}", | ||
| 1072 | .{ fn_params_len, zir_args.len }, | ||
| 1073 | ); | ||
| 1074 | } | ||
| 1075 | } else if (fn_params_len != zir_args.len) { | ||
| 1076 | // TODO add error note: declared here | ||
| 1077 | return sema.mod.fail( | ||
| 1078 | &block.base, | ||
| 1079 | func_src, | ||
| 1080 | "expected {d} argument(s), found {d}", | ||
| 1081 | .{ fn_params_len, zir_args.len }, | ||
| 1082 | ); | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | if (modifier == .compile_time) { | ||
| 1086 | return sema.mod.fail(&block.base, call_src, "TODO implement comptime function calls", .{}); | ||
| 1087 | } | ||
| 1088 | if (modifier != .auto) { | ||
| 1089 | return sema.mod.fail(&block.base, call_src, "TODO implement call with modifier {}", .{inst.positionals.modifier}); | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | // TODO handle function calls of generic functions | ||
| 1093 | const casted_args = try block.arena.alloc(*Inst, zir_args.len); | ||
| 1094 | for (zir_args) |zir_arg, i| { | ||
| 1095 | // the args are already casted to the result of a param type instruction. | ||
| 1096 | casted_args[i] = sema.resolveInst(block, zir_arg); | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | const ret_type = func.ty.fnReturnType(); | ||
| 1100 | |||
| 1101 | try sema.requireFunctionBlock(block, call_src); | ||
| 1102 | const is_comptime_call = b.is_comptime or modifier == .compile_time; | ||
| 1103 | const is_inline_call = is_comptime_call or modifier == .always_inline or | ||
| 1104 | func.ty.fnCallingConvention() == .Inline; | ||
| 1105 | if (is_inline_call) { | ||
| 1106 | const func_val = try sema.resolveConstValue(block, func_src, func); | ||
| 1107 | const module_fn = switch (func_val.tag()) { | ||
| 1108 | .function => func_val.castTag(.function).?.data, | ||
| 1109 | .extern_fn => return sema.mod.fail(&block.base, call_src, "{s} call of extern function", .{ | ||
| 1110 | @as([]const u8, if (is_comptime_call) "comptime" else "inline"), | ||
| 1111 | }), | ||
| 1112 | else => unreachable, | ||
| 1113 | }; | ||
| 1114 | |||
| 1115 | // Analyze the ZIR. The same ZIR gets analyzed into a runtime function | ||
| 1116 | // or an inlined call depending on what union tag the `label` field is | ||
| 1117 | // set to in the `Scope.Block`. | ||
| 1118 | // This block instruction will be used to capture the return value from the | ||
| 1119 | // inlined function. | ||
| 1120 | const block_inst = try block.arena.create(Inst.Block); | ||
| 1121 | block_inst.* = .{ | ||
| 1122 | .base = .{ | ||
| 1123 | .tag = Inst.Block.base_tag, | ||
| 1124 | .ty = ret_type, | ||
| 1125 | .src = call_src, | ||
| 1126 | }, | ||
| 1127 | .body = undefined, | ||
| 1128 | }; | ||
| 1129 | // If this is the top of the inline/comptime call stack, we use this data. | ||
| 1130 | // Otherwise we pass on the shared data from the parent scope. | ||
| 1131 | var shared_inlining: Scope.Block.Inlining.Shared = .{ | ||
| 1132 | .branch_count = 0, | ||
| 1133 | .caller = b.func, | ||
| 1134 | }; | ||
| 1135 | // This one is shared among sub-blocks within the same callee, but not | ||
| 1136 | // shared among the entire inline/comptime call stack. | ||
| 1137 | var inlining: Scope.Block.Inlining = .{ | ||
| 1138 | .shared = if (b.inlining) |inlining| inlining.shared else &shared_inlining, | ||
| 1139 | .param_index = 0, | ||
| 1140 | .casted_args = casted_args, | ||
| 1141 | .merges = .{ | ||
| 1142 | .results = .{}, | ||
| 1143 | .br_list = .{}, | ||
| 1144 | .block_inst = block_inst, | ||
| 1145 | }, | ||
| 1146 | }; | ||
| 1147 | var inst_table = Scope.Block.InstTable.init(mod.gpa); | ||
| 1148 | defer inst_table.deinit(); | ||
| 1149 | |||
| 1150 | var child_block: Scope.Block = .{ | ||
| 1151 | .parent = null, | ||
| 1152 | .inst_table = &inst_table, | ||
| 1153 | .func = module_fn, | ||
| 1154 | .owner_decl = scope.ownerDecl().?, | ||
| 1155 | .src_decl = module_fn.owner_decl, | ||
| 1156 | .instructions = .{}, | ||
| 1157 | .arena = block.arena, | ||
| 1158 | .label = null, | ||
| 1159 | .inlining = &inlining, | ||
| 1160 | .is_comptime = is_comptime_call, | ||
| 1161 | .branch_quota = b.branch_quota, | ||
| 1162 | }; | ||
| 1163 | |||
| 1164 | const merges = &child_block.inlining.?.merges; | ||
| 1165 | |||
| 1166 | defer child_block.instructions.deinit(mod.gpa); | ||
| 1167 | defer merges.results.deinit(mod.gpa); | ||
| 1168 | defer merges.br_list.deinit(mod.gpa); | ||
| 1169 | |||
| 1170 | try mod.emitBackwardBranch(&child_block, call_src); | ||
| 1171 | |||
| 1172 | // This will have return instructions analyzed as break instructions to | ||
| 1173 | // the block_inst above. | ||
| 1174 | try sema.body(&child_block, module_fn.zir); | ||
| 1175 | |||
| 1176 | return analyzeBlockBody(mod, scope, &child_block, merges); | ||
| 1177 | } | ||
| 1178 | |||
| 1179 | return block.addCall(call_src, ret_type, func, casted_args); | ||
| 1180 | } | ||
| 1181 | |||
| 1182 | fn zirIntType(sema: *Sema, block: *Scope.Block, inttype: zir.Inst.Index) InnerError!*Inst { | ||
| 1183 | const tracy = trace(@src()); | ||
| 1184 | defer tracy.end(); | ||
| 1185 | return sema.mod.fail(&block.base, inttype.base.src, "TODO implement inttype", .{}); | ||
| 1186 | } | ||
| 1187 | |||
| 1188 | fn zirOptionalType(sema: *Sema, block: *Scope.Block, optional: zir.Inst.Index) InnerError!*Inst { | ||
| 1189 | const tracy = trace(@src()); | ||
| 1190 | defer tracy.end(); | ||
| 1191 | |||
| 1192 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1193 | const child_type = try sema.resolveType(block, inst_data.operand); | ||
| 1194 | const opt_type = try mod.optionalType(block.arena, child_type); | ||
| 1195 | |||
| 1196 | return sema.mod.constType(block.arena, inst_data.src(), opt_type); | ||
| 1197 | } | ||
| 1198 | |||
| 1199 | fn zirOptionalTypeFromPtrElem(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1200 | const tracy = trace(@src()); | ||
| 1201 | defer tracy.end(); | ||
| 1202 | |||
| 1203 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1204 | const ptr = sema.resolveInst(block, inst_data.operand); | ||
| 1205 | const elem_ty = ptr.ty.elemType(); | ||
| 1206 | const opt_ty = try mod.optionalType(block.arena, elem_ty); | ||
| 1207 | |||
| 1208 | return sema.mod.constType(block.arena, inst_data.src(), opt_ty); | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | fn zirArrayType(sema: *Sema, block: *Scope.Block, array: zir.Inst.Index) InnerError!*Inst { | ||
| 1212 | const tracy = trace(@src()); | ||
| 1213 | defer tracy.end(); | ||
| 1214 | // TODO these should be lazily evaluated | ||
| 1215 | const len = try resolveInstConst(mod, scope, array.positionals.lhs); | ||
| 1216 | const elem_type = try sema.resolveType(block, array.positionals.rhs); | ||
| 1217 | |||
| 1218 | return sema.mod.constType(block.arena, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), null, elem_type)); | ||
| 1219 | } | ||
| 1220 | |||
| 1221 | fn zirArrayTypeSentinel(sema: *Sema, block: *Scope.Block, array: zir.Inst.Index) InnerError!*Inst { | ||
| 1222 | const tracy = trace(@src()); | ||
| 1223 | defer tracy.end(); | ||
| 1224 | // TODO these should be lazily evaluated | ||
| 1225 | const len = try resolveInstConst(mod, scope, array.positionals.len); | ||
| 1226 | const sentinel = try resolveInstConst(mod, scope, array.positionals.sentinel); | ||
| 1227 | const elem_type = try sema.resolveType(block, array.positionals.elem_type); | ||
| 1228 | |||
| 1229 | return sema.mod.constType(block.arena, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), sentinel.val, elem_type)); | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | fn zirErrorUnionType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1233 | const tracy = trace(@src()); | ||
| 1234 | defer tracy.end(); | ||
| 1235 | |||
| 1236 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1237 | const error_union = try sema.resolveType(block, bin_inst.lhs); | ||
| 1238 | const payload = try sema.resolveType(block, bin_inst.rhs); | ||
| 1239 | |||
| 1240 | if (error_union.zigTypeTag() != .ErrorSet) { | ||
| 1241 | return sema.mod.fail(&block.base, inst.base.src, "expected error set type, found {}", .{error_union.elemType()}); | ||
| 1242 | } | ||
| 1243 | |||
| 1244 | return sema.mod.constType(block.arena, inst.base.src, try mod.errorUnionType(scope, error_union, payload)); | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | fn zirAnyframeType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1248 | const tracy = trace(@src()); | ||
| 1249 | defer tracy.end(); | ||
| 1250 | |||
| 1251 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 1252 | const src = inst_data.src(); | ||
| 1253 | const operand_src: LazySrcLoc = .{ .node_offset_anyframe_type = inst_data.src_node }; | ||
| 1254 | const return_type = try sema.resolveType(block, operand_src, inst_data.operand); | ||
| 1255 | const anyframe_type = try sema.mod.anyframeType(block.arena, return_type); | ||
| 1256 | |||
| 1257 | return sema.mod.constType(block.arena, src, anyframe_type); | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | fn zirErrorSet(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1261 | const tracy = trace(@src()); | ||
| 1262 | defer tracy.end(); | ||
| 1263 | |||
| 1264 | // The owner Decl arena will store the hashmap. | ||
| 1265 | var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa); | ||
| 1266 | errdefer new_decl_arena.deinit(); | ||
| 1267 | |||
| 1268 | const payload = try new_decl_arena.allocator.create(Value.Payload.ErrorSet); | ||
| 1269 | payload.* = .{ | ||
| 1270 | .base = .{ .tag = .error_set }, | ||
| 1271 | .data = .{ | ||
| 1272 | .fields = .{}, | ||
| 1273 | .decl = undefined, // populated below | ||
| 1274 | }, | ||
| 1275 | }; | ||
| 1276 | try payload.data.fields.ensureCapacity(&new_decl_arena.allocator, @intCast(u32, inst.positionals.fields.len)); | ||
| 1277 | |||
| 1278 | for (inst.positionals.fields) |field_name| { | ||
| 1279 | const entry = try mod.getErrorValue(field_name); | ||
| 1280 | if (payload.data.fields.fetchPutAssumeCapacity(entry.key, {})) |_| { | ||
| 1281 | return sema.mod.fail(&block.base, inst.base.src, "duplicate error: '{s}'", .{field_name}); | ||
| 1282 | } | ||
| 1283 | } | ||
| 1284 | // TODO create name in format "error:line:column" | ||
| 1285 | const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{ | ||
| 1286 | .ty = Type.initTag(.type), | ||
| 1287 | .val = Value.initPayload(&payload.base), | ||
| 1288 | }); | ||
| 1289 | payload.data.decl = new_decl; | ||
| 1290 | return mod.analyzeDeclVal(scope, inst.base.src, new_decl); | ||
| 1291 | } | ||
| 1292 | |||
| 1293 | fn zirErrorValue(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1294 | const tracy = trace(@src()); | ||
| 1295 | defer tracy.end(); | ||
| 1296 | |||
| 1297 | // Create an anonymous error set type with only this error value, and return the value. | ||
| 1298 | const entry = try mod.getErrorValue(inst.positionals.name); | ||
| 1299 | const result_type = try Type.Tag.error_set_single.create(block.arena, entry.key); | ||
| 1300 | return sema.mod.constInst(scope, inst.base.src, .{ | ||
| 1301 | .ty = result_type, | ||
| 1302 | .val = try Value.Tag.@"error".create(block.arena, .{ | ||
| 1303 | .name = entry.key, | ||
| 1304 | }), | ||
| 1305 | }); | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1309 | const tracy = trace(@src()); | ||
| 1310 | defer tracy.end(); | ||
| 1311 | |||
| 1312 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1313 | const lhs_ty = try sema.resolveType(block, bin_inst.lhs); | ||
| 1314 | const rhs_ty = try sema.resolveType(block, bin_inst.rhs); | ||
| 1315 | if (rhs_ty.zigTypeTag() != .ErrorSet) | ||
| 1316 | return sema.mod.fail(&block.base, inst.positionals.rhs.src, "expected error set type, found {}", .{rhs_ty}); | ||
| 1317 | if (lhs_ty.zigTypeTag() != .ErrorSet) | ||
| 1318 | return sema.mod.fail(&block.base, inst.positionals.lhs.src, "expected error set type, found {}", .{lhs_ty}); | ||
| 1319 | |||
| 1320 | // anything merged with anyerror is anyerror | ||
| 1321 | if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) | ||
| 1322 | return sema.mod.constInst(scope, inst.base.src, .{ | ||
| 1323 | .ty = Type.initTag(.type), | ||
| 1324 | .val = Value.initTag(.anyerror_type), | ||
| 1325 | }); | ||
| 1326 | // The declarations arena will store the hashmap. | ||
| 1327 | var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa); | ||
| 1328 | errdefer new_decl_arena.deinit(); | ||
| 1329 | |||
| 1330 | const payload = try new_decl_arena.allocator.create(Value.Payload.ErrorSet); | ||
| 1331 | payload.* = .{ | ||
| 1332 | .base = .{ .tag = .error_set }, | ||
| 1333 | .data = .{ | ||
| 1334 | .fields = .{}, | ||
| 1335 | .decl = undefined, // populated below | ||
| 1336 | }, | ||
| 1337 | }; | ||
| 1338 | try payload.data.fields.ensureCapacity(&new_decl_arena.allocator, @intCast(u32, switch (rhs_ty.tag()) { | ||
| 1339 | .error_set_single => 1, | ||
| 1340 | .error_set => rhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields.size, | ||
| 1341 | else => unreachable, | ||
| 1342 | } + switch (lhs_ty.tag()) { | ||
| 1343 | .error_set_single => 1, | ||
| 1344 | .error_set => lhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields.size, | ||
| 1345 | else => unreachable, | ||
| 1346 | })); | ||
| 1347 | |||
| 1348 | switch (lhs_ty.tag()) { | ||
| 1349 | .error_set_single => { | ||
| 1350 | const name = lhs_ty.castTag(.error_set_single).?.data; | ||
| 1351 | payload.data.fields.putAssumeCapacity(name, {}); | ||
| 1352 | }, | ||
| 1353 | .error_set => { | ||
| 1354 | var multiple = lhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields; | ||
| 1355 | var it = multiple.iterator(); | ||
| 1356 | while (it.next()) |entry| { | ||
| 1357 | payload.data.fields.putAssumeCapacity(entry.key, entry.value); | ||
| 1358 | } | ||
| 1359 | }, | ||
| 1360 | else => unreachable, | ||
| 1361 | } | ||
| 1362 | |||
| 1363 | switch (rhs_ty.tag()) { | ||
| 1364 | .error_set_single => { | ||
| 1365 | const name = rhs_ty.castTag(.error_set_single).?.data; | ||
| 1366 | payload.data.fields.putAssumeCapacity(name, {}); | ||
| 1367 | }, | ||
| 1368 | .error_set => { | ||
| 1369 | var multiple = rhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields; | ||
| 1370 | var it = multiple.iterator(); | ||
| 1371 | while (it.next()) |entry| { | ||
| 1372 | payload.data.fields.putAssumeCapacity(entry.key, entry.value); | ||
| 1373 | } | ||
| 1374 | }, | ||
| 1375 | else => unreachable, | ||
| 1376 | } | ||
| 1377 | // TODO create name in format "error:line:column" | ||
| 1378 | const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{ | ||
| 1379 | .ty = Type.initTag(.type), | ||
| 1380 | .val = Value.initPayload(&payload.base), | ||
| 1381 | }); | ||
| 1382 | payload.data.decl = new_decl; | ||
| 1383 | |||
| 1384 | return mod.analyzeDeclVal(scope, inst.base.src, new_decl); | ||
| 1385 | } | ||
| 1386 | |||
| 1387 | fn zirEnumLiteral(sema: *Sema, block: *Scope.Block, zir_inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1388 | const tracy = trace(@src()); | ||
| 1389 | defer tracy.end(); | ||
| 1390 | |||
| 1391 | const duped_name = try block.arena.dupe(u8, inst.positionals.name); | ||
| 1392 | return sema.mod.constInst(scope, inst.base.src, .{ | ||
| 1393 | .ty = Type.initTag(.enum_literal), | ||
| 1394 | .val = try Value.Tag.enum_literal.create(block.arena, duped_name), | ||
| 1395 | }); | ||
| 1396 | } | ||
| 1397 | |||
| 1398 | /// Pointer in, pointer out. | ||
| 1399 | fn zirOptionalPayloadPtr( | ||
| 1400 | sema: *Sema, | ||
| 1401 | block: *Scope.Block, | ||
| 1402 | inst: zir.Inst.Index, | ||
| 1403 | safety_check: bool, | ||
| 1404 | ) InnerError!*Inst { | ||
| 1405 | const tracy = trace(@src()); | ||
| 1406 | defer tracy.end(); | ||
| 1407 | |||
| 1408 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1409 | const optional_ptr = sema.resolveInst(block, inst_data.operand); | ||
| 1410 | assert(optional_ptr.ty.zigTypeTag() == .Pointer); | ||
| 1411 | const src = inst_data.src(); | ||
| 1412 | |||
| 1413 | const opt_type = optional_ptr.ty.elemType(); | ||
| 1414 | if (opt_type.zigTypeTag() != .Optional) { | ||
| 1415 | return sema.mod.fail(&block.base, src, "expected optional type, found {}", .{opt_type}); | ||
| 1416 | } | ||
| 1417 | |||
| 1418 | const child_type = try opt_type.optionalChildAlloc(block.arena); | ||
| 1419 | const child_pointer = try sema.mod.simplePtrType(block.arena, child_type, !optional_ptr.ty.isConstPtr(), .One); | ||
| 1420 | |||
| 1421 | if (optional_ptr.value()) |pointer_val| { | ||
| 1422 | const val = try pointer_val.pointerDeref(block.arena); | ||
| 1423 | if (val.isNull()) { | ||
| 1424 | return sema.mod.fail(&block.base, src, "unable to unwrap null", .{}); | ||
| 1425 | } | ||
| 1426 | // The same Value represents the pointer to the optional and the payload. | ||
| 1427 | return sema.mod.constInst(scope, src, .{ | ||
| 1428 | .ty = child_pointer, | ||
| 1429 | .val = pointer_val, | ||
| 1430 | }); | ||
| 1431 | } | ||
| 1432 | |||
| 1433 | try sema.requireRuntimeBlock(block, src); | ||
| 1434 | if (safety_check and block.wantSafety()) { | ||
| 1435 | const is_non_null = try block.addUnOp(src, Type.initTag(.bool), .is_non_null_ptr, optional_ptr); | ||
| 1436 | try mod.addSafetyCheck(b, is_non_null, .unwrap_null); | ||
| 1437 | } | ||
| 1438 | return block.addUnOp(src, child_pointer, .optional_payload_ptr, optional_ptr); | ||
| 1439 | } | ||
| 1440 | |||
| 1441 | /// Value in, value out. | ||
| 1442 | fn zirOptionalPayload( | ||
| 1443 | sema: *Sema, | ||
| 1444 | block: *Scope.Block, | ||
| 1445 | inst: zir.Inst.Index, | ||
| 1446 | safety_check: bool, | ||
| 1447 | ) InnerError!*Inst { | ||
| 1448 | const tracy = trace(@src()); | ||
| 1449 | defer tracy.end(); | ||
| 1450 | |||
| 1451 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1452 | const src = inst_data.src(); | ||
| 1453 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 1454 | const opt_type = operand.ty; | ||
| 1455 | if (opt_type.zigTypeTag() != .Optional) { | ||
| 1456 | return sema.mod.fail(&block.base, src, "expected optional type, found {}", .{opt_type}); | ||
| 1457 | } | ||
| 1458 | |||
| 1459 | const child_type = try opt_type.optionalChildAlloc(block.arena); | ||
| 1460 | |||
| 1461 | if (operand.value()) |val| { | ||
| 1462 | if (val.isNull()) { | ||
| 1463 | return sema.mod.fail(&block.base, src, "unable to unwrap null", .{}); | ||
| 1464 | } | ||
| 1465 | return sema.mod.constInst(scope, src, .{ | ||
| 1466 | .ty = child_type, | ||
| 1467 | .val = val, | ||
| 1468 | }); | ||
| 1469 | } | ||
| 1470 | |||
| 1471 | try sema.requireRuntimeBlock(block, src); | ||
| 1472 | if (safety_check and block.wantSafety()) { | ||
| 1473 | const is_non_null = try block.addUnOp(src, Type.initTag(.bool), .is_non_null, operand); | ||
| 1474 | try mod.addSafetyCheck(b, is_non_null, .unwrap_null); | ||
| 1475 | } | ||
| 1476 | return block.addUnOp(src, child_type, .optional_payload, operand); | ||
| 1477 | } | ||
| 1478 | |||
| 1479 | /// Value in, value out | ||
| 1480 | fn zirErrUnionPayload( | ||
| 1481 | sema: *Sema, | ||
| 1482 | block: *Scope.Block, | ||
| 1483 | inst: zir.Inst.Index, | ||
| 1484 | safety_check: bool, | ||
| 1485 | ) InnerError!*Inst { | ||
| 1486 | const tracy = trace(@src()); | ||
| 1487 | defer tracy.end(); | ||
| 1488 | |||
| 1489 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1490 | const src = inst_data.src(); | ||
| 1491 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 1492 | if (operand.ty.zigTypeTag() != .ErrorUnion) | ||
| 1493 | return sema.mod.fail(&block.base, operand.src, "expected error union type, found '{}'", .{operand.ty}); | ||
| 1494 | |||
| 1495 | if (operand.value()) |val| { | ||
| 1496 | if (val.getError()) |name| { | ||
| 1497 | return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name}); | ||
| 1498 | } | ||
| 1499 | const data = val.castTag(.error_union).?.data; | ||
| 1500 | return sema.mod.constInst(scope, src, .{ | ||
| 1501 | .ty = operand.ty.castTag(.error_union).?.data.payload, | ||
| 1502 | .val = data, | ||
| 1503 | }); | ||
| 1504 | } | ||
| 1505 | try sema.requireRuntimeBlock(block, src); | ||
| 1506 | if (safety_check and block.wantSafety()) { | ||
| 1507 | const is_non_err = try block.addUnOp(src, Type.initTag(.bool), .is_err, operand); | ||
| 1508 | try mod.addSafetyCheck(b, is_non_err, .unwrap_errunion); | ||
| 1509 | } | ||
| 1510 | return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_payload, operand); | ||
| 1511 | } | ||
| 1512 | |||
| 1513 | /// Pointer in, pointer out. | ||
| 1514 | fn zirErrUnionPayloadPtr( | ||
| 1515 | sema: *Sema, | ||
| 1516 | block: *Scope.Block, | ||
| 1517 | inst: zir.Inst.Index, | ||
| 1518 | safety_check: bool, | ||
| 1519 | ) InnerError!*Inst { | ||
| 1520 | const tracy = trace(@src()); | ||
| 1521 | defer tracy.end(); | ||
| 1522 | |||
| 1523 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1524 | const src = inst_data.src(); | ||
| 1525 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 1526 | assert(operand.ty.zigTypeTag() == .Pointer); | ||
| 1527 | |||
| 1528 | if (operand.ty.elemType().zigTypeTag() != .ErrorUnion) | ||
| 1529 | return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()}); | ||
| 1530 | |||
| 1531 | const operand_pointer_ty = try sema.mod.simplePtrType(block.arena, operand.ty.elemType().castTag(.error_union).?.data.payload, !operand.ty.isConstPtr(), .One); | ||
| 1532 | |||
| 1533 | if (operand.value()) |pointer_val| { | ||
| 1534 | const val = try pointer_val.pointerDeref(block.arena); | ||
| 1535 | if (val.getError()) |name| { | ||
| 1536 | return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name}); | ||
| 1537 | } | ||
| 1538 | const data = val.castTag(.error_union).?.data; | ||
| 1539 | // The same Value represents the pointer to the error union and the payload. | ||
| 1540 | return sema.mod.constInst(scope, src, .{ | ||
| 1541 | .ty = operand_pointer_ty, | ||
| 1542 | .val = try Value.Tag.ref_val.create( | ||
| 1543 | block.arena, | ||
| 1544 | data, | ||
| 1545 | ), | ||
| 1546 | }); | ||
| 1547 | } | ||
| 1548 | |||
| 1549 | try sema.requireRuntimeBlock(block, src); | ||
| 1550 | if (safety_check and block.wantSafety()) { | ||
| 1551 | const is_non_err = try block.addUnOp(src, Type.initTag(.bool), .is_err, operand); | ||
| 1552 | try mod.addSafetyCheck(b, is_non_err, .unwrap_errunion); | ||
| 1553 | } | ||
| 1554 | return block.addUnOp(src, operand_pointer_ty, .unwrap_errunion_payload_ptr, operand); | ||
| 1555 | } | ||
| 1556 | |||
| 1557 | /// Value in, value out | ||
| 1558 | fn zirErrUnionCode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1559 | const tracy = trace(@src()); | ||
| 1560 | defer tracy.end(); | ||
| 1561 | |||
| 1562 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1563 | const src = inst_data.src(); | ||
| 1564 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 1565 | if (operand.ty.zigTypeTag() != .ErrorUnion) | ||
| 1566 | return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty}); | ||
| 1567 | |||
| 1568 | if (operand.value()) |val| { | ||
| 1569 | assert(val.getError() != null); | ||
| 1570 | const data = val.castTag(.error_union).?.data; | ||
| 1571 | return sema.mod.constInst(scope, src, .{ | ||
| 1572 | .ty = operand.ty.castTag(.error_union).?.data.error_set, | ||
| 1573 | .val = data, | ||
| 1574 | }); | ||
| 1575 | } | ||
| 1576 | |||
| 1577 | try sema.requireRuntimeBlock(block, src); | ||
| 1578 | return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err, operand); | ||
| 1579 | } | ||
| 1580 | |||
| 1581 | /// Pointer in, value out | ||
| 1582 | fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1583 | const tracy = trace(@src()); | ||
| 1584 | defer tracy.end(); | ||
| 1585 | |||
| 1586 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1587 | const src = inst_data.src(); | ||
| 1588 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 1589 | assert(operand.ty.zigTypeTag() == .Pointer); | ||
| 1590 | |||
| 1591 | if (operand.ty.elemType().zigTypeTag() != .ErrorUnion) | ||
| 1592 | return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()}); | ||
| 1593 | |||
| 1594 | if (operand.value()) |pointer_val| { | ||
| 1595 | const val = try pointer_val.pointerDeref(block.arena); | ||
| 1596 | assert(val.getError() != null); | ||
| 1597 | const data = val.castTag(.error_union).?.data; | ||
| 1598 | return sema.mod.constInst(scope, src, .{ | ||
| 1599 | .ty = operand.ty.elemType().castTag(.error_union).?.data.error_set, | ||
| 1600 | .val = data, | ||
| 1601 | }); | ||
| 1602 | } | ||
| 1603 | |||
| 1604 | try sema.requireRuntimeBlock(block, src); | ||
| 1605 | return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err_ptr, operand); | ||
| 1606 | } | ||
| 1607 | |||
| 1608 | fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1609 | const tracy = trace(@src()); | ||
| 1610 | defer tracy.end(); | ||
| 1611 | |||
| 1612 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1613 | const src = inst_data.src(); | ||
| 1614 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 1615 | if (operand.ty.zigTypeTag() != .ErrorUnion) | ||
| 1616 | return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty}); | ||
| 1617 | if (operand.ty.castTag(.error_union).?.data.payload.zigTypeTag() != .Void) { | ||
| 1618 | return sema.mod.fail(&block.base, src, "expression value is ignored", .{}); | ||
| 1619 | } | ||
| 1620 | return sema.mod.constVoid(block.arena, .unneeded); | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | fn zirFnType(sema: *Sema, block: *Scope.Block, fntype: zir.Inst.Index, var_args: bool) InnerError!*Inst { | ||
| 1624 | const tracy = trace(@src()); | ||
| 1625 | defer tracy.end(); | ||
| 1626 | |||
| 1627 | return fnTypeCommon( | ||
| 1628 | mod, | ||
| 1629 | scope, | ||
| 1630 | &fntype.base, | ||
| 1631 | fntype.positionals.param_types, | ||
| 1632 | fntype.positionals.return_type, | ||
| 1633 | .Unspecified, | ||
| 1634 | var_args, | ||
| 1635 | ); | ||
| 1636 | } | ||
| 1637 | |||
| 1638 | fn zirFnTypeCc(sema: *Sema, block: *Scope.Block, fntype: zir.Inst.Index, var_args: bool) InnerError!*Inst { | ||
| 1639 | const tracy = trace(@src()); | ||
| 1640 | defer tracy.end(); | ||
| 1641 | |||
| 1642 | const cc_tv = try resolveInstConst(mod, scope, fntype.positionals.cc); | ||
| 1643 | // TODO once we're capable of importing and analyzing decls from | ||
| 1644 | // std.builtin, this needs to change | ||
| 1645 | const cc_str = cc_tv.val.castTag(.enum_literal).?.data; | ||
| 1646 | const cc = std.meta.stringToEnum(std.builtin.CallingConvention, cc_str) orelse | ||
| 1647 | return sema.mod.fail(&block.base, fntype.positionals.cc.src, "Unknown calling convention {s}", .{cc_str}); | ||
| 1648 | return fnTypeCommon( | ||
| 1649 | mod, | ||
| 1650 | scope, | ||
| 1651 | &fntype.base, | ||
| 1652 | fntype.positionals.param_types, | ||
| 1653 | fntype.positionals.return_type, | ||
| 1654 | cc, | ||
| 1655 | var_args, | ||
| 1656 | ); | ||
| 1657 | } | ||
| 1658 | |||
| 1659 | fn fnTypeCommon( | ||
| 1660 | sema: *Sema, | ||
| 1661 | block: *Scope.Block, | ||
| 1662 | zir_inst: zir.Inst.Index, | ||
| 1663 | zir_param_types: []zir.Inst.Index, | ||
| 1664 | zir_return_type: zir.Inst.Index, | ||
| 1665 | cc: std.builtin.CallingConvention, | ||
| 1666 | var_args: bool, | ||
| 1667 | ) InnerError!*Inst { | ||
| 1668 | const return_type = try sema.resolveType(block, zir_return_type); | ||
| 1669 | |||
| 1670 | // Hot path for some common function types. | ||
| 1671 | if (zir_param_types.len == 0 and !var_args) { | ||
| 1672 | if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { | ||
| 1673 | return sema.mod.constType(block.arena, zir_inst.src, Type.initTag(.fn_noreturn_no_args)); | ||
| 1674 | } | ||
| 1675 | |||
| 1676 | if (return_type.zigTypeTag() == .Void and cc == .Unspecified) { | ||
| 1677 | return sema.mod.constType(block.arena, zir_inst.src, Type.initTag(.fn_void_no_args)); | ||
| 1678 | } | ||
| 1679 | |||
| 1680 | if (return_type.zigTypeTag() == .NoReturn and cc == .Naked) { | ||
| 1681 | return sema.mod.constType(block.arena, zir_inst.src, Type.initTag(.fn_naked_noreturn_no_args)); | ||
| 1682 | } | ||
| 1683 | |||
| 1684 | if (return_type.zigTypeTag() == .Void and cc == .C) { | ||
| 1685 | return sema.mod.constType(block.arena, zir_inst.src, Type.initTag(.fn_ccc_void_no_args)); | ||
| 1686 | } | ||
| 1687 | } | ||
| 1688 | |||
| 1689 | const param_types = try block.arena.alloc(Type, zir_param_types.len); | ||
| 1690 | for (zir_param_types) |param_type, i| { | ||
| 1691 | const resolved = try sema.resolveType(block, param_type); | ||
| 1692 | // TODO skip for comptime params | ||
| 1693 | if (!resolved.isValidVarType(false)) { | ||
| 1694 | return sema.mod.fail(&block.base, param_type.src, "parameter of type '{}' must be declared comptime", .{resolved}); | ||
| 1695 | } | ||
| 1696 | param_types[i] = resolved; | ||
| 1697 | } | ||
| 1698 | |||
| 1699 | const fn_ty = try Type.Tag.function.create(block.arena, .{ | ||
| 1700 | .param_types = param_types, | ||
| 1701 | .return_type = return_type, | ||
| 1702 | .cc = cc, | ||
| 1703 | .is_var_args = var_args, | ||
| 1704 | }); | ||
| 1705 | return sema.mod.constType(block.arena, zir_inst.src, fn_ty); | ||
| 1706 | } | ||
| 1707 | |||
| 1708 | fn zirAs(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1709 | const tracy = trace(@src()); | ||
| 1710 | defer tracy.end(); | ||
| 1711 | |||
| 1712 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1713 | const dest_type = try sema.resolveType(block, bin_inst.lhs); | ||
| 1714 | const tzir_inst = sema.resolveInst(block, bin_inst.rhs); | ||
| 1715 | return sema.coerce(scope, dest_type, tzir_inst); | ||
| 1716 | } | ||
| 1717 | |||
| 1718 | fn zirPtrtoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1719 | const tracy = trace(@src()); | ||
| 1720 | defer tracy.end(); | ||
| 1721 | |||
| 1722 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 1723 | const ptr = sema.resolveInst(block, inst_data.operand); | ||
| 1724 | if (ptr.ty.zigTypeTag() != .Pointer) { | ||
| 1725 | const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | ||
| 1726 | return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr.ty}); | ||
| 1727 | } | ||
| 1728 | // TODO handle known-pointer-address | ||
| 1729 | const src = inst_data.src(); | ||
| 1730 | try sema.requireRuntimeBlock(block, src); | ||
| 1731 | const ty = Type.initTag(.usize); | ||
| 1732 | return block.addUnOp(src, ty, .ptrtoint, ptr); | ||
| 1733 | } | ||
| 1734 | |||
| 1735 | fn zirFieldVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1736 | const tracy = trace(@src()); | ||
| 1737 | defer tracy.end(); | ||
| 1738 | |||
| 1739 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1740 | const src = inst_data.src(); | ||
| 1741 | const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; | ||
| 1742 | const extra = sema.code.extraData(zir.Inst.Field, inst_data.payload_index).data; | ||
| 1743 | const field_name = sema.code.string_bytes[extra.field_name_start..][0..extra.field_name_len]; | ||
| 1744 | const object = sema.resolveInst(block, extra.lhs); | ||
| 1745 | const object_ptr = try sema.analyzeRef(block, src, object); | ||
| 1746 | const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src); | ||
| 1747 | return sema.analyzeDeref(block, src, result_ptr, result_ptr.src); | ||
| 1748 | } | ||
| 1749 | |||
| 1750 | fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1751 | const tracy = trace(@src()); | ||
| 1752 | defer tracy.end(); | ||
| 1753 | |||
| 1754 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1755 | const src = inst_data.src(); | ||
| 1756 | const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; | ||
| 1757 | const extra = sema.code.extraData(zir.Inst.Field, inst_data.payload_index).data; | ||
| 1758 | const field_name = sema.code.string_bytes[extra.field_name_start..][0..extra.field_name_len]; | ||
| 1759 | const object_ptr = sema.resolveInst(block, extra.lhs); | ||
| 1760 | return sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src); | ||
| 1761 | } | ||
| 1762 | |||
| 1763 | fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1764 | const tracy = trace(@src()); | ||
| 1765 | defer tracy.end(); | ||
| 1766 | |||
| 1767 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1768 | const src = inst_data.src(); | ||
| 1769 | const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | ||
| 1770 | const extra = sema.code.extraData(zir.Inst.FieldNamed, inst_data.payload_index).data; | ||
| 1771 | const object = sema.resolveInst(block, extra.lhs); | ||
| 1772 | const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); | ||
| 1773 | const object_ptr = try sema.analyzeRef(block, src, object); | ||
| 1774 | const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src); | ||
| 1775 | return sema.analyzeDeref(block, src, result_ptr, src); | ||
| 1776 | } | ||
| 1777 | |||
| 1778 | fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1779 | const tracy = trace(@src()); | ||
| 1780 | defer tracy.end(); | ||
| 1781 | |||
| 1782 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1783 | const src = inst_data.src(); | ||
| 1784 | const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | ||
| 1785 | const extra = sema.code.extraData(zir.Inst.FieldNamed, inst_data.payload_index).data; | ||
| 1786 | const object_ptr = sema.resolveInst(block, extra.lhs); | ||
| 1787 | const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); | ||
| 1788 | return sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src); | ||
| 1789 | } | ||
| 1790 | |||
| 1791 | fn zirIntcast(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1792 | const tracy = trace(@src()); | ||
| 1793 | defer tracy.end(); | ||
| 1794 | |||
| 1795 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1796 | const dest_type = try sema.resolveType(block, bin_inst.lhs); | ||
| 1797 | const operand = sema.resolveInst(bin_inst.rhs); | ||
| 1798 | |||
| 1799 | const dest_is_comptime_int = switch (dest_type.zigTypeTag()) { | ||
| 1800 | .ComptimeInt => true, | ||
| 1801 | .Int => false, | ||
| 1802 | else => return mod.fail( | ||
| 1803 | scope, | ||
| 1804 | inst.positionals.lhs.src, | ||
| 1805 | "expected integer type, found '{}'", | ||
| 1806 | .{ | ||
| 1807 | dest_type, | ||
| 1808 | }, | ||
| 1809 | ), | ||
| 1810 | }; | ||
| 1811 | |||
| 1812 | switch (operand.ty.zigTypeTag()) { | ||
| 1813 | .ComptimeInt, .Int => {}, | ||
| 1814 | else => return mod.fail( | ||
| 1815 | scope, | ||
| 1816 | inst.positionals.rhs.src, | ||
| 1817 | "expected integer type, found '{}'", | ||
| 1818 | .{operand.ty}, | ||
| 1819 | ), | ||
| 1820 | } | ||
| 1821 | |||
| 1822 | if (operand.value() != null) { | ||
| 1823 | return sema.coerce(scope, dest_type, operand); | ||
| 1824 | } else if (dest_is_comptime_int) { | ||
| 1825 | return sema.mod.fail(&block.base, inst.base.src, "unable to cast runtime value to 'comptime_int'", .{}); | ||
| 1826 | } | ||
| 1827 | |||
| 1828 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement analyze widen or shorten int", .{}); | ||
| 1829 | } | ||
| 1830 | |||
| 1831 | fn zirBitcast(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1832 | const tracy = trace(@src()); | ||
| 1833 | defer tracy.end(); | ||
| 1834 | |||
| 1835 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1836 | const dest_type = try sema.resolveType(block, bin_inst.lhs); | ||
| 1837 | const operand = sema.resolveInst(bin_inst.rhs); | ||
| 1838 | return mod.bitcast(scope, dest_type, operand); | ||
| 1839 | } | ||
| 1840 | |||
| 1841 | fn zirFloatcast(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1842 | const tracy = trace(@src()); | ||
| 1843 | defer tracy.end(); | ||
| 1844 | |||
| 1845 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1846 | const dest_type = try sema.resolveType(block, bin_inst.lhs); | ||
| 1847 | const operand = sema.resolveInst(bin_inst.rhs); | ||
| 1848 | |||
| 1849 | const dest_is_comptime_float = switch (dest_type.zigTypeTag()) { | ||
| 1850 | .ComptimeFloat => true, | ||
| 1851 | .Float => false, | ||
| 1852 | else => return mod.fail( | ||
| 1853 | scope, | ||
| 1854 | inst.positionals.lhs.src, | ||
| 1855 | "expected float type, found '{}'", | ||
| 1856 | .{ | ||
| 1857 | dest_type, | ||
| 1858 | }, | ||
| 1859 | ), | ||
| 1860 | }; | ||
| 1861 | |||
| 1862 | switch (operand.ty.zigTypeTag()) { | ||
| 1863 | .ComptimeFloat, .Float, .ComptimeInt => {}, | ||
| 1864 | else => return mod.fail( | ||
| 1865 | scope, | ||
| 1866 | inst.positionals.rhs.src, | ||
| 1867 | "expected float type, found '{}'", | ||
| 1868 | .{operand.ty}, | ||
| 1869 | ), | ||
| 1870 | } | ||
| 1871 | |||
| 1872 | if (operand.value() != null) { | ||
| 1873 | return sema.coerce(scope, dest_type, operand); | ||
| 1874 | } else if (dest_is_comptime_float) { | ||
| 1875 | return sema.mod.fail(&block.base, inst.base.src, "unable to cast runtime value to 'comptime_float'", .{}); | ||
| 1876 | } | ||
| 1877 | |||
| 1878 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement analyze widen or shorten float", .{}); | ||
| 1879 | } | ||
| 1880 | |||
| 1881 | fn zirElemVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1882 | const tracy = trace(@src()); | ||
| 1883 | defer tracy.end(); | ||
| 1884 | |||
| 1885 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1886 | const array = sema.resolveInst(block, bin_inst.lhs); | ||
| 1887 | const array_ptr = try sema.analyzeRef(block, sema.src, array); | ||
| 1888 | const elem_index = sema.resolveInst(block, bin_inst.rhs); | ||
| 1889 | const result_ptr = try sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src); | ||
| 1890 | return sema.analyzeDeref(block, sema.src, result_ptr, sema.src); | ||
| 1891 | } | ||
| 1892 | |||
| 1893 | fn zirElemValNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1894 | const tracy = trace(@src()); | ||
| 1895 | defer tracy.end(); | ||
| 1896 | |||
| 1897 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1898 | const src = inst_data.src(); | ||
| 1899 | const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node }; | ||
| 1900 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; | ||
| 1901 | const array = sema.resolveInst(block, extra.lhs); | ||
| 1902 | const array_ptr = try sema.analyzeRef(block, src, array); | ||
| 1903 | const elem_index = sema.resolveInst(block, extra.rhs); | ||
| 1904 | const result_ptr = try sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src); | ||
| 1905 | return sema.analyzeDeref(block, src, result_ptr, src); | ||
| 1906 | } | ||
| 1907 | |||
| 1908 | fn zirElemPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1909 | const tracy = trace(@src()); | ||
| 1910 | defer tracy.end(); | ||
| 1911 | |||
| 1912 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1913 | const array_ptr = sema.resolveInst(block, bin_inst.lhs); | ||
| 1914 | const elem_index = sema.resolveInst(block, bin_inst.rhs); | ||
| 1915 | return sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src); | ||
| 1916 | } | ||
| 1917 | |||
| 1918 | fn zirElemPtrNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1919 | const tracy = trace(@src()); | ||
| 1920 | defer tracy.end(); | ||
| 1921 | |||
| 1922 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1923 | const src = inst_data.src(); | ||
| 1924 | const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node }; | ||
| 1925 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; | ||
| 1926 | const array_ptr = sema.resolveInst(block, extra.lhs); | ||
| 1927 | const elem_index = sema.resolveInst(block, extra.rhs); | ||
| 1928 | return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src); | ||
| 1929 | } | ||
| 1930 | |||
| 1931 | fn zirSliceStart(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1932 | const tracy = trace(@src()); | ||
| 1933 | defer tracy.end(); | ||
| 1934 | |||
| 1935 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1936 | const src = inst_data.src(); | ||
| 1937 | const extra = sema.code.extraData(zir.Inst.SliceStart, inst_data.payload_index).data; | ||
| 1938 | const array_ptr = sema.resolveInst(extra.lhs); | ||
| 1939 | const start = sema.resolveInst(extra.start); | ||
| 1940 | |||
| 1941 | return sema.analyzeSlice(block, src, array_ptr, start, null, null, .unneeded); | ||
| 1942 | } | ||
| 1943 | |||
| 1944 | fn zirSliceEnd(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1945 | const tracy = trace(@src()); | ||
| 1946 | defer tracy.end(); | ||
| 1947 | |||
| 1948 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1949 | const src = inst_data.src(); | ||
| 1950 | const extra = sema.code.extraData(zir.Inst.SliceEnd, inst_data.payload_index).data; | ||
| 1951 | const array_ptr = sema.resolveInst(extra.lhs); | ||
| 1952 | const start = sema.resolveInst(extra.start); | ||
| 1953 | const end = sema.resolveInst(extra.end); | ||
| 1954 | |||
| 1955 | return sema.analyzeSlice(block, src, array_ptr, start, end, null, .unneeded); | ||
| 1956 | } | ||
| 1957 | |||
| 1958 | fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1959 | const tracy = trace(@src()); | ||
| 1960 | defer tracy.end(); | ||
| 1961 | |||
| 1962 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1963 | const src = inst_data.src(); | ||
| 1964 | const sentinel_src: LazySrcLoc = .{ .node_offset_slice_sentinel = inst_data.src_node }; | ||
| 1965 | const extra = sema.code.extraData(zir.Inst.SliceSentinel, inst_data.payload_index).data; | ||
| 1966 | const array_ptr = sema.resolveInst(extra.lhs); | ||
| 1967 | const start = sema.resolveInst(extra.start); | ||
| 1968 | const end = sema.resolveInst(extra.end); | ||
| 1969 | const sentinel = sema.resolveInst(extra.sentinel); | ||
| 1970 | |||
| 1971 | return sema.analyzeSlice(block, inst.base.src, array_ptr, start, end, sentinel, sentinel_src); | ||
| 1972 | } | ||
| 1973 | |||
| 1974 | fn zirSwitchRange(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1975 | const tracy = trace(@src()); | ||
| 1976 | defer tracy.end(); | ||
| 1977 | |||
| 1978 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1979 | const start = sema.resolveInst(bin_inst.lhs); | ||
| 1980 | const end = sema.resolveInst(bin_inst.rhs); | ||
| 1981 | |||
| 1982 | switch (start.ty.zigTypeTag()) { | ||
| 1983 | .Int, .ComptimeInt => {}, | ||
| 1984 | else => return sema.mod.constVoid(block.arena, .unneeded), | ||
| 1985 | } | ||
| 1986 | switch (end.ty.zigTypeTag()) { | ||
| 1987 | .Int, .ComptimeInt => {}, | ||
| 1988 | else => return sema.mod.constVoid(block.arena, .unneeded), | ||
| 1989 | } | ||
| 1990 | // .switch_range must be inside a comptime scope | ||
| 1991 | const start_val = start.value().?; | ||
| 1992 | const end_val = end.value().?; | ||
| 1993 | if (start_val.compare(.gte, end_val)) { | ||
| 1994 | return sema.mod.fail(&block.base, inst.base.src, "range start value must be smaller than the end value", .{}); | ||
| 1995 | } | ||
| 1996 | return sema.mod.constVoid(block.arena, .unneeded); | ||
| 1997 | } | ||
| 1998 | |||
| 1999 | fn zirSwitchBr( | ||
| 2000 | sema: *Sema, | ||
| 2001 | parent_block: *Scope.Block, | ||
| 2002 | inst: zir.Inst.Index, | ||
| 2003 | ref: bool, | ||
| 2004 | ) InnerError!*Inst { | ||
| 2005 | const tracy = trace(@src()); | ||
| 2006 | defer tracy.end(); | ||
| 2007 | |||
| 2008 | if (true) @panic("TODO rework with zir-memory-layout in mind"); | ||
| 2009 | |||
| 2010 | const target_ptr = sema.resolveInst(block, inst.positionals.target); | ||
| 2011 | const target = if (ref) | ||
| 2012 | try sema.analyzeDeref(block, inst.base.src, target_ptr, inst.positionals.target.src) | ||
| 2013 | else | ||
| 2014 | target_ptr; | ||
| 2015 | try validateSwitch(mod, scope, target, inst); | ||
| 2016 | |||
| 2017 | if (try mod.resolveDefinedValue(scope, target)) |target_val| { | ||
| 2018 | for (inst.positionals.cases) |case| { | ||
| 2019 | const resolved = sema.resolveInst(block, case.item); | ||
| 2020 | const casted = try sema.coerce(scope, target.ty, resolved); | ||
| 2021 | const item = try sema.resolveConstValue(parent_block, case_src, casted); | ||
| 2022 | |||
| 2023 | if (target_val.eql(item)) { | ||
| 2024 | try sema.body(scope.cast(Scope.Block).?, case.body); | ||
| 2025 | return mod.constNoReturn(scope, inst.base.src); | ||
| 2026 | } | ||
| 2027 | } | ||
| 2028 | try sema.body(scope.cast(Scope.Block).?, inst.positionals.else_body); | ||
| 2029 | return mod.constNoReturn(scope, inst.base.src); | ||
| 2030 | } | ||
| 2031 | |||
| 2032 | if (inst.positionals.cases.len == 0) { | ||
| 2033 | // no cases just analyze else_branch | ||
| 2034 | try sema.body(scope.cast(Scope.Block).?, inst.positionals.else_body); | ||
| 2035 | return mod.constNoReturn(scope, inst.base.src); | ||
| 2036 | } | ||
| 2037 | |||
| 2038 | try sema.requireRuntimeBlock(parent_block, inst.base.src); | ||
| 2039 | const cases = try parent_block.arena.alloc(Inst.SwitchBr.Case, inst.positionals.cases.len); | ||
| 2040 | |||
| 2041 | var case_block: Scope.Block = .{ | ||
| 2042 | .parent = parent_block, | ||
| 2043 | .inst_table = parent_block.inst_table, | ||
| 2044 | .func = parent_block.func, | ||
| 2045 | .owner_decl = parent_block.owner_decl, | ||
| 2046 | .src_decl = parent_block.src_decl, | ||
| 2047 | .instructions = .{}, | ||
| 2048 | .arena = parent_block.arena, | ||
| 2049 | .inlining = parent_block.inlining, | ||
| 2050 | .is_comptime = parent_block.is_comptime, | ||
| 2051 | .branch_quota = parent_block.branch_quota, | ||
| 2052 | }; | ||
| 2053 | defer case_block.instructions.deinit(mod.gpa); | ||
| 2054 | |||
| 2055 | for (inst.positionals.cases) |case, i| { | ||
| 2056 | // Reset without freeing. | ||
| 2057 | case_block.instructions.items.len = 0; | ||
| 2058 | |||
| 2059 | const resolved = sema.resolveInst(block, case.item); | ||
| 2060 | const casted = try sema.coerce(scope, target.ty, resolved); | ||
| 2061 | const item = try sema.resolveConstValue(parent_block, case_src, casted); | ||
| 2062 | |||
| 2063 | try sema.body(&case_block, case.body); | ||
| 2064 | |||
| 2065 | cases[i] = .{ | ||
| 2066 | .item = item, | ||
| 2067 | .body = .{ .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items) }, | ||
| 2068 | }; | ||
| 2069 | } | ||
| 2070 | |||
| 2071 | case_block.instructions.items.len = 0; | ||
| 2072 | try sema.body(&case_block, inst.positionals.else_body); | ||
| 2073 | |||
| 2074 | const else_body: ir.Body = .{ | ||
| 2075 | .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items), | ||
| 2076 | }; | ||
| 2077 | |||
| 2078 | return mod.addSwitchBr(parent_block, inst.base.src, target, cases, else_body); | ||
| 2079 | } | ||
| 2080 | |||
| 2081 | fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Inst.Index) InnerError!void { | ||
| 2082 | // validate usage of '_' prongs | ||
| 2083 | if (inst.positionals.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) { | ||
| 2084 | return sema.mod.fail(&block.base, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{}); | ||
| 2085 | // TODO notes "'_' prong here" inst.positionals.cases[last].src | ||
| 2086 | } | ||
| 2087 | |||
| 2088 | // check that target type supports ranges | ||
| 2089 | if (inst.positionals.range) |range_inst| { | ||
| 2090 | switch (target.ty.zigTypeTag()) { | ||
| 2091 | .Int, .ComptimeInt => {}, | ||
| 2092 | else => { | ||
| 2093 | return sema.mod.fail(&block.base, target.src, "ranges not allowed when switching on type {}", .{target.ty}); | ||
| 2094 | // TODO notes "range used here" range_inst.src | ||
| 2095 | }, | ||
| 2096 | } | ||
| 2097 | } | ||
| 2098 | |||
| 2099 | // validate for duplicate items/missing else prong | ||
| 2100 | switch (target.ty.zigTypeTag()) { | ||
| 2101 | .Enum => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Enum", .{}), | ||
| 2102 | .ErrorSet => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .ErrorSet", .{}), | ||
| 2103 | .Union => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Union", .{}), | ||
| 2104 | .Int, .ComptimeInt => { | ||
| 2105 | var range_set = @import("RangeSet.zig").init(mod.gpa); | ||
| 2106 | defer range_set.deinit(); | ||
| 2107 | |||
| 2108 | for (inst.positionals.items) |item| { | ||
| 2109 | const maybe_src = if (item.castTag(.switch_range)) |range| blk: { | ||
| 2110 | const start_resolved = sema.resolveInst(block, range.positionals.lhs); | ||
| 2111 | const start_casted = try sema.coerce(scope, target.ty, start_resolved); | ||
| 2112 | const end_resolved = sema.resolveInst(block, range.positionals.rhs); | ||
| 2113 | const end_casted = try sema.coerce(scope, target.ty, end_resolved); | ||
| 2114 | |||
| 2115 | break :blk try range_set.add( | ||
| 2116 | try sema.resolveConstValue(block, range_start_src, start_casted), | ||
| 2117 | try sema.resolveConstValue(block, range_end_src, end_casted), | ||
| 2118 | item.src, | ||
| 2119 | ); | ||
| 2120 | } else blk: { | ||
| 2121 | const resolved = sema.resolveInst(block, item); | ||
| 2122 | const casted = try sema.coerce(scope, target.ty, resolved); | ||
| 2123 | const value = try sema.resolveConstValue(block, item_src, casted); | ||
| 2124 | break :blk try range_set.add(value, value, item.src); | ||
| 2125 | }; | ||
| 2126 | |||
| 2127 | if (maybe_src) |previous_src| { | ||
| 2128 | return sema.mod.fail(&block.base, item.src, "duplicate switch value", .{}); | ||
| 2129 | // TODO notes "previous value is here" previous_src | ||
| 2130 | } | ||
| 2131 | } | ||
| 2132 | |||
| 2133 | if (target.ty.zigTypeTag() == .Int) { | ||
| 2134 | var arena = std.heap.ArenaAllocator.init(mod.gpa); | ||
| 2135 | defer arena.deinit(); | ||
| 2136 | |||
| 2137 | const start = try target.ty.minInt(&arena, mod.getTarget()); | ||
| 2138 | const end = try target.ty.maxInt(&arena, mod.getTarget()); | ||
| 2139 | if (try range_set.spans(start, end)) { | ||
| 2140 | if (inst.positionals.special_prong == .@"else") { | ||
| 2141 | return sema.mod.fail(&block.base, inst.base.src, "unreachable else prong, all cases already handled", .{}); | ||
| 2142 | } | ||
| 2143 | return; | ||
| 2144 | } | ||
| 2145 | } | ||
| 2146 | |||
| 2147 | if (inst.positionals.special_prong != .@"else") { | ||
| 2148 | return sema.mod.fail(&block.base, inst.base.src, "switch must handle all possibilities", .{}); | ||
| 2149 | } | ||
| 2150 | }, | ||
| 2151 | .Bool => { | ||
| 2152 | var true_count: u8 = 0; | ||
| 2153 | var false_count: u8 = 0; | ||
| 2154 | for (inst.positionals.items) |item| { | ||
| 2155 | const resolved = sema.resolveInst(block, item); | ||
| 2156 | const casted = try sema.coerce(scope, Type.initTag(.bool), resolved); | ||
| 2157 | if ((try sema.resolveConstValue(block, item_src, casted)).toBool()) { | ||
| 2158 | true_count += 1; | ||
| 2159 | } else { | ||
| 2160 | false_count += 1; | ||
| 2161 | } | ||
| 2162 | |||
| 2163 | if (true_count + false_count > 2) { | ||
| 2164 | return sema.mod.fail(&block.base, item.src, "duplicate switch value", .{}); | ||
| 2165 | } | ||
| 2166 | } | ||
| 2167 | if ((true_count + false_count < 2) and inst.positionals.special_prong != .@"else") { | ||
| 2168 | return sema.mod.fail(&block.base, inst.base.src, "switch must handle all possibilities", .{}); | ||
| 2169 | } | ||
| 2170 | if ((true_count + false_count == 2) and inst.positionals.special_prong == .@"else") { | ||
| 2171 | return sema.mod.fail(&block.base, inst.base.src, "unreachable else prong, all cases already handled", .{}); | ||
| 2172 | } | ||
| 2173 | }, | ||
| 2174 | .EnumLiteral, .Void, .Fn, .Pointer, .Type => { | ||
| 2175 | if (inst.positionals.special_prong != .@"else") { | ||
| 2176 | return sema.mod.fail(&block.base, inst.base.src, "else prong required when switching on type '{}'", .{target.ty}); | ||
| 2177 | } | ||
| 2178 | |||
| 2179 | var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(mod.gpa); | ||
| 2180 | defer seen_values.deinit(); | ||
| 2181 | |||
| 2182 | for (inst.positionals.items) |item| { | ||
| 2183 | const resolved = sema.resolveInst(block, item); | ||
| 2184 | const casted = try sema.coerce(scope, target.ty, resolved); | ||
| 2185 | const val = try sema.resolveConstValue(block, item_src, casted); | ||
| 2186 | |||
| 2187 | if (try seen_values.fetchPut(val, item.src)) |prev| { | ||
| 2188 | return sema.mod.fail(&block.base, item.src, "duplicate switch value", .{}); | ||
| 2189 | // TODO notes "previous value here" prev.value | ||
| 2190 | } | ||
| 2191 | } | ||
| 2192 | }, | ||
| 2193 | |||
| 2194 | .ErrorUnion, | ||
| 2195 | .NoReturn, | ||
| 2196 | .Array, | ||
| 2197 | .Struct, | ||
| 2198 | .Undefined, | ||
| 2199 | .Null, | ||
| 2200 | .Optional, | ||
| 2201 | .BoundFn, | ||
| 2202 | .Opaque, | ||
| 2203 | .Vector, | ||
| 2204 | .Frame, | ||
| 2205 | .AnyFrame, | ||
| 2206 | .ComptimeFloat, | ||
| 2207 | .Float, | ||
| 2208 | => { | ||
| 2209 | return sema.mod.fail(&block.base, target.src, "invalid switch target type '{}'", .{target.ty}); | ||
| 2210 | }, | ||
| 2211 | } | ||
| 2212 | } | ||
| 2213 | |||
| 2214 | fn zirImport(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2215 | const tracy = trace(@src()); | ||
| 2216 | defer tracy.end(); | ||
| 2217 | |||
| 2218 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 2219 | const src = inst_data.src(); | ||
| 2220 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | ||
| 2221 | const operand = try sema.resolveConstString(block, operand_src, inst_data.operand); | ||
| 2222 | |||
| 2223 | const file_scope = sema.analyzeImport(block, src, operand) catch |err| switch (err) { | ||
| 2224 | error.ImportOutsidePkgPath => { | ||
| 2225 | return sema.mod.fail(&block.base, src, "import of file outside package path: '{s}'", .{operand}); | ||
| 2226 | }, | ||
| 2227 | error.FileNotFound => { | ||
| 2228 | return sema.mod.fail(&block.base, src, "unable to find '{s}'", .{operand}); | ||
| 2229 | }, | ||
| 2230 | else => { | ||
| 2231 | // TODO: make sure this gets retried and not cached | ||
| 2232 | return sema.mod.fail(&block.base, src, "unable to open '{s}': {s}", .{ operand, @errorName(err) }); | ||
| 2233 | }, | ||
| 2234 | }; | ||
| 2235 | return sema.mod.constType(block.arena, src, file_scope.root_container.ty); | ||
| 2236 | } | ||
| 2237 | |||
| 2238 | fn zirShl(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2239 | const tracy = trace(@src()); | ||
| 2240 | defer tracy.end(); | ||
| 2241 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement zirShl", .{}); | ||
| 2242 | } | ||
| 2243 | |||
| 2244 | fn zirShr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2245 | const tracy = trace(@src()); | ||
| 2246 | defer tracy.end(); | ||
| 2247 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement zirShr", .{}); | ||
| 2248 | } | ||
| 2249 | |||
| 2250 | fn zirBitwise(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2251 | const tracy = trace(@src()); | ||
| 2252 | defer tracy.end(); | ||
| 2253 | |||
| 2254 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 2255 | const lhs = sema.resolveInst(bin_inst.lhs); | ||
| 2256 | const rhs = sema.resolveInst(bin_inst.rhs); | ||
| 2257 | |||
| 2258 | const instructions = &[_]*Inst{ lhs, rhs }; | ||
| 2259 | const resolved_type = try sema.resolvePeerTypes(block, instructions); | ||
| 2260 | const casted_lhs = try sema.coerce(scope, resolved_type, lhs); | ||
| 2261 | const casted_rhs = try sema.coerce(scope, resolved_type, rhs); | ||
| 2262 | |||
| 2263 | const scalar_type = if (resolved_type.zigTypeTag() == .Vector) | ||
| 2264 | resolved_type.elemType() | ||
| 2265 | else | ||
| 2266 | resolved_type; | ||
| 2267 | |||
| 2268 | const scalar_tag = scalar_type.zigTypeTag(); | ||
| 2269 | |||
| 2270 | if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) { | ||
| 2271 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { | ||
| 2272 | return sema.mod.fail(&block.base, inst.base.src, "vector length mismatch: {d} and {d}", .{ | ||
| 2273 | lhs.ty.arrayLen(), | ||
| 2274 | rhs.ty.arrayLen(), | ||
| 2275 | }); | ||
| 2276 | } | ||
| 2277 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement support for vectors in zirBitwise", .{}); | ||
| 2278 | } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { | ||
| 2279 | return sema.mod.fail(&block.base, inst.base.src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ | ||
| 2280 | lhs.ty, | ||
| 2281 | rhs.ty, | ||
| 2282 | }); | ||
| 2283 | } | ||
| 2284 | |||
| 2285 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; | ||
| 2286 | |||
| 2287 | if (!is_int) { | ||
| 2288 | return sema.mod.fail(&block.base, inst.base.src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); | ||
| 2289 | } | ||
| 2290 | |||
| 2291 | if (casted_lhs.value()) |lhs_val| { | ||
| 2292 | if (casted_rhs.value()) |rhs_val| { | ||
| 2293 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | ||
| 2294 | return sema.mod.constInst(scope, inst.base.src, .{ | ||
| 2295 | .ty = resolved_type, | ||
| 2296 | .val = Value.initTag(.undef), | ||
| 2297 | }); | ||
| 2298 | } | ||
| 2299 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement comptime bitwise operations", .{}); | ||
| 2300 | } | ||
| 2301 | } | ||
| 2302 | |||
| 2303 | try sema.requireRuntimeBlock(block, inst.base.src); | ||
| 2304 | const ir_tag = switch (inst.base.tag) { | ||
| 2305 | .bit_and => Inst.Tag.bit_and, | ||
| 2306 | .bit_or => Inst.Tag.bit_or, | ||
| 2307 | .xor => Inst.Tag.xor, | ||
| 2308 | else => unreachable, | ||
| 2309 | }; | ||
| 2310 | |||
| 2311 | return mod.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs); | ||
| 2312 | } | ||
| 2313 | |||
| 2314 | fn zirBitNot(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2315 | const tracy = trace(@src()); | ||
| 2316 | defer tracy.end(); | ||
| 2317 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement zirBitNot", .{}); | ||
| 2318 | } | ||
| 2319 | |||
| 2320 | fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2321 | const tracy = trace(@src()); | ||
| 2322 | defer tracy.end(); | ||
| 2323 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement zirArrayCat", .{}); | ||
| 2324 | } | ||
| 2325 | |||
| 2326 | fn zirArrayMul(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2327 | const tracy = trace(@src()); | ||
| 2328 | defer tracy.end(); | ||
| 2329 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement zirArrayMul", .{}); | ||
| 2330 | } | ||
| 2331 | |||
| 2332 | fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2333 | const tracy = trace(@src()); | ||
| 2334 | defer tracy.end(); | ||
| 2335 | |||
| 2336 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 2337 | const lhs = sema.resolveInst(bin_inst.lhs); | ||
| 2338 | const rhs = sema.resolveInst(bin_inst.rhs); | ||
| 2339 | |||
| 2340 | const instructions = &[_]*Inst{ lhs, rhs }; | ||
| 2341 | const resolved_type = try sema.resolvePeerTypes(block, instructions); | ||
| 2342 | const casted_lhs = try sema.coerce(scope, resolved_type, lhs); | ||
| 2343 | const casted_rhs = try sema.coerce(scope, resolved_type, rhs); | ||
| 2344 | |||
| 2345 | const scalar_type = if (resolved_type.zigTypeTag() == .Vector) | ||
| 2346 | resolved_type.elemType() | ||
| 2347 | else | ||
| 2348 | resolved_type; | ||
| 2349 | |||
| 2350 | const scalar_tag = scalar_type.zigTypeTag(); | ||
| 2351 | |||
| 2352 | if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) { | ||
| 2353 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { | ||
| 2354 | return sema.mod.fail(&block.base, inst.base.src, "vector length mismatch: {d} and {d}", .{ | ||
| 2355 | lhs.ty.arrayLen(), | ||
| 2356 | rhs.ty.arrayLen(), | ||
| 2357 | }); | ||
| 2358 | } | ||
| 2359 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement support for vectors in zirBinOp", .{}); | ||
| 2360 | } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { | ||
| 2361 | return sema.mod.fail(&block.base, inst.base.src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ | ||
| 2362 | lhs.ty, | ||
| 2363 | rhs.ty, | ||
| 2364 | }); | ||
| 2365 | } | ||
| 2366 | |||
| 2367 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; | ||
| 2368 | const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat; | ||
| 2369 | |||
| 2370 | if (!is_int and !(is_float and floatOpAllowed(inst.base.tag))) { | ||
| 2371 | return sema.mod.fail(&block.base, inst.base.src, "invalid operands to binary expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); | ||
| 2372 | } | ||
| 2373 | |||
| 2374 | if (casted_lhs.value()) |lhs_val| { | ||
| 2375 | if (casted_rhs.value()) |rhs_val| { | ||
| 2376 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | ||
| 2377 | return sema.mod.constInst(scope, inst.base.src, .{ | ||
| 2378 | .ty = resolved_type, | ||
| 2379 | .val = Value.initTag(.undef), | ||
| 2380 | }); | ||
| 2381 | } | ||
| 2382 | return analyzeInstComptimeOp(mod, scope, scalar_type, inst, lhs_val, rhs_val); | ||
| 2383 | } | ||
| 2384 | } | ||
| 2385 | |||
| 2386 | try sema.requireRuntimeBlock(block, inst.base.src); | ||
| 2387 | const ir_tag: Inst.Tag = switch (inst.base.tag) { | ||
| 2388 | .add => .add, | ||
| 2389 | .addwrap => .addwrap, | ||
| 2390 | .sub => .sub, | ||
| 2391 | .subwrap => .subwrap, | ||
| 2392 | .mul => .mul, | ||
| 2393 | .mulwrap => .mulwrap, | ||
| 2394 | else => return sema.mod.fail(&block.base, inst.base.src, "TODO implement arithmetic for operand '{s}''", .{@tagName(inst.base.tag)}), | ||
| 2395 | }; | ||
| 2396 | |||
| 2397 | return mod.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs); | ||
| 2398 | } | ||
| 2399 | |||
| 2400 | /// Analyzes operands that are known at comptime | ||
| 2401 | fn analyzeInstComptimeOp(sema: *Sema, block: *Scope.Block, res_type: Type, inst: zir.Inst.Index, lhs_val: Value, rhs_val: Value) InnerError!*Inst { | ||
| 2402 | // incase rhs is 0, simply return lhs without doing any calculations | ||
| 2403 | // TODO Once division is implemented we should throw an error when dividing by 0. | ||
| 2404 | if (rhs_val.compareWithZero(.eq)) { | ||
| 2405 | return sema.mod.constInst(scope, inst.base.src, .{ | ||
| 2406 | .ty = res_type, | ||
| 2407 | .val = lhs_val, | ||
| 2408 | }); | ||
| 2409 | } | ||
| 2410 | const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt; | ||
| 2411 | |||
| 2412 | const value = switch (inst.base.tag) { | ||
| 2413 | .add => blk: { | ||
| 2414 | const val = if (is_int) | ||
| 2415 | try Module.intAdd(block.arena, lhs_val, rhs_val) | ||
| 2416 | else | ||
| 2417 | try mod.floatAdd(scope, res_type, inst.base.src, lhs_val, rhs_val); | ||
| 2418 | break :blk val; | ||
| 2419 | }, | ||
| 2420 | .sub => blk: { | ||
| 2421 | const val = if (is_int) | ||
| 2422 | try Module.intSub(block.arena, lhs_val, rhs_val) | ||
| 2423 | else | ||
| 2424 | try mod.floatSub(scope, res_type, inst.base.src, lhs_val, rhs_val); | ||
| 2425 | break :blk val; | ||
| 2426 | }, | ||
| 2427 | else => return sema.mod.fail(&block.base, inst.base.src, "TODO Implement arithmetic operand '{s}'", .{@tagName(inst.base.tag)}), | ||
| 2428 | }; | ||
| 2429 | |||
| 2430 | log.debug("{s}({}, {}) result: {}", .{ @tagName(inst.base.tag), lhs_val, rhs_val, value }); | ||
| 2431 | |||
| 2432 | return sema.mod.constInst(scope, inst.base.src, .{ | ||
| 2433 | .ty = res_type, | ||
| 2434 | .val = value, | ||
| 2435 | }); | ||
| 2436 | } | ||
| 2437 | |||
| 2438 | fn zirDeref(sema: *Sema, block: *Scope.Block, deref: zir.Inst.Index) InnerError!*Inst { | ||
| 2439 | const tracy = trace(@src()); | ||
| 2440 | defer tracy.end(); | ||
| 2441 | |||
| 2442 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 2443 | const src = inst_data.src(); | ||
| 2444 | const ptr_src: LazySrcLoc = .{ .node_offset_deref_ptr = inst_data.src_node }; | ||
| 2445 | const ptr = sema.resolveInst(block, inst_data.operand); | ||
| 2446 | return sema.analyzeDeref(block, src, ptr, ptr_src); | ||
| 2447 | } | ||
| 2448 | |||
| 2449 | fn zirAsm( | ||
| 2450 | sema: *Sema, | ||
| 2451 | block: *Scope.Block, | ||
| 2452 | assembly: zir.Inst.Index, | ||
| 2453 | is_volatile: bool, | ||
| 2454 | ) InnerError!*Inst { | ||
| 2455 | const tracy = trace(@src()); | ||
| 2456 | defer tracy.end(); | ||
| 2457 | |||
| 2458 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2459 | const src = inst_data.src(); | ||
| 2460 | const asm_source_src: LazySrcLoc = .{ .node_offset_asm_source = inst_data.src_node }; | ||
| 2461 | const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = inst_data.src_node }; | ||
| 2462 | const extra = sema.code.extraData(zir.Inst.Asm, inst_data.payload_index); | ||
| 2463 | const return_type = try sema.resolveType(block, ret_ty_src, extra.data.return_type); | ||
| 2464 | const asm_source = try sema.resolveConstString(block, asm_source_src, extra.data.asm_source); | ||
| 2465 | |||
| 2466 | var extra_i = extra.end; | ||
| 2467 | const output = if (extra.data.output != 0) blk: { | ||
| 2468 | const name = sema.code.nullTerminatedString(sema.code.extra[extra_i]); | ||
| 2469 | extra_i += 1; | ||
| 2470 | break :blk .{ | ||
| 2471 | .name = name, | ||
| 2472 | .inst = try sema.resolveInst(block, extra.data.output), | ||
| 2473 | }; | ||
| 2474 | } else null; | ||
| 2475 | |||
| 2476 | const args = try block.arena.alloc(*Inst, extra.data.args.len); | ||
| 2477 | const inputs = try block.arena.alloc([]const u8, extra.data.args_len); | ||
| 2478 | const clobbers = try block.arena.alloc([]const u8, extra.data.clobbers_len); | ||
| 2479 | |||
| 2480 | for (args) |*arg| { | ||
| 2481 | const uncasted = sema.resolveInst(block, sema.code.extra[extra_i]); | ||
| 2482 | extra_i += 1; | ||
| 2483 | arg.* = try sema.coerce(block, Type.initTag(.usize), uncasted); | ||
| 2484 | } | ||
| 2485 | for (inputs) |*name| { | ||
| 2486 | name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]); | ||
| 2487 | extra_i += 1; | ||
| 2488 | } | ||
| 2489 | for (clobbers) |*name| { | ||
| 2490 | name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]); | ||
| 2491 | extra_i += 1; | ||
| 2492 | } | ||
| 2493 | |||
| 2494 | try sema.requireRuntimeBlock(block, src); | ||
| 2495 | const inst = try block.arena.create(Inst.Assembly); | ||
| 2496 | inst.* = .{ | ||
| 2497 | .base = .{ | ||
| 2498 | .tag = .assembly, | ||
| 2499 | .ty = return_type, | ||
| 2500 | .src = src, | ||
| 2501 | }, | ||
| 2502 | .asm_source = asm_source, | ||
| 2503 | .is_volatile = is_volatile, | ||
| 2504 | .output = if (output) |o| o.inst else null, | ||
| 2505 | .output_name = if (output) |o| o.name else null, | ||
| 2506 | .inputs = inputs, | ||
| 2507 | .clobbers = clobbers, | ||
| 2508 | .args = args, | ||
| 2509 | }; | ||
| 2510 | try block.instructions.append(mod.gpa, &inst.base); | ||
| 2511 | return &inst.base; | ||
| 2512 | } | ||
| 2513 | |||
| 2514 | fn zirCmp( | ||
| 2515 | sema: *Sema, | ||
| 2516 | block: *Scope.Block, | ||
| 2517 | inst: zir.Inst.Index, | ||
| 2518 | op: std.math.CompareOperator, | ||
| 2519 | ) InnerError!*Inst { | ||
| 2520 | const tracy = trace(@src()); | ||
| 2521 | defer tracy.end(); | ||
| 2522 | |||
| 2523 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 2524 | const lhs = sema.resolveInst(bin_inst.lhs); | ||
| 2525 | const rhs = sema.resolveInst(bin_inst.rhs); | ||
| 2526 | |||
| 2527 | const is_equality_cmp = switch (op) { | ||
| 2528 | .eq, .neq => true, | ||
| 2529 | else => false, | ||
| 2530 | }; | ||
| 2531 | const lhs_ty_tag = lhs.ty.zigTypeTag(); | ||
| 2532 | const rhs_ty_tag = rhs.ty.zigTypeTag(); | ||
| 2533 | if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) { | ||
| 2534 | // null == null, null != null | ||
| 2535 | return mod.constBool(block.arena, inst.base.src, op == .eq); | ||
| 2536 | } else if (is_equality_cmp and | ||
| 2537 | ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or | ||
| 2538 | rhs_ty_tag == .Null and lhs_ty_tag == .Optional)) | ||
| 2539 | { | ||
| 2540 | // comparing null with optionals | ||
| 2541 | const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs; | ||
| 2542 | return sema.analyzeIsNull(block, inst.base.src, opt_operand, op == .neq); | ||
| 2543 | } else if (is_equality_cmp and | ||
| 2544 | ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr()))) | ||
| 2545 | { | ||
| 2546 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement C pointer cmp", .{}); | ||
| 2547 | } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) { | ||
| 2548 | const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty; | ||
| 2549 | return sema.mod.fail(&block.base, inst.base.src, "comparison of '{}' with null", .{non_null_type}); | ||
| 2550 | } else if (is_equality_cmp and | ||
| 2551 | ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or | ||
| 2552 | (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union))) | ||
| 2553 | { | ||
| 2554 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{}); | ||
| 2555 | } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { | ||
| 2556 | if (!is_equality_cmp) { | ||
| 2557 | return sema.mod.fail(&block.base, inst.base.src, "{s} operator not allowed for errors", .{@tagName(op)}); | ||
| 2558 | } | ||
| 2559 | if (rhs.value()) |rval| { | ||
| 2560 | if (lhs.value()) |lval| { | ||
| 2561 | // TODO optimisation oppurtunity: evaluate if std.mem.eql is faster with the names, or calling to Module.getErrorValue to get the values and then compare them is faster | ||
| 2562 | return mod.constBool(block.arena, inst.base.src, std.mem.eql(u8, lval.castTag(.@"error").?.data.name, rval.castTag(.@"error").?.data.name) == (op == .eq)); | ||
| 2563 | } | ||
| 2564 | } | ||
| 2565 | try sema.requireRuntimeBlock(block, inst.base.src); | ||
| 2566 | return mod.addBinOp(b, inst.base.src, Type.initTag(.bool), if (op == .eq) .cmp_eq else .cmp_neq, lhs, rhs); | ||
| 2567 | } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) { | ||
| 2568 | // This operation allows any combination of integer and float types, regardless of the | ||
| 2569 | // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for | ||
| 2570 | // numeric types. | ||
| 2571 | return mod.cmpNumeric(scope, inst.base.src, lhs, rhs, op); | ||
| 2572 | } else if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) { | ||
| 2573 | if (!is_equality_cmp) { | ||
| 2574 | return sema.mod.fail(&block.base, inst.base.src, "{s} operator not allowed for types", .{@tagName(op)}); | ||
| 2575 | } | ||
| 2576 | return mod.constBool(block.arena, inst.base.src, lhs.value().?.eql(rhs.value().?) == (op == .eq)); | ||
| 2577 | } | ||
| 2578 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement more cmp analysis", .{}); | ||
| 2579 | } | ||
| 2580 | |||
| 2581 | fn zirTypeof(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2582 | const tracy = trace(@src()); | ||
| 2583 | defer tracy.end(); | ||
| 2584 | |||
| 2585 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 2586 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 2587 | return sema.mod.constType(block.arena, inst_data.src(), operand.ty); | ||
| 2588 | } | ||
| 2589 | |||
| 2590 | fn zirTypeofPeer(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2591 | const tracy = trace(@src()); | ||
| 2592 | defer tracy.end(); | ||
| 2593 | |||
| 2594 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2595 | const src = inst_data.src(); | ||
| 2596 | const extra = sema.code.extraData(zir.Inst.MultiOp, inst_data.payload_index); | ||
| 2597 | |||
| 2598 | const inst_list = try mod.gpa.alloc(*ir.Inst, extra.data.operands_len); | ||
| 2599 | defer mod.gpa.free(inst_list); | ||
| 2600 | |||
| 2601 | const src_list = try mod.gpa.alloc(LazySrcLoc, extra.data.operands_len); | ||
| 2602 | defer mod.gpa.free(src_list); | ||
| 2603 | |||
| 2604 | for (sema.code.extra[extra.end..][0..extra.data.operands_len]) |arg_ref, i| { | ||
| 2605 | inst_list[i] = sema.resolveInst(block, arg_ref); | ||
| 2606 | src_list[i] = .{ .node_offset_builtin_call_argn = inst_data.src_node }; | ||
| 2607 | } | ||
| 2608 | |||
| 2609 | const result_type = try sema.resolvePeerTypes(block, inst_list, src_list); | ||
| 2610 | return sema.mod.constType(block.arena, src, result_type); | ||
| 2611 | } | ||
| 2612 | |||
| 2613 | fn zirBoolNot(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2614 | const tracy = trace(@src()); | ||
| 2615 | defer tracy.end(); | ||
| 2616 | |||
| 2617 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 2618 | const src = inst_data.src(); | ||
| 2619 | const uncasted_operand = sema.resolveInst(block, inst_data.operand); | ||
| 2620 | |||
| 2621 | const bool_type = Type.initTag(.bool); | ||
| 2622 | const operand = try sema.coerce(scope, bool_type, uncasted_operand); | ||
| 2623 | if (try mod.resolveDefinedValue(scope, operand)) |val| { | ||
| 2624 | return mod.constBool(block.arena, src, !val.toBool()); | ||
| 2625 | } | ||
| 2626 | try sema.requireRuntimeBlock(block, src); | ||
| 2627 | return block.addUnOp(src, bool_type, .not, operand); | ||
| 2628 | } | ||
| 2629 | |||
| 2630 | fn zirBoolOp( | ||
| 2631 | sema: *Sema, | ||
| 2632 | block: *Scope.Block, | ||
| 2633 | inst: zir.Inst.Index, | ||
| 2634 | comptime is_bool_or: bool, | ||
| 2635 | ) InnerError!*Inst { | ||
| 2636 | const tracy = trace(@src()); | ||
| 2637 | defer tracy.end(); | ||
| 2638 | |||
| 2639 | const bool_type = Type.initTag(.bool); | ||
| 2640 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 2641 | const uncasted_lhs = sema.resolveInst(bin_inst.lhs); | ||
| 2642 | const lhs = try sema.coerce(scope, bool_type, uncasted_lhs); | ||
| 2643 | const uncasted_rhs = sema.resolveInst(bin_inst.rhs); | ||
| 2644 | const rhs = try sema.coerce(scope, bool_type, uncasted_rhs); | ||
| 2645 | |||
| 2646 | if (lhs.value()) |lhs_val| { | ||
| 2647 | if (rhs.value()) |rhs_val| { | ||
| 2648 | if (is_bool_or) { | ||
| 2649 | return mod.constBool(block.arena, inst.base.src, lhs_val.toBool() or rhs_val.toBool()); | ||
| 2650 | } else { | ||
| 2651 | return mod.constBool(block.arena, inst.base.src, lhs_val.toBool() and rhs_val.toBool()); | ||
| 2652 | } | ||
| 2653 | } | ||
| 2654 | } | ||
| 2655 | try sema.requireRuntimeBlock(block, inst.base.src); | ||
| 2656 | const tag: ir.Inst.Tag = if (is_bool_or) .bool_or else .bool_and; | ||
| 2657 | return mod.addBinOp(b, inst.base.src, bool_type, tag, lhs, rhs); | ||
| 2658 | } | ||
| 2659 | |||
| 2660 | fn zirIsNull( | ||
| 2661 | sema: *Sema, | ||
| 2662 | block: *Scope.Block, | ||
| 2663 | inst: zir.Inst.Index, | ||
| 2664 | invert_logic: bool, | ||
| 2665 | ) InnerError!*Inst { | ||
| 2666 | const tracy = trace(@src()); | ||
| 2667 | defer tracy.end(); | ||
| 2668 | |||
| 2669 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 2670 | const src = inst_data.src(); | ||
| 2671 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 2672 | return sema.analyzeIsNull(block, src, operand, invert_logic); | ||
| 2673 | } | ||
| 2674 | |||
| 2675 | fn zirIsNullPtr( | ||
| 2676 | sema: *Sema, | ||
| 2677 | block: *Scope.Block, | ||
| 2678 | inst: zir.Inst.Index, | ||
| 2679 | invert_logic: bool, | ||
| 2680 | ) InnerError!*Inst { | ||
| 2681 | const tracy = trace(@src()); | ||
| 2682 | defer tracy.end(); | ||
| 2683 | |||
| 2684 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 2685 | const src = inst_data.src(); | ||
| 2686 | const ptr = sema.resolveInst(block, inst_data.operand); | ||
| 2687 | const loaded = try sema.analyzeDeref(block, src, ptr, src); | ||
| 2688 | return sema.analyzeIsNull(block, src, loaded, invert_logic); | ||
| 2689 | } | ||
| 2690 | |||
| 2691 | fn zirIsErr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2692 | const tracy = trace(@src()); | ||
| 2693 | defer tracy.end(); | ||
| 2694 | |||
| 2695 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 2696 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 2697 | return mod.analyzeIsErr(scope, inst_data.src(), operand); | ||
| 2698 | } | ||
| 2699 | |||
| 2700 | fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2701 | const tracy = trace(@src()); | ||
| 2702 | defer tracy.end(); | ||
| 2703 | |||
| 2704 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 2705 | const src = inst_data.src(); | ||
| 2706 | const ptr = sema.resolveInst(block, inst_data.operand); | ||
| 2707 | const loaded = try sema.analyzeDeref(block, src, ptr, src); | ||
| 2708 | return mod.analyzeIsErr(scope, src, loaded); | ||
| 2709 | } | ||
| 2710 | |||
| 2711 | fn zirCondbr(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2712 | const tracy = trace(@src()); | ||
| 2713 | defer tracy.end(); | ||
| 2714 | |||
| 2715 | const uncasted_cond = sema.resolveInst(block, inst.positionals.condition); | ||
| 2716 | const cond = try sema.coerce(scope, Type.initTag(.bool), uncasted_cond); | ||
| 2717 | |||
| 2718 | if (try mod.resolveDefinedValue(scope, cond)) |cond_val| { | ||
| 2719 | const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body; | ||
| 2720 | try sema.body(parent_block, body.*); | ||
| 2721 | return mod.constNoReturn(scope, inst.base.src); | ||
| 2722 | } | ||
| 2723 | |||
| 2724 | var true_block: Scope.Block = .{ | ||
| 2725 | .parent = parent_block, | ||
| 2726 | .inst_table = parent_block.inst_table, | ||
| 2727 | .func = parent_block.func, | ||
| 2728 | .owner_decl = parent_block.owner_decl, | ||
| 2729 | .src_decl = parent_block.src_decl, | ||
| 2730 | .instructions = .{}, | ||
| 2731 | .arena = parent_block.arena, | ||
| 2732 | .inlining = parent_block.inlining, | ||
| 2733 | .is_comptime = parent_block.is_comptime, | ||
| 2734 | .branch_quota = parent_block.branch_quota, | ||
| 2735 | }; | ||
| 2736 | defer true_block.instructions.deinit(mod.gpa); | ||
| 2737 | try sema.body(&true_block, inst.positionals.then_body); | ||
| 2738 | |||
| 2739 | var false_block: Scope.Block = .{ | ||
| 2740 | .parent = parent_block, | ||
| 2741 | .inst_table = parent_block.inst_table, | ||
| 2742 | .func = parent_block.func, | ||
| 2743 | .owner_decl = parent_block.owner_decl, | ||
| 2744 | .src_decl = parent_block.src_decl, | ||
| 2745 | .instructions = .{}, | ||
| 2746 | .arena = parent_block.arena, | ||
| 2747 | .inlining = parent_block.inlining, | ||
| 2748 | .is_comptime = parent_block.is_comptime, | ||
| 2749 | .branch_quota = parent_block.branch_quota, | ||
| 2750 | }; | ||
| 2751 | defer false_block.instructions.deinit(mod.gpa); | ||
| 2752 | try sema.body(&false_block, inst.positionals.else_body); | ||
| 2753 | |||
| 2754 | const then_body: ir.Body = .{ .instructions = try block.arena.dupe(*Inst, true_block.instructions.items) }; | ||
| 2755 | const else_body: ir.Body = .{ .instructions = try block.arena.dupe(*Inst, false_block.instructions.items) }; | ||
| 2756 | return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body); | ||
| 2757 | } | ||
| 2758 | |||
| 2759 | fn zirUnreachable( | ||
| 2760 | sema: *Sema, | ||
| 2761 | block: *Scope.Block, | ||
| 2762 | zir_index: zir.Inst.Index, | ||
| 2763 | safety_check: bool, | ||
| 2764 | ) InnerError!*Inst { | ||
| 2765 | const tracy = trace(@src()); | ||
| 2766 | defer tracy.end(); | ||
| 2767 | |||
| 2768 | try sema.requireRuntimeBlock(block, zir_index.base.src); | ||
| 2769 | // TODO Add compile error for @optimizeFor occurring too late in a scope. | ||
| 2770 | if (safety_check and block.wantSafety()) { | ||
| 2771 | return mod.safetyPanic(b, zir_index.base.src, .unreach); | ||
| 2772 | } else { | ||
| 2773 | return block.addNoOp(zir_index.base.src, Type.initTag(.noreturn), .unreach); | ||
| 2774 | } | ||
| 2775 | } | ||
| 2776 | |||
| 2777 | fn zirRetTok(sema: *Sema, block: *Scope.Block, zir_inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2778 | @compileError("TODO"); | ||
| 2779 | } | ||
| 2780 | |||
| 2781 | fn zirRetNode(sema: *Sema, block: *Scope.Block, zir_inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2782 | @compileError("TODO"); | ||
| 2783 | } | ||
| 2784 | |||
| 2785 | fn floatOpAllowed(tag: zir.Inst.Tag) bool { | ||
| 2786 | // extend this swich as additional operators are implemented | ||
| 2787 | return switch (tag) { | ||
| 2788 | .add, .sub => true, | ||
| 2789 | else => false, | ||
| 2790 | }; | ||
| 2791 | } | ||
| 2792 | |||
| 2793 | fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2794 | const tracy = trace(@src()); | ||
| 2795 | defer tracy.end(); | ||
| 2796 | |||
| 2797 | const inst_data = sema.code.instructions.items(.data)[inst].ptr_type_simple; | ||
| 2798 | const elem_type = try sema.resolveType(block, .unneeded, inst_data.elem_type); | ||
| 2799 | const ty = try sema.mod.ptrType( | ||
| 2800 | block.arena, | ||
| 2801 | elem_type, | ||
| 2802 | null, | ||
| 2803 | 0, | ||
| 2804 | 0, | ||
| 2805 | 0, | ||
| 2806 | inst_data.is_mutable, | ||
| 2807 | inst_data.is_allowzero, | ||
| 2808 | inst_data.is_volatile, | ||
| 2809 | inst_data.size, | ||
| 2810 | ); | ||
| 2811 | return sema.mod.constType(block.arena, .unneeded, ty); | ||
| 2812 | } | ||
| 2813 | |||
| 2814 | fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2815 | const tracy = trace(@src()); | ||
| 2816 | defer tracy.end(); | ||
| 2817 | |||
| 2818 | const inst_data = sema.code.instructions.items(.data)[inst].ptr_type; | ||
| 2819 | const extra = sema.code.extraData(zir.Inst.PtrType, inst_data.payload_index); | ||
| 2820 | |||
| 2821 | var extra_i = extra.end; | ||
| 2822 | |||
| 2823 | const sentinel = if (inst_data.flags.has_sentinel) blk: { | ||
| 2824 | const ref = sema.code.extra[extra_i]; | ||
| 2825 | extra_i += 1; | ||
| 2826 | break :blk (try sema.resolveInstConst(block, .unneeded, ref)).val; | ||
| 2827 | } else null; | ||
| 2828 | |||
| 2829 | const abi_align = if (inst_data.flags.has_align) blk: { | ||
| 2830 | const ref = sema.code.extra[extra_i]; | ||
| 2831 | extra_i += 1; | ||
| 2832 | break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u32); | ||
| 2833 | } else 0; | ||
| 2834 | |||
| 2835 | const bit_start = if (inst_data.flags.has_bit_start) blk: { | ||
| 2836 | const ref = sema.code.extra[extra_i]; | ||
| 2837 | extra_i += 1; | ||
| 2838 | break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u16); | ||
| 2839 | } else 0; | ||
| 2840 | |||
| 2841 | const bit_end = if (inst_data.flags.has_bit_end) blk: { | ||
| 2842 | const ref = sema.code.extra[extra_i]; | ||
| 2843 | extra_i += 1; | ||
| 2844 | break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u16); | ||
| 2845 | } else 0; | ||
| 2846 | |||
| 2847 | if (bit_end != 0 and bit_offset >= bit_end * 8) | ||
| 2848 | return sema.mod.fail(&block.base, inst.base.src, "bit offset starts after end of host integer", .{}); | ||
| 2849 | |||
| 2850 | const elem_type = try sema.resolveType(block, extra.data.elem_type); | ||
| 2851 | |||
| 2852 | const ty = try mod.ptrType( | ||
| 2853 | scope, | ||
| 2854 | elem_type, | ||
| 2855 | sentinel, | ||
| 2856 | abi_align, | ||
| 2857 | bit_start, | ||
| 2858 | bit_end, | ||
| 2859 | inst_data.flags.is_mutable, | ||
| 2860 | inst_data.flags.is_allowzero, | ||
| 2861 | inst_data.flags.is_volatile, | ||
| 2862 | inst_data.size, | ||
| 2863 | ); | ||
| 2864 | return sema.mod.constType(block.arena, .unneeded, ty); | ||
| 2865 | } | ||
| 2866 | |||
| 2867 | fn requireFunctionBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { | ||
| 2868 | if (sema.func == null) { | ||
| 2869 | return sema.mod.fail(&block.base, src, "instruction illegal outside function body", .{}); | ||
| 2870 | } | ||
| 2871 | } | ||
| 2872 | |||
| 2873 | fn requireRuntimeBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { | ||
| 2874 | try sema.requireFunctionBlock(scope, src); | ||
| 2875 | if (block.is_comptime) { | ||
| 2876 | return sema.mod.fail(&block.base, src, "unable to resolve comptime value", .{}); | ||
| 2877 | } | ||
| 2878 | } | ||
| 2879 | |||
| 2880 | fn validateVarType(sema: *Module, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void { | ||
| 2881 | if (!ty.isValidVarType(false)) { | ||
| 2882 | return mod.fail(&block.base, src, "variable of type '{}' must be const or comptime", .{ty}); | ||
| 2883 | } | ||
| 2884 | } | ||
| 2885 | |||
| 2886 | pub const PanicId = enum { | ||
| 2887 | unreach, | ||
| 2888 | unwrap_null, | ||
| 2889 | unwrap_errunion, | ||
| 2890 | }; | ||
| 2891 | |||
| 2892 | fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void { | ||
| 2893 | const block_inst = try parent_block.arena.create(Inst.Block); | ||
| 2894 | block_inst.* = .{ | ||
| 2895 | .base = .{ | ||
| 2896 | .tag = Inst.Block.base_tag, | ||
| 2897 | .ty = Type.initTag(.void), | ||
| 2898 | .src = ok.src, | ||
| 2899 | }, | ||
| 2900 | .body = .{ | ||
| 2901 | .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the condbr. | ||
| 2902 | }, | ||
| 2903 | }; | ||
| 2904 | |||
| 2905 | const ok_body: ir.Body = .{ | ||
| 2906 | .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the br_void. | ||
| 2907 | }; | ||
| 2908 | const br_void = try parent_block.arena.create(Inst.BrVoid); | ||
| 2909 | br_void.* = .{ | ||
| 2910 | .base = .{ | ||
| 2911 | .tag = .br_void, | ||
| 2912 | .ty = Type.initTag(.noreturn), | ||
| 2913 | .src = ok.src, | ||
| 2914 | }, | ||
| 2915 | .block = block_inst, | ||
| 2916 | }; | ||
| 2917 | ok_body.instructions[0] = &br_void.base; | ||
| 2918 | |||
| 2919 | var fail_block: Scope.Block = .{ | ||
| 2920 | .parent = parent_block, | ||
| 2921 | .inst_map = parent_block.inst_map, | ||
| 2922 | .func = parent_block.func, | ||
| 2923 | .owner_decl = parent_block.owner_decl, | ||
| 2924 | .src_decl = parent_block.src_decl, | ||
| 2925 | .instructions = .{}, | ||
| 2926 | .arena = parent_block.arena, | ||
| 2927 | .inlining = parent_block.inlining, | ||
| 2928 | .is_comptime = parent_block.is_comptime, | ||
| 2929 | .branch_quota = parent_block.branch_quota, | ||
| 2930 | }; | ||
| 2931 | |||
| 2932 | defer fail_block.instructions.deinit(mod.gpa); | ||
| 2933 | |||
| 2934 | _ = try mod.safetyPanic(&fail_block, ok.src, panic_id); | ||
| 2935 | |||
| 2936 | const fail_body: ir.Body = .{ .instructions = try parent_block.arena.dupe(*Inst, fail_block.instructions.items) }; | ||
| 2937 | |||
| 2938 | const condbr = try parent_block.arena.create(Inst.CondBr); | ||
| 2939 | condbr.* = .{ | ||
| 2940 | .base = .{ | ||
| 2941 | .tag = .condbr, | ||
| 2942 | .ty = Type.initTag(.noreturn), | ||
| 2943 | .src = ok.src, | ||
| 2944 | }, | ||
| 2945 | .condition = ok, | ||
| 2946 | .then_body = ok_body, | ||
| 2947 | .else_body = fail_body, | ||
| 2948 | }; | ||
| 2949 | block_inst.body.instructions[0] = &condbr.base; | ||
| 2950 | |||
| 2951 | try parent_block.instructions.append(mod.gpa, &block_inst.base); | ||
| 2952 | } | ||
| 2953 | |||
| 2954 | fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId) !*Inst { | ||
| 2955 | // TODO Once we have a panic function to call, call it here instead of breakpoint. | ||
| 2956 | _ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint); | ||
| 2957 | return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach); | ||
| 2958 | } | ||
| 2959 | |||
| 2960 | fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { | ||
| 2961 | const shared = block.inlining.?.shared; | ||
| 2962 | shared.branch_count += 1; | ||
| 2963 | if (shared.branch_count > block.branch_quota.*) { | ||
| 2964 | // TODO show the "called from here" stack | ||
| 2965 | return mod.fail(&block.base, src, "evaluation exceeded {d} backwards branches", .{ | ||
| 2966 | block.branch_quota.*, | ||
| 2967 | }); | ||
| 2968 | } | ||
| 2969 | } | ||
| 2970 | |||
| 2971 | fn namedFieldPtr( | ||
| 2972 | sema: *Sema, | ||
| 2973 | block: *Scope.Block, | ||
| 2974 | src: LazySrcLoc, | ||
| 2975 | object_ptr: *Inst, | ||
| 2976 | field_name: []const u8, | ||
| 2977 | field_name_src: LazySrcLoc, | ||
| 2978 | ) InnerError!*Inst { | ||
| 2979 | const elem_ty = switch (object_ptr.ty.zigTypeTag()) { | ||
| 2980 | .Pointer => object_ptr.ty.elemType(), | ||
| 2981 | else => return sema.mod.fail(&block.base, object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}), | ||
| 2982 | }; | ||
| 2983 | switch (elem_ty.zigTypeTag()) { | ||
| 2984 | .Array => { | ||
| 2985 | if (mem.eql(u8, field_name, "len")) { | ||
| 2986 | return mod.constInst(scope, src, .{ | ||
| 2987 | .ty = Type.initTag(.single_const_pointer_to_comptime_int), | ||
| 2988 | .val = try Value.Tag.ref_val.create( | ||
| 2989 | scope.arena(), | ||
| 2990 | try Value.Tag.int_u64.create(scope.arena(), elem_ty.arrayLen()), | ||
| 2991 | ), | ||
| 2992 | }); | ||
| 2993 | } else { | ||
| 2994 | return mod.fail( | ||
| 2995 | scope, | ||
| 2996 | field_name_src, | ||
| 2997 | "no member named '{s}' in '{}'", | ||
| 2998 | .{ field_name, elem_ty }, | ||
| 2999 | ); | ||
| 3000 | } | ||
| 3001 | }, | ||
| 3002 | .Pointer => { | ||
| 3003 | const ptr_child = elem_ty.elemType(); | ||
| 3004 | switch (ptr_child.zigTypeTag()) { | ||
| 3005 | .Array => { | ||
| 3006 | if (mem.eql(u8, field_name, "len")) { | ||
| 3007 | return mod.constInst(scope, src, .{ | ||
| 3008 | .ty = Type.initTag(.single_const_pointer_to_comptime_int), | ||
| 3009 | .val = try Value.Tag.ref_val.create( | ||
| 3010 | scope.arena(), | ||
| 3011 | try Value.Tag.int_u64.create(scope.arena(), ptr_child.arrayLen()), | ||
| 3012 | ), | ||
| 3013 | }); | ||
| 3014 | } else { | ||
| 3015 | return mod.fail( | ||
| 3016 | scope, | ||
| 3017 | field_name_src, | ||
| 3018 | "no member named '{s}' in '{}'", | ||
| 3019 | .{ field_name, elem_ty }, | ||
| 3020 | ); | ||
| 3021 | } | ||
| 3022 | }, | ||
| 3023 | else => {}, | ||
| 3024 | } | ||
| 3025 | }, | ||
| 3026 | .Type => { | ||
| 3027 | _ = try sema.resolveConstValue(scope, object_ptr.src, object_ptr); | ||
| 3028 | const result = try sema.analyzeDeref(block, src, object_ptr, object_ptr.src); | ||
| 3029 | const val = result.value().?; | ||
| 3030 | const child_type = try val.toType(scope.arena()); | ||
| 3031 | switch (child_type.zigTypeTag()) { | ||
| 3032 | .ErrorSet => { | ||
| 3033 | var name: []const u8 = undefined; | ||
| 3034 | // TODO resolve inferred error sets | ||
| 3035 | if (val.castTag(.error_set)) |payload| | ||
| 3036 | name = (payload.data.fields.getEntry(field_name) orelse return sema.mod.fail(&block.base, src, "no error named '{s}' in '{}'", .{ field_name, child_type })).key | ||
| 3037 | else | ||
| 3038 | name = (try mod.getErrorValue(field_name)).key; | ||
| 3039 | |||
| 3040 | const result_type = if (child_type.tag() == .anyerror) | ||
| 3041 | try Type.Tag.error_set_single.create(scope.arena(), name) | ||
| 3042 | else | ||
| 3043 | child_type; | ||
| 3044 | |||
| 3045 | return mod.constInst(scope, src, .{ | ||
| 3046 | .ty = try mod.simplePtrType(scope.arena(), result_type, false, .One), | ||
| 3047 | .val = try Value.Tag.ref_val.create( | ||
| 3048 | scope.arena(), | ||
| 3049 | try Value.Tag.@"error".create(scope.arena(), .{ | ||
| 3050 | .name = name, | ||
| 3051 | }), | ||
| 3052 | ), | ||
| 3053 | }); | ||
| 3054 | }, | ||
| 3055 | .Struct => { | ||
| 3056 | const container_scope = child_type.getContainerScope(); | ||
| 3057 | if (mod.lookupDeclName(&container_scope.base, field_name)) |decl| { | ||
| 3058 | // TODO if !decl.is_pub and inDifferentFiles() "{} is private" | ||
| 3059 | return sema.analyzeDeclRef(block, src, decl); | ||
| 3060 | } | ||
| 3061 | |||
| 3062 | if (container_scope.file_scope == mod.root_scope) { | ||
| 3063 | return sema.mod.fail(&block.base, src, "root source file has no member called '{s}'", .{field_name}); | ||
| 3064 | } else { | ||
| 3065 | return sema.mod.fail(&block.base, src, "container '{}' has no member called '{s}'", .{ child_type, field_name }); | ||
| 3066 | } | ||
| 3067 | }, | ||
| 3068 | else => return sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{child_type}), | ||
| 3069 | } | ||
| 3070 | }, | ||
| 3071 | else => {}, | ||
| 3072 | } | ||
| 3073 | return sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{elem_ty}); | ||
| 3074 | } | ||
| 3075 | |||
| 3076 | fn elemPtr( | ||
| 3077 | sema: *Sema, | ||
| 3078 | block: *Scope.Block, | ||
| 3079 | src: LazySrcLoc, | ||
| 3080 | array_ptr: *Inst, | ||
| 3081 | elem_index: *Inst, | ||
| 3082 | elem_index_src: LazySrcLoc, | ||
| 3083 | ) InnerError!*Inst { | ||
| 3084 | const elem_ty = switch (array_ptr.ty.zigTypeTag()) { | ||
| 3085 | .Pointer => array_ptr.ty.elemType(), | ||
| 3086 | else => return sema.mod.fail(&block.base, array_ptr.src, "expected pointer, found '{}'", .{array_ptr.ty}), | ||
| 3087 | }; | ||
| 3088 | if (!elem_ty.isIndexable()) { | ||
| 3089 | return sema.mod.fail(&block.base, src, "array access of non-array type '{}'", .{elem_ty}); | ||
| 3090 | } | ||
| 3091 | |||
| 3092 | if (elem_ty.isSinglePointer() and elem_ty.elemType().zigTypeTag() == .Array) { | ||
| 3093 | // we have to deref the ptr operand to get the actual array pointer | ||
| 3094 | const array_ptr_deref = try sema.analyzeDeref(block, src, array_ptr, array_ptr.src); | ||
| 3095 | if (array_ptr_deref.value()) |array_ptr_val| { | ||
| 3096 | if (elem_index.value()) |index_val| { | ||
| 3097 | // Both array pointer and index are compile-time known. | ||
| 3098 | const index_u64 = index_val.toUnsignedInt(); | ||
| 3099 | // @intCast here because it would have been impossible to construct a value that | ||
| 3100 | // required a larger index. | ||
| 3101 | const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64)); | ||
| 3102 | const pointee_type = elem_ty.elemType().elemType(); | ||
| 3103 | |||
| 3104 | return mod.constInst(scope, src, .{ | ||
| 3105 | .ty = try Type.Tag.single_const_pointer.create(scope.arena(), pointee_type), | ||
| 3106 | .val = elem_ptr, | ||
| 3107 | }); | ||
| 3108 | } | ||
| 3109 | } | ||
| 3110 | } | ||
| 3111 | |||
| 3112 | return sema.mod.fail(&block.base, src, "TODO implement more analyze elemptr", .{}); | ||
| 3113 | } | ||
| 3114 | |||
| 3115 | fn coerce(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) InnerError!*Inst { | ||
| 3116 | if (dest_type.tag() == .var_args_param) { | ||
| 3117 | return sema.coerceVarArgParam(scope, inst); | ||
| 3118 | } | ||
| 3119 | // If the types are the same, we can return the operand. | ||
| 3120 | if (dest_type.eql(inst.ty)) | ||
| 3121 | return inst; | ||
| 3122 | |||
| 3123 | const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty); | ||
| 3124 | if (in_memory_result == .ok) { | ||
| 3125 | return sema.bitcast(scope, dest_type, inst); | ||
| 3126 | } | ||
| 3127 | |||
| 3128 | // undefined to anything | ||
| 3129 | if (inst.value()) |val| { | ||
| 3130 | if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) { | ||
| 3131 | return mod.constInst(scope.arena(), inst.src, .{ .ty = dest_type, .val = val }); | ||
| 3132 | } | ||
| 3133 | } | ||
| 3134 | assert(inst.ty.zigTypeTag() != .Undefined); | ||
| 3135 | |||
| 3136 | // null to ?T | ||
| 3137 | if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) { | ||
| 3138 | return mod.constInst(scope.arena(), inst.src, .{ .ty = dest_type, .val = Value.initTag(.null_value) }); | ||
| 3139 | } | ||
| 3140 | |||
| 3141 | // T to ?T | ||
| 3142 | if (dest_type.zigTypeTag() == .Optional) { | ||
| 3143 | var buf: Type.Payload.ElemType = undefined; | ||
| 3144 | const child_type = dest_type.optionalChild(&buf); | ||
| 3145 | if (child_type.eql(inst.ty)) { | ||
| 3146 | return mod.wrapOptional(scope, dest_type, inst); | ||
| 3147 | } else if (try sema.coerceNum(scope, child_type, inst)) |some| { | ||
| 3148 | return mod.wrapOptional(scope, dest_type, some); | ||
| 3149 | } | ||
| 3150 | } | ||
| 3151 | |||
| 3152 | // T to E!T or E to E!T | ||
| 3153 | if (dest_type.tag() == .error_union) { | ||
| 3154 | return try mod.wrapErrorUnion(scope, dest_type, inst); | ||
| 3155 | } | ||
| 3156 | |||
| 3157 | // Coercions where the source is a single pointer to an array. | ||
| 3158 | src_array_ptr: { | ||
| 3159 | if (!inst.ty.isSinglePointer()) break :src_array_ptr; | ||
| 3160 | const array_type = inst.ty.elemType(); | ||
| 3161 | if (array_type.zigTypeTag() != .Array) break :src_array_ptr; | ||
| 3162 | const array_elem_type = array_type.elemType(); | ||
| 3163 | if (inst.ty.isConstPtr() and !dest_type.isConstPtr()) break :src_array_ptr; | ||
| 3164 | if (inst.ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr; | ||
| 3165 | |||
| 3166 | const dst_elem_type = dest_type.elemType(); | ||
| 3167 | switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) { | ||
| 3168 | .ok => {}, | ||
| 3169 | .no_match => break :src_array_ptr, | ||
| 3170 | } | ||
| 3171 | |||
| 3172 | switch (dest_type.ptrSize()) { | ||
| 3173 | .Slice => { | ||
| 3174 | // *[N]T to []T | ||
| 3175 | return sema.coerceArrayPtrToSlice(scope, dest_type, inst); | ||
| 3176 | }, | ||
| 3177 | .C => { | ||
| 3178 | // *[N]T to [*c]T | ||
| 3179 | return sema.coerceArrayPtrToMany(scope, dest_type, inst); | ||
| 3180 | }, | ||
| 3181 | .Many => { | ||
| 3182 | // *[N]T to [*]T | ||
| 3183 | // *[N:s]T to [*:s]T | ||
| 3184 | const src_sentinel = array_type.sentinel(); | ||
| 3185 | const dst_sentinel = dest_type.sentinel(); | ||
| 3186 | if (src_sentinel == null and dst_sentinel == null) | ||
| 3187 | return sema.coerceArrayPtrToMany(scope, dest_type, inst); | ||
| 3188 | |||
| 3189 | if (src_sentinel) |src_s| { | ||
| 3190 | if (dst_sentinel) |dst_s| { | ||
| 3191 | if (src_s.eql(dst_s)) { | ||
| 3192 | return sema.coerceArrayPtrToMany(scope, dest_type, inst); | ||
| 3193 | } | ||
| 3194 | } | ||
| 3195 | } | ||
| 3196 | }, | ||
| 3197 | .One => {}, | ||
| 3198 | } | ||
| 3199 | } | ||
| 3200 | |||
| 3201 | // comptime known number to other number | ||
| 3202 | if (try sema.coerceNum(scope, dest_type, inst)) |some| | ||
| 3203 | return some; | ||
| 3204 | |||
| 3205 | // integer widening | ||
| 3206 | if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) { | ||
| 3207 | assert(inst.value() == null); // handled above | ||
| 3208 | |||
| 3209 | const src_info = inst.ty.intInfo(mod.getTarget()); | ||
| 3210 | const dst_info = dest_type.intInfo(mod.getTarget()); | ||
| 3211 | if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or | ||
| 3212 | // small enough unsigned ints can get casted to large enough signed ints | ||
| 3213 | (src_info.signedness == .signed and dst_info.signedness == .unsigned and dst_info.bits > src_info.bits)) | ||
| 3214 | { | ||
| 3215 | try sema.requireRuntimeBlock(block, inst.src); | ||
| 3216 | return mod.addUnOp(b, inst.src, dest_type, .intcast, inst); | ||
| 3217 | } | ||
| 3218 | } | ||
| 3219 | |||
| 3220 | // float widening | ||
| 3221 | if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) { | ||
| 3222 | assert(inst.value() == null); // handled above | ||
| 3223 | |||
| 3224 | const src_bits = inst.ty.floatBits(mod.getTarget()); | ||
| 3225 | const dst_bits = dest_type.floatBits(mod.getTarget()); | ||
| 3226 | if (dst_bits >= src_bits) { | ||
| 3227 | try sema.requireRuntimeBlock(block, inst.src); | ||
| 3228 | return mod.addUnOp(b, inst.src, dest_type, .floatcast, inst); | ||
| 3229 | } | ||
| 3230 | } | ||
| 3231 | |||
| 3232 | return sema.mod.fail(&block.base, inst.src, "expected {}, found {}", .{ dest_type, inst.ty }); | ||
| 3233 | } | ||
| 3234 | |||
| 3235 | const InMemoryCoercionResult = enum { | ||
| 3236 | ok, | ||
| 3237 | no_match, | ||
| 3238 | }; | ||
| 3239 | |||
| 3240 | fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult { | ||
| 3241 | if (dest_type.eql(src_type)) | ||
| 3242 | return .ok; | ||
| 3243 | |||
| 3244 | // TODO: implement more of this function | ||
| 3245 | |||
| 3246 | return .no_match; | ||
| 3247 | } | ||
| 3248 | |||
| 3249 | fn coerceNum(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) InnerError!?*Inst { | ||
| 3250 | const val = inst.value() orelse return null; | ||
| 3251 | const src_zig_tag = inst.ty.zigTypeTag(); | ||
| 3252 | const dst_zig_tag = dest_type.zigTypeTag(); | ||
| 3253 | |||
| 3254 | if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) { | ||
| 3255 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { | ||
| 3256 | if (val.floatHasFraction()) { | ||
| 3257 | return sema.mod.fail(&block.base, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty }); | ||
| 3258 | } | ||
| 3259 | return sema.mod.fail(&block.base, inst.src, "TODO float to int", .{}); | ||
| 3260 | } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { | ||
| 3261 | if (!val.intFitsInType(dest_type, mod.getTarget())) { | ||
| 3262 | return sema.mod.fail(&block.base, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val }); | ||
| 3263 | } | ||
| 3264 | return mod.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 3265 | } | ||
| 3266 | } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) { | ||
| 3267 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { | ||
| 3268 | const res = val.floatCast(scope.arena(), dest_type, mod.getTarget()) catch |err| switch (err) { | ||
| 3269 | error.Overflow => return mod.fail( | ||
| 3270 | scope, | ||
| 3271 | inst.src, | ||
| 3272 | "cast of value {} to type '{}' loses information", | ||
| 3273 | .{ val, dest_type }, | ||
| 3274 | ), | ||
| 3275 | error.OutOfMemory => return error.OutOfMemory, | ||
| 3276 | }; | ||
| 3277 | return mod.constInst(scope, inst.src, .{ .ty = dest_type, .val = res }); | ||
| 3278 | } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { | ||
| 3279 | return sema.mod.fail(&block.base, inst.src, "TODO int to float", .{}); | ||
| 3280 | } | ||
| 3281 | } | ||
| 3282 | return null; | ||
| 3283 | } | ||
| 3284 | |||
| 3285 | fn coerceVarArgParam(sema: *Sema, block: *Scope.Block, inst: *Inst) !*Inst { | ||
| 3286 | switch (inst.ty.zigTypeTag()) { | ||
| 3287 | .ComptimeInt, .ComptimeFloat => return sema.mod.fail(&block.base, inst.src, "integer and float literals in var args function must be casted", .{}), | ||
| 3288 | else => {}, | ||
| 3289 | } | ||
| 3290 | // TODO implement more of this function. | ||
| 3291 | return inst; | ||
| 3292 | } | ||
| 3293 | |||
| 3294 | fn storePtr(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ptr: *Inst, uncasted_value: *Inst) !*Inst { | ||
| 3295 | if (ptr.ty.isConstPtr()) | ||
| 3296 | return sema.mod.fail(&block.base, src, "cannot assign to constant", .{}); | ||
| 3297 | |||
| 3298 | const elem_ty = ptr.ty.elemType(); | ||
| 3299 | const value = try sema.coerce(scope, elem_ty, uncasted_value); | ||
| 3300 | if (elem_ty.onePossibleValue() != null) | ||
| 3301 | return sema.mod.constVoid(block.arena, .unneeded); | ||
| 3302 | |||
| 3303 | // TODO handle comptime pointer writes | ||
| 3304 | // TODO handle if the element type requires comptime | ||
| 3305 | |||
| 3306 | try sema.requireRuntimeBlock(block, src); | ||
| 3307 | return mod.addBinOp(b, src, Type.initTag(.void), .store, ptr, value); | ||
| 3308 | } | ||
| 3309 | |||
| 3310 | fn bitcast(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { | ||
| 3311 | if (inst.value()) |val| { | ||
| 3312 | // Keep the comptime Value representation; take the new type. | ||
| 3313 | return mod.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 3314 | } | ||
| 3315 | // TODO validate the type size and other compile errors | ||
| 3316 | try sema.requireRuntimeBlock(block, inst.src); | ||
| 3317 | return mod.addUnOp(b, inst.src, dest_type, .bitcast, inst); | ||
| 3318 | } | ||
| 3319 | |||
| 3320 | fn coerceArrayPtrToSlice(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { | ||
| 3321 | if (inst.value()) |val| { | ||
| 3322 | // The comptime Value representation is compatible with both types. | ||
| 3323 | return mod.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 3324 | } | ||
| 3325 | return sema.mod.fail(&block.base, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{}); | ||
| 3326 | } | ||
| 3327 | |||
| 3328 | fn coerceArrayPtrToMany(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { | ||
| 3329 | if (inst.value()) |val| { | ||
| 3330 | // The comptime Value representation is compatible with both types. | ||
| 3331 | return mod.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 3332 | } | ||
| 3333 | return sema.mod.fail(&block.base, inst.src, "TODO implement coerceArrayPtrToMany runtime instruction", .{}); | ||
| 3334 | } | ||
| 3335 | |||
| 3336 | fn analyzeDeclVal(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) InnerError!*Inst { | ||
| 3337 | const decl_ref = try sema.analyzeDeclRef(block, src, decl); | ||
| 3338 | return sema.analyzeDeref(block, src, decl_ref, src); | ||
| 3339 | } | ||
| 3340 | |||
| 3341 | fn analyzeDeclRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) InnerError!*Inst { | ||
| 3342 | const scope_decl = scope.ownerDecl().?; | ||
| 3343 | try mod.declareDeclDependency(scope_decl, decl); | ||
| 3344 | mod.ensureDeclAnalyzed(decl) catch |err| { | ||
| 3345 | if (scope.cast(Scope.Block)) |block| { | ||
| 3346 | if (block.func) |func| { | ||
| 3347 | func.state = .dependency_failure; | ||
| 3348 | } else { | ||
| 3349 | block.owner_decl.analysis = .dependency_failure; | ||
| 3350 | } | ||
| 3351 | } else { | ||
| 3352 | scope_decl.analysis = .dependency_failure; | ||
| 3353 | } | ||
| 3354 | return err; | ||
| 3355 | }; | ||
| 3356 | |||
| 3357 | const decl_tv = try decl.typedValue(); | ||
| 3358 | if (decl_tv.val.tag() == .variable) { | ||
| 3359 | return mod.analyzeVarRef(scope, src, decl_tv); | ||
| 3360 | } | ||
| 3361 | return mod.constInst(scope.arena(), src, .{ | ||
| 3362 | .ty = try mod.simplePtrType(scope.arena(), decl_tv.ty, false, .One), | ||
| 3363 | .val = try Value.Tag.decl_ref.create(scope.arena(), decl), | ||
| 3364 | }); | ||
| 3365 | } | ||
| 3366 | |||
| 3367 | fn analyzeVarRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, tv: TypedValue) InnerError!*Inst { | ||
| 3368 | const variable = tv.val.castTag(.variable).?.data; | ||
| 3369 | |||
| 3370 | const ty = try mod.simplePtrType(scope.arena(), tv.ty, variable.is_mutable, .One); | ||
| 3371 | if (!variable.is_mutable and !variable.is_extern) { | ||
| 3372 | return mod.constInst(scope.arena(), src, .{ | ||
| 3373 | .ty = ty, | ||
| 3374 | .val = try Value.Tag.ref_val.create(scope.arena(), variable.init), | ||
| 3375 | }); | ||
| 3376 | } | ||
| 3377 | |||
| 3378 | try sema.requireRuntimeBlock(block, src); | ||
| 3379 | const inst = try b.arena.create(Inst.VarPtr); | ||
| 3380 | inst.* = .{ | ||
| 3381 | .base = .{ | ||
| 3382 | .tag = .varptr, | ||
| 3383 | .ty = ty, | ||
| 3384 | .src = src, | ||
| 3385 | }, | ||
| 3386 | .variable = variable, | ||
| 3387 | }; | ||
| 3388 | try b.instructions.append(mod.gpa, &inst.base); | ||
| 3389 | return &inst.base; | ||
| 3390 | } | ||
| 3391 | |||
| 3392 | fn analyzeRef( | ||
| 3393 | sema: *Sema, | ||
| 3394 | block: *Scope.Block, | ||
| 3395 | src: LazySrcLoc, | ||
| 3396 | operand: *Inst, | ||
| 3397 | ) InnerError!*Inst { | ||
| 3398 | const ptr_type = try mod.simplePtrType(scope.arena(), operand.ty, false, .One); | ||
| 3399 | |||
| 3400 | if (operand.value()) |val| { | ||
| 3401 | return mod.constInst(scope.arena(), src, .{ | ||
| 3402 | .ty = ptr_type, | ||
| 3403 | .val = try Value.Tag.ref_val.create(scope.arena(), val), | ||
| 3404 | }); | ||
| 3405 | } | ||
| 3406 | |||
| 3407 | try sema.requireRuntimeBlock(block, src); | ||
| 3408 | return block.addUnOp(src, ptr_type, .ref, operand); | ||
| 3409 | } | ||
| 3410 | |||
| 3411 | fn analyzeDeref( | ||
| 3412 | sema: *Sema, | ||
| 3413 | block: *Scope.Block, | ||
| 3414 | src: LazySrcLoc, | ||
| 3415 | ptr: *Inst, | ||
| 3416 | ptr_src: LazySrcLoc, | ||
| 3417 | ) InnerError!*Inst { | ||
| 3418 | const elem_ty = switch (ptr.ty.zigTypeTag()) { | ||
| 3419 | .Pointer => ptr.ty.elemType(), | ||
| 3420 | else => return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr.ty}), | ||
| 3421 | }; | ||
| 3422 | if (ptr.value()) |val| { | ||
| 3423 | return mod.constInst(scope.arena(), src, .{ | ||
| 3424 | .ty = elem_ty, | ||
| 3425 | .val = try val.pointerDeref(scope.arena()), | ||
| 3426 | }); | ||
| 3427 | } | ||
| 3428 | |||
| 3429 | try sema.requireRuntimeBlock(block, src); | ||
| 3430 | return mod.addUnOp(b, src, elem_ty, .load, ptr); | ||
| 3431 | } | ||
| 3432 | |||
| 3433 | fn analyzeIsNull( | ||
| 3434 | sema: *Sema, | ||
| 3435 | block: *Scope.Block, | ||
| 3436 | src: LazySrcLoc, | ||
| 3437 | operand: *Inst, | ||
| 3438 | invert_logic: bool, | ||
| 3439 | ) InnerError!*Inst { | ||
| 3440 | if (operand.value()) |opt_val| { | ||
| 3441 | const is_null = opt_val.isNull(); | ||
| 3442 | const bool_value = if (invert_logic) !is_null else is_null; | ||
| 3443 | return mod.constBool(block.arena, src, bool_value); | ||
| 3444 | } | ||
| 3445 | try sema.requireRuntimeBlock(block, src); | ||
| 3446 | const inst_tag: Inst.Tag = if (invert_logic) .is_non_null else .is_null; | ||
| 3447 | return mod.addUnOp(b, src, Type.initTag(.bool), inst_tag, operand); | ||
| 3448 | } | ||
| 3449 | |||
| 3450 | fn analyzeIsErr(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, operand: *Inst) InnerError!*Inst { | ||
| 3451 | const ot = operand.ty.zigTypeTag(); | ||
| 3452 | if (ot != .ErrorSet and ot != .ErrorUnion) return mod.constBool(block.arena, src, false); | ||
| 3453 | if (ot == .ErrorSet) return mod.constBool(block.arena, src, true); | ||
| 3454 | assert(ot == .ErrorUnion); | ||
| 3455 | if (operand.value()) |err_union| { | ||
| 3456 | return mod.constBool(block.arena, src, err_union.getError() != null); | ||
| 3457 | } | ||
| 3458 | try sema.requireRuntimeBlock(block, src); | ||
| 3459 | return mod.addUnOp(b, src, Type.initTag(.bool), .is_err, operand); | ||
| 3460 | } | ||
| 3461 | |||
| 3462 | fn analyzeSlice( | ||
| 3463 | sema: *Sema, | ||
| 3464 | block: *Scope.Block, | ||
| 3465 | src: LazySrcLoc, | ||
| 3466 | array_ptr: *Inst, | ||
| 3467 | start: *Inst, | ||
| 3468 | end_opt: ?*Inst, | ||
| 3469 | sentinel_opt: ?*Inst, | ||
| 3470 | sentinel_src: LazySrcLoc, | ||
| 3471 | ) InnerError!*Inst { | ||
| 3472 | const ptr_child = switch (array_ptr.ty.zigTypeTag()) { | ||
| 3473 | .Pointer => array_ptr.ty.elemType(), | ||
| 3474 | else => return sema.mod.fail(&block.base, src, "expected pointer, found '{}'", .{array_ptr.ty}), | ||
| 3475 | }; | ||
| 3476 | |||
| 3477 | var array_type = ptr_child; | ||
| 3478 | const elem_type = switch (ptr_child.zigTypeTag()) { | ||
| 3479 | .Array => ptr_child.elemType(), | ||
| 3480 | .Pointer => blk: { | ||
| 3481 | if (ptr_child.isSinglePointer()) { | ||
| 3482 | if (ptr_child.elemType().zigTypeTag() == .Array) { | ||
| 3483 | array_type = ptr_child.elemType(); | ||
| 3484 | break :blk ptr_child.elemType().elemType(); | ||
| 3485 | } | ||
| 3486 | |||
| 3487 | return sema.mod.fail(&block.base, src, "slice of single-item pointer", .{}); | ||
| 3488 | } | ||
| 3489 | break :blk ptr_child.elemType(); | ||
| 3490 | }, | ||
| 3491 | else => return sema.mod.fail(&block.base, src, "slice of non-array type '{}'", .{ptr_child}), | ||
| 3492 | }; | ||
| 3493 | |||
| 3494 | const slice_sentinel = if (sentinel_opt) |sentinel| blk: { | ||
| 3495 | const casted = try sema.coerce(scope, elem_type, sentinel); | ||
| 3496 | break :blk try sema.resolveConstValue(block, sentinel_src, casted); | ||
| 3497 | } else null; | ||
| 3498 | |||
| 3499 | var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice; | ||
| 3500 | var return_elem_type = elem_type; | ||
| 3501 | if (end_opt) |end| { | ||
| 3502 | if (end.value()) |end_val| { | ||
| 3503 | if (start.value()) |start_val| { | ||
| 3504 | const start_u64 = start_val.toUnsignedInt(); | ||
| 3505 | const end_u64 = end_val.toUnsignedInt(); | ||
| 3506 | if (start_u64 > end_u64) { | ||
| 3507 | return sema.mod.fail(&block.base, src, "out of bounds slice", .{}); | ||
| 3508 | } | ||
| 3509 | |||
| 3510 | const len = end_u64 - start_u64; | ||
| 3511 | const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen()) | ||
| 3512 | array_type.sentinel() | ||
| 3513 | else | ||
| 3514 | slice_sentinel; | ||
| 3515 | return_elem_type = try mod.arrayType(scope, len, array_sentinel, elem_type); | ||
| 3516 | return_ptr_size = .One; | ||
| 3517 | } | ||
| 3518 | } | ||
| 3519 | } | ||
| 3520 | const return_type = try mod.ptrType( | ||
| 3521 | scope, | ||
| 3522 | return_elem_type, | ||
| 3523 | if (end_opt == null) slice_sentinel else null, | ||
| 3524 | 0, // TODO alignment | ||
| 3525 | 0, | ||
| 3526 | 0, | ||
| 3527 | !ptr_child.isConstPtr(), | ||
| 3528 | ptr_child.isAllowzeroPtr(), | ||
| 3529 | ptr_child.isVolatilePtr(), | ||
| 3530 | return_ptr_size, | ||
| 3531 | ); | ||
| 3532 | |||
| 3533 | return sema.mod.fail(&block.base, src, "TODO implement analysis of slice", .{}); | ||
| 3534 | } | ||
| 3535 | |||
| 3536 | fn analyzeImport(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, target_string: []const u8) !*Scope.File { | ||
| 3537 | const cur_pkg = scope.getFileScope().pkg; | ||
| 3538 | const cur_pkg_dir_path = cur_pkg.root_src_directory.path orelse "."; | ||
| 3539 | const found_pkg = cur_pkg.table.get(target_string); | ||
| 3540 | |||
| 3541 | const resolved_path = if (found_pkg) |pkg| | ||
| 3542 | try std.fs.path.resolve(mod.gpa, &[_][]const u8{ pkg.root_src_directory.path orelse ".", pkg.root_src_path }) | ||
| 3543 | else | ||
| 3544 | try std.fs.path.resolve(mod.gpa, &[_][]const u8{ cur_pkg_dir_path, target_string }); | ||
| 3545 | errdefer mod.gpa.free(resolved_path); | ||
| 3546 | |||
| 3547 | if (mod.import_table.get(resolved_path)) |some| { | ||
| 3548 | mod.gpa.free(resolved_path); | ||
| 3549 | return some; | ||
| 3550 | } | ||
| 3551 | |||
| 3552 | if (found_pkg == null) { | ||
| 3553 | const resolved_root_path = try std.fs.path.resolve(mod.gpa, &[_][]const u8{cur_pkg_dir_path}); | ||
| 3554 | defer mod.gpa.free(resolved_root_path); | ||
| 3555 | |||
| 3556 | if (!mem.startsWith(u8, resolved_path, resolved_root_path)) { | ||
| 3557 | return error.ImportOutsidePkgPath; | ||
| 3558 | } | ||
| 3559 | } | ||
| 3560 | |||
| 3561 | // TODO Scope.Container arena for ty and sub_file_path | ||
| 3562 | const file_scope = try mod.gpa.create(Scope.File); | ||
| 3563 | errdefer mod.gpa.destroy(file_scope); | ||
| 3564 | const struct_ty = try Type.Tag.empty_struct.create(mod.gpa, &file_scope.root_container); | ||
| 3565 | errdefer mod.gpa.destroy(struct_ty.castTag(.empty_struct).?); | ||
| 3566 | |||
| 3567 | file_scope.* = .{ | ||
| 3568 | .sub_file_path = resolved_path, | ||
| 3569 | .source = .{ .unloaded = {} }, | ||
| 3570 | .tree = undefined, | ||
| 3571 | .status = .never_loaded, | ||
| 3572 | .pkg = found_pkg orelse cur_pkg, | ||
| 3573 | .root_container = .{ | ||
| 3574 | .file_scope = file_scope, | ||
| 3575 | .decls = .{}, | ||
| 3576 | .ty = struct_ty, | ||
| 3577 | }, | ||
| 3578 | }; | ||
| 3579 | mod.analyzeContainer(&file_scope.root_container) catch |err| switch (err) { | ||
| 3580 | error.AnalysisFail => { | ||
| 3581 | assert(mod.comp.totalErrorCount() != 0); | ||
| 3582 | }, | ||
| 3583 | else => |e| return e, | ||
| 3584 | }; | ||
| 3585 | try mod.import_table.put(mod.gpa, file_scope.sub_file_path, file_scope); | ||
| 3586 | return file_scope; | ||
| 3587 | } | ||
| 3588 | |||
| 3589 | /// Asserts that lhs and rhs types are both numeric. | ||
| 3590 | fn cmpNumeric( | ||
| 3591 | sema: *Sema, | ||
| 3592 | block: *Scope.Block, | ||
| 3593 | src: LazySrcLoc, | ||
| 3594 | lhs: *Inst, | ||
| 3595 | rhs: *Inst, | ||
| 3596 | op: std.math.CompareOperator, | ||
| 3597 | ) InnerError!*Inst { | ||
| 3598 | assert(lhs.ty.isNumeric()); | ||
| 3599 | assert(rhs.ty.isNumeric()); | ||
| 3600 | |||
| 3601 | const lhs_ty_tag = lhs.ty.zigTypeTag(); | ||
| 3602 | const rhs_ty_tag = rhs.ty.zigTypeTag(); | ||
| 3603 | |||
| 3604 | if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) { | ||
| 3605 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { | ||
| 3606 | return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{ | ||
| 3607 | lhs.ty.arrayLen(), | ||
| 3608 | rhs.ty.arrayLen(), | ||
| 3609 | }); | ||
| 3610 | } | ||
| 3611 | return sema.mod.fail(&block.base, src, "TODO implement support for vectors in cmpNumeric", .{}); | ||
| 3612 | } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) { | ||
| 3613 | return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ | ||
| 3614 | lhs.ty, | ||
| 3615 | rhs.ty, | ||
| 3616 | }); | ||
| 3617 | } | ||
| 3618 | |||
| 3619 | if (lhs.value()) |lhs_val| { | ||
| 3620 | if (rhs.value()) |rhs_val| { | ||
| 3621 | return mod.constBool(block.arena, src, Value.compare(lhs_val, op, rhs_val)); | ||
| 3622 | } | ||
| 3623 | } | ||
| 3624 | |||
| 3625 | // TODO handle comparisons against lazy zero values | ||
| 3626 | // Some values can be compared against zero without being runtime known or without forcing | ||
| 3627 | // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to | ||
| 3628 | // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout | ||
| 3629 | // of this function if we don't need to. | ||
| 3630 | |||
| 3631 | // It must be a runtime comparison. | ||
| 3632 | try sema.requireRuntimeBlock(block, src); | ||
| 3633 | // For floats, emit a float comparison instruction. | ||
| 3634 | const lhs_is_float = switch (lhs_ty_tag) { | ||
| 3635 | .Float, .ComptimeFloat => true, | ||
| 3636 | else => false, | ||
| 3637 | }; | ||
| 3638 | const rhs_is_float = switch (rhs_ty_tag) { | ||
| 3639 | .Float, .ComptimeFloat => true, | ||
| 3640 | else => false, | ||
| 3641 | }; | ||
| 3642 | if (lhs_is_float and rhs_is_float) { | ||
| 3643 | // Implicit cast the smaller one to the larger one. | ||
| 3644 | const dest_type = x: { | ||
| 3645 | if (lhs_ty_tag == .ComptimeFloat) { | ||
| 3646 | break :x rhs.ty; | ||
| 3647 | } else if (rhs_ty_tag == .ComptimeFloat) { | ||
| 3648 | break :x lhs.ty; | ||
| 3649 | } | ||
| 3650 | if (lhs.ty.floatBits(mod.getTarget()) >= rhs.ty.floatBits(mod.getTarget())) { | ||
| 3651 | break :x lhs.ty; | ||
| 3652 | } else { | ||
| 3653 | break :x rhs.ty; | ||
| 3654 | } | ||
| 3655 | }; | ||
| 3656 | const casted_lhs = try sema.coerce(scope, dest_type, lhs); | ||
| 3657 | const casted_rhs = try sema.coerce(scope, dest_type, rhs); | ||
| 3658 | return mod.addBinOp(b, src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs); | ||
| 3659 | } | ||
| 3660 | // For mixed unsigned integer sizes, implicit cast both operands to the larger integer. | ||
| 3661 | // For mixed signed and unsigned integers, implicit cast both operands to a signed | ||
| 3662 | // integer with + 1 bit. | ||
| 3663 | // For mixed floats and integers, extract the integer part from the float, cast that to | ||
| 3664 | // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float, | ||
| 3665 | // add/subtract 1. | ||
| 3666 | const lhs_is_signed = if (lhs.value()) |lhs_val| | ||
| 3667 | lhs_val.compareWithZero(.lt) | ||
| 3668 | else | ||
| 3669 | (lhs.ty.isFloat() or lhs.ty.isSignedInt()); | ||
| 3670 | const rhs_is_signed = if (rhs.value()) |rhs_val| | ||
| 3671 | rhs_val.compareWithZero(.lt) | ||
| 3672 | else | ||
| 3673 | (rhs.ty.isFloat() or rhs.ty.isSignedInt()); | ||
| 3674 | const dest_int_is_signed = lhs_is_signed or rhs_is_signed; | ||
| 3675 | |||
| 3676 | var dest_float_type: ?Type = null; | ||
| 3677 | |||
| 3678 | var lhs_bits: usize = undefined; | ||
| 3679 | if (lhs.value()) |lhs_val| { | ||
| 3680 | if (lhs_val.isUndef()) | ||
| 3681 | return mod.constUndef(scope, src, Type.initTag(.bool)); | ||
| 3682 | const is_unsigned = if (lhs_is_float) x: { | ||
| 3683 | var bigint_space: Value.BigIntSpace = undefined; | ||
| 3684 | var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(mod.gpa); | ||
| 3685 | defer bigint.deinit(); | ||
| 3686 | const zcmp = lhs_val.orderAgainstZero(); | ||
| 3687 | if (lhs_val.floatHasFraction()) { | ||
| 3688 | switch (op) { | ||
| 3689 | .eq => return mod.constBool(block.arena, src, false), | ||
| 3690 | .neq => return mod.constBool(block.arena, src, true), | ||
| 3691 | else => {}, | ||
| 3692 | } | ||
| 3693 | if (zcmp == .lt) { | ||
| 3694 | try bigint.addScalar(bigint.toConst(), -1); | ||
| 3695 | } else { | ||
| 3696 | try bigint.addScalar(bigint.toConst(), 1); | ||
| 3697 | } | ||
| 3698 | } | ||
| 3699 | lhs_bits = bigint.toConst().bitCountTwosComp(); | ||
| 3700 | break :x (zcmp != .lt); | ||
| 3701 | } else x: { | ||
| 3702 | lhs_bits = lhs_val.intBitCountTwosComp(); | ||
| 3703 | break :x (lhs_val.orderAgainstZero() != .lt); | ||
| 3704 | }; | ||
| 3705 | lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed); | ||
| 3706 | } else if (lhs_is_float) { | ||
| 3707 | dest_float_type = lhs.ty; | ||
| 3708 | } else { | ||
| 3709 | const int_info = lhs.ty.intInfo(mod.getTarget()); | ||
| 3710 | lhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed); | ||
| 3711 | } | ||
| 3712 | |||
| 3713 | var rhs_bits: usize = undefined; | ||
| 3714 | if (rhs.value()) |rhs_val| { | ||
| 3715 | if (rhs_val.isUndef()) | ||
| 3716 | return mod.constUndef(scope, src, Type.initTag(.bool)); | ||
| 3717 | const is_unsigned = if (rhs_is_float) x: { | ||
| 3718 | var bigint_space: Value.BigIntSpace = undefined; | ||
| 3719 | var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(mod.gpa); | ||
| 3720 | defer bigint.deinit(); | ||
| 3721 | const zcmp = rhs_val.orderAgainstZero(); | ||
| 3722 | if (rhs_val.floatHasFraction()) { | ||
| 3723 | switch (op) { | ||
| 3724 | .eq => return mod.constBool(block.arena, src, false), | ||
| 3725 | .neq => return mod.constBool(block.arena, src, true), | ||
| 3726 | else => {}, | ||
| 3727 | } | ||
| 3728 | if (zcmp == .lt) { | ||
| 3729 | try bigint.addScalar(bigint.toConst(), -1); | ||
| 3730 | } else { | ||
| 3731 | try bigint.addScalar(bigint.toConst(), 1); | ||
| 3732 | } | ||
| 3733 | } | ||
| 3734 | rhs_bits = bigint.toConst().bitCountTwosComp(); | ||
| 3735 | break :x (zcmp != .lt); | ||
| 3736 | } else x: { | ||
| 3737 | rhs_bits = rhs_val.intBitCountTwosComp(); | ||
| 3738 | break :x (rhs_val.orderAgainstZero() != .lt); | ||
| 3739 | }; | ||
| 3740 | rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed); | ||
| 3741 | } else if (rhs_is_float) { | ||
| 3742 | dest_float_type = rhs.ty; | ||
| 3743 | } else { | ||
| 3744 | const int_info = rhs.ty.intInfo(mod.getTarget()); | ||
| 3745 | rhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed); | ||
| 3746 | } | ||
| 3747 | |||
| 3748 | const dest_type = if (dest_float_type) |ft| ft else blk: { | ||
| 3749 | const max_bits = std.math.max(lhs_bits, rhs_bits); | ||
| 3750 | const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) { | ||
| 3751 | error.Overflow => return sema.mod.fail(&block.base, src, "{d} exceeds maximum integer bit count", .{max_bits}), | ||
| 3752 | }; | ||
| 3753 | break :blk try mod.makeIntType(scope, dest_int_is_signed, casted_bits); | ||
| 3754 | }; | ||
| 3755 | const casted_lhs = try sema.coerce(scope, dest_type, lhs); | ||
| 3756 | const casted_rhs = try sema.coerce(scope, dest_type, rhs); | ||
| 3757 | |||
| 3758 | return mod.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs); | ||
| 3759 | } | ||
| 3760 | |||
| 3761 | fn wrapOptional(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { | ||
| 3762 | if (inst.value()) |val| { | ||
| 3763 | return mod.constInst(scope.arena(), inst.src, .{ .ty = dest_type, .val = val }); | ||
| 3764 | } | ||
| 3765 | |||
| 3766 | try sema.requireRuntimeBlock(block, inst.src); | ||
| 3767 | return mod.addUnOp(b, inst.src, dest_type, .wrap_optional, inst); | ||
| 3768 | } | ||
| 3769 | |||
| 3770 | fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { | ||
| 3771 | // TODO deal with inferred error sets | ||
| 3772 | const err_union = dest_type.castTag(.error_union).?; | ||
| 3773 | if (inst.value()) |val| { | ||
| 3774 | const to_wrap = if (inst.ty.zigTypeTag() != .ErrorSet) blk: { | ||
| 3775 | _ = try sema.coerce(scope, err_union.data.payload, inst); | ||
| 3776 | break :blk val; | ||
| 3777 | } else switch (err_union.data.error_set.tag()) { | ||
| 3778 | .anyerror => val, | ||
| 3779 | .error_set_single => blk: { | ||
| 3780 | const n = err_union.data.error_set.castTag(.error_set_single).?.data; | ||
| 3781 | if (!mem.eql(u8, val.castTag(.@"error").?.data.name, n)) | ||
| 3782 | return sema.mod.fail(&block.base, inst.src, "expected type '{}', found type '{}'", .{ err_union.data.error_set, inst.ty }); | ||
| 3783 | break :blk val; | ||
| 3784 | }, | ||
| 3785 | .error_set => blk: { | ||
| 3786 | const f = err_union.data.error_set.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields; | ||
| 3787 | if (f.get(val.castTag(.@"error").?.data.name) == null) | ||
| 3788 | return sema.mod.fail(&block.base, inst.src, "expected type '{}', found type '{}'", .{ err_union.data.error_set, inst.ty }); | ||
| 3789 | break :blk val; | ||
| 3790 | }, | ||
| 3791 | else => unreachable, | ||
| 3792 | }; | ||
| 3793 | |||
| 3794 | return mod.constInst(scope.arena(), inst.src, .{ | ||
| 3795 | .ty = dest_type, | ||
| 3796 | // creating a SubValue for the error_union payload | ||
| 3797 | .val = try Value.Tag.error_union.create( | ||
| 3798 | scope.arena(), | ||
| 3799 | to_wrap, | ||
| 3800 | ), | ||
| 3801 | }); | ||
| 3802 | } | ||
| 3803 | |||
| 3804 | try sema.requireRuntimeBlock(block, inst.src); | ||
| 3805 | |||
| 3806 | // we are coercing from E to E!T | ||
| 3807 | if (inst.ty.zigTypeTag() == .ErrorSet) { | ||
| 3808 | var coerced = try sema.coerce(scope, err_union.data.error_set, inst); | ||
| 3809 | return mod.addUnOp(b, inst.src, dest_type, .wrap_errunion_err, coerced); | ||
| 3810 | } else { | ||
| 3811 | var coerced = try sema.coerce(scope, err_union.data.payload, inst); | ||
| 3812 | return mod.addUnOp(b, inst.src, dest_type, .wrap_errunion_payload, coerced); | ||
| 3813 | } | ||
| 3814 | } | ||
| 3815 | |||
| 3816 | fn resolvePeerTypes(sema: *Sema, block: *Scope.Block, instructions: []*Inst) !Type { | ||
| 3817 | if (instructions.len == 0) | ||
| 3818 | return Type.initTag(.noreturn); | ||
| 3819 | |||
| 3820 | if (instructions.len == 1) | ||
| 3821 | return instructions[0].ty; | ||
| 3822 | |||
| 3823 | var chosen = instructions[0]; | ||
| 3824 | for (instructions[1..]) |candidate| { | ||
| 3825 | if (candidate.ty.eql(chosen.ty)) | ||
| 3826 | continue; | ||
| 3827 | if (candidate.ty.zigTypeTag() == .NoReturn) | ||
| 3828 | continue; | ||
| 3829 | if (chosen.ty.zigTypeTag() == .NoReturn) { | ||
| 3830 | chosen = candidate; | ||
| 3831 | continue; | ||
| 3832 | } | ||
| 3833 | if (candidate.ty.zigTypeTag() == .Undefined) | ||
| 3834 | continue; | ||
| 3835 | if (chosen.ty.zigTypeTag() == .Undefined) { | ||
| 3836 | chosen = candidate; | ||
| 3837 | continue; | ||
| 3838 | } | ||
| 3839 | if (chosen.ty.isInt() and | ||
| 3840 | candidate.ty.isInt() and | ||
| 3841 | chosen.ty.isSignedInt() == candidate.ty.isSignedInt()) | ||
| 3842 | { | ||
| 3843 | if (chosen.ty.intInfo(mod.getTarget()).bits < candidate.ty.intInfo(mod.getTarget()).bits) { | ||
| 3844 | chosen = candidate; | ||
| 3845 | } | ||
| 3846 | continue; | ||
| 3847 | } | ||
| 3848 | if (chosen.ty.isFloat() and candidate.ty.isFloat()) { | ||
| 3849 | if (chosen.ty.floatBits(mod.getTarget()) < candidate.ty.floatBits(mod.getTarget())) { | ||
| 3850 | chosen = candidate; | ||
| 3851 | } | ||
| 3852 | continue; | ||
| 3853 | } | ||
| 3854 | |||
| 3855 | if (chosen.ty.zigTypeTag() == .ComptimeInt and candidate.ty.isInt()) { | ||
| 3856 | chosen = candidate; | ||
| 3857 | continue; | ||
| 3858 | } | ||
| 3859 | |||
| 3860 | if (chosen.ty.isInt() and candidate.ty.zigTypeTag() == .ComptimeInt) { | ||
| 3861 | continue; | ||
| 3862 | } | ||
| 3863 | |||
| 3864 | // TODO error notes pointing out each type | ||
| 3865 | return sema.mod.fail(&block.base, candidate.src, "incompatible types: '{}' and '{}'", .{ chosen.ty, candidate.ty }); | ||
| 3866 | } | ||
| 3867 | |||
| 3868 | return chosen.ty; | ||
| 3869 | } | ||
src/zir_sema.zig deleted-3869| ... | @@ -1,3869 +0,0 @@ | ||
| 1 | //! Semantic analysis of ZIR instructions. | ||
| 2 | //! Shared to every Block. Stored on the stack. | ||
| 3 | //! State used for compiling a `zir.Code` into TZIR. | ||
| 4 | //! Transforms untyped ZIR instructions into semantically-analyzed TZIR instructions. | ||
| 5 | //! Does type checking, comptime control flow, and safety-check generation. | ||
| 6 | //! This is the the heart of the Zig compiler. | ||
| 7 | |||
| 8 | mod: *Module, | ||
| 9 | /// Same as `mod.gpa`. | ||
| 10 | gpa: *Allocator, | ||
| 11 | /// Points to the arena allocator of the Decl. | ||
| 12 | arena: *Allocator, | ||
| 13 | code: zir.Code, | ||
| 14 | /// Maps ZIR to TZIR. | ||
| 15 | inst_map: []*const Inst, | ||
| 16 | /// When analyzing an inline function call, owner_decl is the Decl of the caller | ||
| 17 | /// and `src_decl` of `Scope.Block` is the `Decl` of the callee. | ||
| 18 | /// This `Decl` owns the arena memory of this `Sema`. | ||
| 19 | owner_decl: *Decl, | ||
| 20 | func: ?*Module.Fn, | ||
| 21 | /// For now, TZIR requires arg instructions to be the first N instructions in the | ||
| 22 | /// TZIR code. We store references here for the purpose of `resolveInst`. | ||
| 23 | /// This can get reworked with TZIR memory layout changes, into simply: | ||
| 24 | /// > Denormalized data to make `resolveInst` faster. This is 0 if not inside a function, | ||
| 25 | /// > otherwise it is the number of parameters of the function. | ||
| 26 | /// > param_count: u32 | ||
| 27 | param_inst_list: []const *ir.Inst, | ||
| 28 | branch_quota: u32 = 1000, | ||
| 29 | /// This field is updated when a new source location becomes active, so that | ||
| 30 | /// instructions which do not have explicitly mapped source locations still have | ||
| 31 | /// access to the source location set by the previous instruction which did | ||
| 32 | /// contain a mapped source location. | ||
| 33 | src: LazySrcLoc = .{ .token_offset = 0 }, | ||
| 34 | |||
| 35 | const std = @import("std"); | ||
| 36 | const mem = std.mem; | ||
| 37 | const Allocator = std.mem.Allocator; | ||
| 38 | const assert = std.debug.assert; | ||
| 39 | const log = std.log.scoped(.sema); | ||
| 40 | |||
| 41 | const Sema = @This(); | ||
| 42 | const Value = @import("value.zig").Value; | ||
| 43 | const Type = @import("type.zig").Type; | ||
| 44 | const TypedValue = @import("TypedValue.zig"); | ||
| 45 | const ir = @import("ir.zig"); | ||
| 46 | const zir = @import("zir.zig"); | ||
| 47 | const Module = @import("Module.zig"); | ||
| 48 | const Inst = ir.Inst; | ||
| 49 | const Body = ir.Body; | ||
| 50 | const trace = @import("tracy.zig").trace; | ||
| 51 | const Scope = Module.Scope; | ||
| 52 | const InnerError = Module.InnerError; | ||
| 53 | const Decl = Module.Decl; | ||
| 54 | const LazySrcLoc = Module.LazySrcLoc; | ||
| 55 | |||
| 56 | // TODO when memory layout of TZIR is reworked, this can be simplified. | ||
| 57 | const const_tzir_inst_list = blk: { | ||
| 58 | var result: [zir.const_inst_list.len]ir.Inst.Const = undefined; | ||
| 59 | for (result) |*tzir_const, i| { | ||
| 60 | tzir_const.* = .{ | ||
| 61 | .base = .{ | ||
| 62 | .tag = .constant, | ||
| 63 | .ty = zir.const_inst_list[i].ty, | ||
| 64 | .src = 0, | ||
| 65 | }, | ||
| 66 | .val = zir.const_inst_list[i].val, | ||
| 67 | }; | ||
| 68 | } | ||
| 69 | break :blk result; | ||
| 70 | }; | ||
| 71 | |||
| 72 | pub fn root(sema: *Sema, root_block: *Scope.Block) !void { | ||
| 73 | const root_body = sema.code.extra[sema.code.root_start..][0..sema.code.root_len]; | ||
| 74 | return sema.body(root_block, root_body); | ||
| 75 | } | ||
| 76 | |||
| 77 | pub fn rootAsType( | ||
| 78 | sema: *Sema, | ||
| 79 | root_block: *Scope.Block, | ||
| 80 | zir_result_inst: zir.Inst.Index, | ||
| 81 | body: zir.Body, | ||
| 82 | ) !Type { | ||
| 83 | const root_body = sema.code.extra[sema.code.root_start..][0..sema.code.root_len]; | ||
| 84 | try sema.body(root_block, root_body); | ||
| 85 | |||
| 86 | const result_inst = sema.inst_map[zir_result_inst]; | ||
| 87 | // Source location is unneeded because resolveConstValue must have already | ||
| 88 | // been successfully called when coercing the value to a type, from the | ||
| 89 | // result location. | ||
| 90 | const val = try sema.resolveConstValue(root_block, .unneeded, result_inst); | ||
| 91 | return val.toType(root_block.arena); | ||
| 92 | } | ||
| 93 | |||
| 94 | pub fn body(sema: *Sema, block: *Scope.Block, body: []const zir.Inst.Index) !void { | ||
| 95 | const tracy = trace(@src()); | ||
| 96 | defer tracy.end(); | ||
| 97 | |||
| 98 | const map = block.sema.inst_map; | ||
| 99 | const tags = block.sema.code.instructions.items(.tag); | ||
| 100 | |||
| 101 | // TODO: As an optimization, look into making these switch prongs directly jump | ||
| 102 | // to the next one, rather than detouring through the loop condition. | ||
| 103 | // Also, look into leaving only the "noreturn" loop break condition, and removing | ||
| 104 | // the iteration based one. Better yet, have an extra entry in the tags array as a | ||
| 105 | // sentinel, so that exiting the loop is just another jump table prong. | ||
| 106 | // Related: https://github.com/ziglang/zig/issues/8220 | ||
| 107 | for (body) |zir_inst| { | ||
| 108 | map[zir_inst] = switch (tags[zir_inst]) { | ||
| 109 | .alloc => try sema.zirAlloc(block, zir_inst), | ||
| 110 | .alloc_mut => try sema.zirAllocMut(block, zir_inst), | ||
| 111 | .alloc_inferred => try sema.zirAllocInferred(block, zir_inst, Type.initTag(.inferred_alloc_const)), | ||
| 112 | .alloc_inferred_mut => try sema.zirAllocInferred(block, zir_inst, Type.initTag(.inferred_alloc_mut)), | ||
| 113 | .bitcast_ref => try sema.zirBitcastRef(block, zir_inst), | ||
| 114 | .bitcast_result_ptr => try sema.zirBitcastResultPtr(block, zir_inst), | ||
| 115 | .block => try sema.zirBlock(block, zir_inst, false), | ||
| 116 | .block_comptime => try sema.zirBlock(block, zir_inst, true), | ||
| 117 | .block_flat => try sema.zirBlockFlat(block, zir_inst, false), | ||
| 118 | .block_comptime_flat => try sema.zirBlockFlat(block, zir_inst, true), | ||
| 119 | .@"break" => try sema.zirBreak(block, zir_inst), | ||
| 120 | .break_void_tok => try sema.zirBreakVoidTok(block, zir_inst), | ||
| 121 | .breakpoint => try sema.zirBreakpoint(block, zir_inst), | ||
| 122 | .call => try sema.zirCall(block, zir_inst, .auto), | ||
| 123 | .call_async_kw => try sema.zirCall(block, zir_inst, .async_kw), | ||
| 124 | .call_no_async => try sema.zirCall(block, zir_inst, .no_async), | ||
| 125 | .call_compile_time => try sema.zirCall(block, zir_inst, .compile_time), | ||
| 126 | .call_none => try sema.zirCallNone(block, zir_inst), | ||
| 127 | .coerce_result_ptr => try sema.zirCoerceResultPtr(block, zir_inst), | ||
| 128 | .compile_error => try sema.zirCompileError(block, zir_inst), | ||
| 129 | .compile_log => try sema.zirCompileLog(block, zir_inst), | ||
| 130 | .@"const" => try sema.zirConst(block, zir_inst), | ||
| 131 | .dbg_stmt_node => try sema.zirDbgStmtNode(block, zir_inst), | ||
| 132 | .decl_ref => try sema.zirDeclRef(block, zir_inst), | ||
| 133 | .decl_val => try sema.zirDeclVal(block, zir_inst), | ||
| 134 | .ensure_result_used => try sema.zirEnsureResultUsed(block, zir_inst), | ||
| 135 | .ensure_result_non_error => try sema.zirEnsureResultNonError(block, zir_inst), | ||
| 136 | .indexable_ptr_len => try sema.zirIndexablePtrLen(block, zir_inst), | ||
| 137 | .ref => try sema.zirRef(block, zir_inst), | ||
| 138 | .resolve_inferred_alloc => try sema.zirResolveInferredAlloc(block, zir_inst), | ||
| 139 | .ret_ptr => try sema.zirRetPtr(block, zir_inst), | ||
| 140 | .ret_type => try sema.zirRetType(block, zir_inst), | ||
| 141 | .store_to_block_ptr => try sema.zirStoreToBlockPtr(block, zir_inst), | ||
| 142 | .store_to_inferred_ptr => try sema.zirStoreToInferredPtr(block, zir_inst), | ||
| 143 | .ptr_type_simple => try sema.zirPtrTypeSimple(block, zir_inst), | ||
| 144 | .ptr_type => try sema.zirPtrType(block, zir_inst), | ||
| 145 | .store => try sema.zirStore(block, zir_inst), | ||
| 146 | .set_eval_branch_quota => try sema.zirSetEvalBranchQuota(block, zir_inst), | ||
| 147 | .str => try sema.zirStr(block, zir_inst), | ||
| 148 | .int => try sema.zirInt(block, zir_inst), | ||
| 149 | .int_type => try sema.zirIntType(block, zir_inst), | ||
| 150 | .loop => try sema.zirLoop(block, zir_inst), | ||
| 151 | .param_type => try sema.zirParamType(block, zir_inst), | ||
| 152 | .ptrtoint => try sema.zirPtrtoint(block, zir_inst), | ||
| 153 | .field_ptr => try sema.zirFieldPtr(block, zir_inst), | ||
| 154 | .field_val => try sema.zirFieldVal(block, zir_inst), | ||
| 155 | .field_ptr_named => try sema.zirFieldPtrNamed(block, zir_inst), | ||
| 156 | .field_val_named => try sema.zirFieldValNamed(block, zir_inst), | ||
| 157 | .deref => try sema.zirDeref(block, zir_inst), | ||
| 158 | .as => try sema.zirAs(block, zir_inst), | ||
| 159 | .@"asm" => try sema.zirAsm(block, zir_inst, false), | ||
| 160 | .asm_volatile => try sema.zirAsm(block, zir_inst, true), | ||
| 161 | .unreachable_safe => try sema.zirUnreachable(block, zir_inst, true), | ||
| 162 | .unreachable_unsafe => try sema.zirUnreachable(block, zir_inst, false), | ||
| 163 | .ret_tok => try sema.zirRetTok(block, zir_inst), | ||
| 164 | .ret_node => try sema.zirRetNode(block, zir_inst), | ||
| 165 | .fn_type => try sema.zirFnType(block, zir_inst), | ||
| 166 | .fn_type_cc => try sema.zirFnTypeCc(block, zir_inst), | ||
| 167 | .intcast => try sema.zirIntcast(block, zir_inst), | ||
| 168 | .bitcast => try sema.zirBitcast(block, zir_inst), | ||
| 169 | .floatcast => try sema.zirFloatcast(block, zir_inst), | ||
| 170 | .elem_ptr => try sema.zirElemPtr(block, zir_inst), | ||
| 171 | .elem_ptr_node => try sema.zirElemPtrNode(block, zir_inst), | ||
| 172 | .elem_val => try sema.zirElemVal(block, zir_inst), | ||
| 173 | .elem_val_node => try sema.zirElemValNode(block, zir_inst), | ||
| 174 | .add => try sema.zirArithmetic(block, zir_inst), | ||
| 175 | .addwrap => try sema.zirArithmetic(block, zir_inst), | ||
| 176 | .sub => try sema.zirArithmetic(block, zir_inst), | ||
| 177 | .subwrap => try sema.zirArithmetic(block, zir_inst), | ||
| 178 | .mul => try sema.zirArithmetic(block, zir_inst), | ||
| 179 | .mulwrap => try sema.zirArithmetic(block, zir_inst), | ||
| 180 | .div => try sema.zirArithmetic(block, zir_inst), | ||
| 181 | .mod_rem => try sema.zirArithmetic(block, zir_inst), | ||
| 182 | .array_cat => try sema.zirArrayCat(block, zir_inst), | ||
| 183 | .array_mul => try sema.zirArrayMul(block, zir_inst), | ||
| 184 | .bit_and => try sema.zirBitwise(block, zir_inst), | ||
| 185 | .bit_not => try sema.zirBitNot(block, zir_inst), | ||
| 186 | .bit_or => try sema.zirBitwise(block, zir_inst), | ||
| 187 | .xor => try sema.zirBitwise(block, zir_inst), | ||
| 188 | .shl => try sema.zirShl(block, zir_inst), | ||
| 189 | .shr => try sema.zirShr(block, zir_inst), | ||
| 190 | .cmp_lt => try sema.zirCmp(block, zir_inst, .lt), | ||
| 191 | .cmp_lte => try sema.zirCmp(block, zir_inst, .lte), | ||
| 192 | .cmp_eq => try sema.zirCmp(block, zir_inst, .eq), | ||
| 193 | .cmp_gte => try sema.zirCmp(block, zir_inst, .gte), | ||
| 194 | .cmp_gt => try sema.zirCmp(block, zir_inst, .gt), | ||
| 195 | .cmp_neq => try sema.zirCmp(block, zir_inst, .neq), | ||
| 196 | .condbr => try sema.zirCondbr(block, zir_inst), | ||
| 197 | .is_null => try sema.zirIsNull(block, zir_inst, false), | ||
| 198 | .is_non_null => try sema.zirIsNull(block, zir_inst, true), | ||
| 199 | .is_null_ptr => try sema.zirIsNullPtr(block, zir_inst, false), | ||
| 200 | .is_non_null_ptr => try sema.zirIsNullPtr(block, zir_inst, true), | ||
| 201 | .is_err => try sema.zirIsErr(block, zir_inst), | ||
| 202 | .is_err_ptr => try sema.zirIsErrPtr(block, zir_inst), | ||
| 203 | .bool_not => try sema.zirBoolNot(block, zir_inst), | ||
| 204 | .typeof => try sema.zirTypeof(block, zir_inst), | ||
| 205 | .typeof_peer => try sema.zirTypeofPeer(block, zir_inst), | ||
| 206 | .optional_type => try sema.zirOptionalType(block, zir_inst), | ||
| 207 | .optional_type_from_ptr_elem => try sema.zirOptionalTypeFromPtrElem(block, zir_inst), | ||
| 208 | .optional_payload_safe => try sema.zirOptionalPayload(block, zir_inst, true), | ||
| 209 | .optional_payload_unsafe => try sema.zirOptionalPayload(block, zir_inst, false), | ||
| 210 | .optional_payload_safe_ptr => try sema.zirOptionalPayloadPtr(block, zir_inst, true), | ||
| 211 | .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, zir_inst, false), | ||
| 212 | .err_union_payload_safe => try sema.zirErrUnionPayload(block, zir_inst, true), | ||
| 213 | .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, zir_inst, false), | ||
| 214 | .err_union_payload_safe_ptr => try sema.zirErrUnionPayloadPtr(block, zir_inst, true), | ||
| 215 | .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, zir_inst, false), | ||
| 216 | .err_union_code => try sema.zirErrUnionCode(block, zir_inst), | ||
| 217 | .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, zir_inst), | ||
| 218 | .ensure_err_payload_void => try sema.zirEnsureErrPayloadVoid(block, zir_inst), | ||
| 219 | .array_type => try sema.zirArrayType(block, zir_inst), | ||
| 220 | .array_type_sentinel => try sema.zirArrayTypeSentinel(block, zir_inst), | ||
| 221 | .enum_literal => try sema.zirEnumLiteral(block, zir_inst), | ||
| 222 | .merge_error_sets => try sema.zirMergeErrorSets(block, zir_inst), | ||
| 223 | .error_union_type => try sema.zirErrorUnionType(block, zir_inst), | ||
| 224 | .anyframe_type => try sema.zirAnyframeType(block, zir_inst), | ||
| 225 | .error_set => try sema.zirErrorSet(block, zir_inst), | ||
| 226 | .error_value => try sema.zirErrorValue(block, zir_inst), | ||
| 227 | .slice_start => try sema.zirSliceStart(block, zir_inst), | ||
| 228 | .slice_end => try sema.zirSliceEnd(block, zir_inst), | ||
| 229 | .slice_sentinel => try sema.zirSliceSentinel(block, zir_inst), | ||
| 230 | .import => try sema.zirImport(block, zir_inst), | ||
| 231 | .bool_and => try sema.zirBoolOp(block, zir_inst, false), | ||
| 232 | .bool_or => try sema.zirBoolOp(block, zir_inst, true), | ||
| 233 | .void_value => try sema.mod.constVoid(block.arena, .unneeded), | ||
| 234 | .switchbr => try sema.zirSwitchBr(block, zir_inst, false), | ||
| 235 | .switchbr_ref => try sema.zirSwitchBr(block, zir_inst, true), | ||
| 236 | .switch_range => try sema.zirSwitchRange(block, zir_inst), | ||
| 237 | }; | ||
| 238 | if (map[zir_inst].ty.isNoReturn()) { | ||
| 239 | break; | ||
| 240 | } | ||
| 241 | } | ||
| 242 | } | ||
| 243 | |||
| 244 | fn resolveInst(sema: *Sema, block: *Scope.Block, zir_ref: zir.Inst.Ref) *const ir.Inst { | ||
| 245 | var i = zir_ref; | ||
| 246 | |||
| 247 | // First section of indexes correspond to a set number of constant values. | ||
| 248 | if (i < const_tzir_inst_list.len) { | ||
| 249 | return &const_tzir_inst_list[i]; | ||
| 250 | } | ||
| 251 | i -= const_tzir_inst_list.len; | ||
| 252 | |||
| 253 | // Next section of indexes correspond to function parameters, if any. | ||
| 254 | if (block.inlining) |inlining| { | ||
| 255 | if (i < inlining.casted_args.len) { | ||
| 256 | return inlining.casted_args[i]; | ||
| 257 | } | ||
| 258 | i -= inlining.casted_args.len; | ||
| 259 | } else { | ||
| 260 | if (i < sema.param_inst_list.len) { | ||
| 261 | return sema.param_inst_list[i]; | ||
| 262 | } | ||
| 263 | i -= sema.param_inst_list.len; | ||
| 264 | } | ||
| 265 | |||
| 266 | // Finally, the last section of indexes refers to the map of ZIR=>TZIR. | ||
| 267 | return sema.inst_map[i]; | ||
| 268 | } | ||
| 269 | |||
| 270 | fn resolveConstString( | ||
| 271 | sema: *Sema, | ||
| 272 | block: *Scope.Block, | ||
| 273 | src: LazySrcLoc, | ||
| 274 | zir_ref: zir.Inst.Ref, | ||
| 275 | ) ![]u8 { | ||
| 276 | const tzir_inst = sema.resolveInst(block, zir_ref); | ||
| 277 | const wanted_type = Type.initTag(.const_slice_u8); | ||
| 278 | const coerced_inst = try sema.coerce(block, wanted_type, tzir_inst); | ||
| 279 | const val = try sema.resolveConstValue(block, src, coerced_inst); | ||
| 280 | return val.toAllocatedBytes(block.arena); | ||
| 281 | } | ||
| 282 | |||
| 283 | fn resolveType(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, zir_ref: zir.Inst.Ref) !Type { | ||
| 284 | const tzir_inst = sema.resolveInt(block, zir_ref); | ||
| 285 | const wanted_type = Type.initTag(.@"type"); | ||
| 286 | const coerced_inst = try sema.coerce(block, wanted_type, tzir_inst); | ||
| 287 | const val = try sema.resolveConstValue(block, src, coerced_inst); | ||
| 288 | return val.toType(sema.arena); | ||
| 289 | } | ||
| 290 | |||
| 291 | fn resolveConstValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base: *ir.Inst) !Value { | ||
| 292 | return (try sema.resolveDefinedValue(block, src, base)) orelse | ||
| 293 | return sema.mod.fail(&block.base, src, "unable to resolve comptime value", .{}); | ||
| 294 | } | ||
| 295 | |||
| 296 | fn resolveDefinedValue(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, base: *ir.Inst) !?Value { | ||
| 297 | if (base.value()) |val| { | ||
| 298 | if (val.isUndef()) { | ||
| 299 | return sema.mod.fail(&block.base, src, "use of undefined value here causes undefined behavior", .{}); | ||
| 300 | } | ||
| 301 | return val; | ||
| 302 | } | ||
| 303 | return null; | ||
| 304 | } | ||
| 305 | |||
| 306 | /// Appropriate to call when the coercion has already been done by result | ||
| 307 | /// location semantics. Asserts the value fits in the provided `Int` type. | ||
| 308 | /// Only supports `Int` types 64 bits or less. | ||
| 309 | fn resolveAlreadyCoercedInt( | ||
| 310 | sema: *Sema, | ||
| 311 | block: *Scope.Block, | ||
| 312 | src: LazySrcLoc, | ||
| 313 | zir_ref: zir.Inst.Ref, | ||
| 314 | comptime Int: type, | ||
| 315 | ) !Int { | ||
| 316 | comptime assert(@typeInfo(Int).Int.bits <= 64); | ||
| 317 | const tzir_inst = sema.resolveInst(block, zir_ref); | ||
| 318 | const val = try sema.resolveConstValue(block, src, tzir_inst); | ||
| 319 | switch (@typeInfo(Int).Int.signedness) { | ||
| 320 | .signed => return @intCast(Int, val.toSignedInt()), | ||
| 321 | .unsigned => return @intCast(Int, val.toUnsignedInt()), | ||
| 322 | } | ||
| 323 | } | ||
| 324 | |||
| 325 | fn resolveInt( | ||
| 326 | sema: *Sema, | ||
| 327 | block: *Scope.Block, | ||
| 328 | src: LazySrcLoc, | ||
| 329 | zir_ref: zir.Inst.Ref, | ||
| 330 | dest_type: Type, | ||
| 331 | ) !u64 { | ||
| 332 | const tzir_inst = sema.resolveInst(block, zir_ref); | ||
| 333 | const coerced = try sema.coerce(scope, dest_type, tzir_inst); | ||
| 334 | const val = try sema.resolveConstValue(block, src, coerced); | ||
| 335 | |||
| 336 | return val.toUnsignedInt(); | ||
| 337 | } | ||
| 338 | |||
| 339 | fn resolveInstConst( | ||
| 340 | sema: *Sema, | ||
| 341 | block: *Scope.Block, | ||
| 342 | src: LazySrcLoc, | ||
| 343 | zir_ref: zir.Inst.Ref, | ||
| 344 | ) InnerError!TypedValue { | ||
| 345 | const tzir_inst = sema.resolveInst(block, zir_ref); | ||
| 346 | const val = try sema.resolveConstValue(block, src, tzir_inst); | ||
| 347 | return TypedValue{ | ||
| 348 | .ty = tzir_inst.ty, | ||
| 349 | .val = val, | ||
| 350 | }; | ||
| 351 | } | ||
| 352 | |||
| 353 | fn zirConst(sema: *Sema, block: *Scope.Block, const_inst: zir.Inst.Index) InnerError!*Inst { | ||
| 354 | const tracy = trace(@src()); | ||
| 355 | defer tracy.end(); | ||
| 356 | // Move the TypedValue from old memory to new memory. This allows freeing the ZIR instructions | ||
| 357 | // after analysis. | ||
| 358 | const typed_value_copy = try const_inst.positionals.typed_value.copy(block.arena); | ||
| 359 | return sema.mod.constInst(scope, const_inst.base.src, typed_value_copy); | ||
| 360 | } | ||
| 361 | |||
| 362 | fn zirBitcastRef(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 363 | const tracy = trace(@src()); | ||
| 364 | defer tracy.end(); | ||
| 365 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement zir_sema.zirBitcastRef", .{}); | ||
| 366 | } | ||
| 367 | |||
| 368 | fn zirBitcastResultPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 369 | const tracy = trace(@src()); | ||
| 370 | defer tracy.end(); | ||
| 371 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement zir_sema.zirBitcastResultPtr", .{}); | ||
| 372 | } | ||
| 373 | |||
| 374 | fn zirCoerceResultPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 375 | const tracy = trace(@src()); | ||
| 376 | defer tracy.end(); | ||
| 377 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement zirCoerceResultPtr", .{}); | ||
| 378 | } | ||
| 379 | |||
| 380 | fn zirRetPtr(sema: *Module, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 381 | const tracy = trace(@src()); | ||
| 382 | defer tracy.end(); | ||
| 383 | |||
| 384 | try sema.requireFunctionBlock(block, inst.base.src); | ||
| 385 | const fn_ty = block.func.?.owner_decl.typed_value.most_recent.typed_value.ty; | ||
| 386 | const ret_type = fn_ty.fnReturnType(); | ||
| 387 | const ptr_type = try sema.mod.simplePtrType(block.arena, ret_type, true, .One); | ||
| 388 | return block.addNoOp(inst.base.src, ptr_type, .alloc); | ||
| 389 | } | ||
| 390 | |||
| 391 | fn zirRef(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 392 | const tracy = trace(@src()); | ||
| 393 | defer tracy.end(); | ||
| 394 | |||
| 395 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 396 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 397 | return sema.analyzeRef(block, inst_data.src(), operand); | ||
| 398 | } | ||
| 399 | |||
| 400 | fn zirRetType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 401 | const tracy = trace(@src()); | ||
| 402 | defer tracy.end(); | ||
| 403 | try sema.requireFunctionBlock(block, inst.base.src); | ||
| 404 | const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty; | ||
| 405 | const ret_type = fn_ty.fnReturnType(); | ||
| 406 | return sema.mod.constType(block.arena, inst.base.src, ret_type); | ||
| 407 | } | ||
| 408 | |||
| 409 | fn zirEnsureResultUsed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 410 | const tracy = trace(@src()); | ||
| 411 | defer tracy.end(); | ||
| 412 | |||
| 413 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 414 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 415 | const src = inst_data.src(); | ||
| 416 | switch (operand.ty.zigTypeTag()) { | ||
| 417 | .Void, .NoReturn => return sema.mod.constVoid(block.arena, .unneeded), | ||
| 418 | else => return sema.mod.fail(&block.base, src, "expression value is ignored", .{}), | ||
| 419 | } | ||
| 420 | } | ||
| 421 | |||
| 422 | fn zirEnsureResultNonError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 423 | const tracy = trace(@src()); | ||
| 424 | defer tracy.end(); | ||
| 425 | |||
| 426 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 427 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 428 | const src = inst_data.src(); | ||
| 429 | switch (operand.ty.zigTypeTag()) { | ||
| 430 | .ErrorSet, .ErrorUnion => return sema.mod.fail(&block.base, src, "error is discarded", .{}), | ||
| 431 | else => return sema.mod.constVoid(block.arena, .unneeded), | ||
| 432 | } | ||
| 433 | } | ||
| 434 | |||
| 435 | fn zirIndexablePtrLen(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 436 | const tracy = trace(@src()); | ||
| 437 | defer tracy.end(); | ||
| 438 | |||
| 439 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 440 | const array_ptr = sema.resolveInst(block, inst_data.operand); | ||
| 441 | |||
| 442 | const elem_ty = array_ptr.ty.elemType(); | ||
| 443 | if (!elem_ty.isIndexable()) { | ||
| 444 | const cond_src: LazySrcLoc = .{ .node_offset_for_cond = inst_data.src_node }; | ||
| 445 | const msg = msg: { | ||
| 446 | const msg = try sema.mod.errMsg( | ||
| 447 | &block.base, | ||
| 448 | cond_src, | ||
| 449 | "type '{}' does not support indexing", | ||
| 450 | .{elem_ty}, | ||
| 451 | ); | ||
| 452 | errdefer msg.destroy(mod.gpa); | ||
| 453 | try sema.mod.errNote( | ||
| 454 | &block.base, | ||
| 455 | cond_src, | ||
| 456 | msg, | ||
| 457 | "for loop operand must be an array, slice, tuple, or vector", | ||
| 458 | .{}, | ||
| 459 | ); | ||
| 460 | break :msg msg; | ||
| 461 | }; | ||
| 462 | return mod.failWithOwnedErrorMsg(scope, msg); | ||
| 463 | } | ||
| 464 | const result_ptr = try sema.namedFieldPtr(block, inst.base.src, array_ptr, "len", inst.base.src); | ||
| 465 | return sema.analyzeDeref(block, inst.base.src, result_ptr, result_ptr.src); | ||
| 466 | } | ||
| 467 | |||
| 468 | fn zirAlloc(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 469 | const tracy = trace(@src()); | ||
| 470 | defer tracy.end(); | ||
| 471 | |||
| 472 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 473 | const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; | ||
| 474 | const var_decl_src = inst_data.src(); | ||
| 475 | const var_type = try sema.resolveType(block, ty_src, inst_data.operand); | ||
| 476 | const ptr_type = try sema.mod.simplePtrType(block.arena, var_type, true, .One); | ||
| 477 | try sema.requireRuntimeBlock(block, var_decl_src); | ||
| 478 | return block.addNoOp(var_decl_src, ptr_type, .alloc); | ||
| 479 | } | ||
| 480 | |||
| 481 | fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 482 | const tracy = trace(@src()); | ||
| 483 | defer tracy.end(); | ||
| 484 | |||
| 485 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 486 | const var_decl_src = inst_data.src(); | ||
| 487 | const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; | ||
| 488 | const var_type = try sema.resolveType(block, ty_src, inst_data.operand); | ||
| 489 | try sema.validateVarType(block, ty_src, var_type); | ||
| 490 | const ptr_type = try sema.mod.simplePtrType(block.arena, var_type, true, .One); | ||
| 491 | try sema.requireRuntimeBlock(block, var_decl_src); | ||
| 492 | return block.addNoOp(var_decl_src, ptr_type, .alloc); | ||
| 493 | } | ||
| 494 | |||
| 495 | fn zirAllocInferred( | ||
| 496 | sema: *Sema, | ||
| 497 | block: *Scope.Block, | ||
| 498 | inst: zir.Inst.Index, | ||
| 499 | inferred_alloc_ty: Type, | ||
| 500 | ) InnerError!*Inst { | ||
| 501 | const tracy = trace(@src()); | ||
| 502 | defer tracy.end(); | ||
| 503 | const val_payload = try block.arena.create(Value.Payload.InferredAlloc); | ||
| 504 | val_payload.* = .{ | ||
| 505 | .data = .{}, | ||
| 506 | }; | ||
| 507 | // `Module.constInst` does not add the instruction to the block because it is | ||
| 508 | // not needed in the case of constant values. However here, we plan to "downgrade" | ||
| 509 | // to a normal instruction when we hit `resolve_inferred_alloc`. So we append | ||
| 510 | // to the block even though it is currently a `.constant`. | ||
| 511 | const result = try sema.mod.constInst(scope, inst.base.src, .{ | ||
| 512 | .ty = inferred_alloc_ty, | ||
| 513 | .val = Value.initPayload(&val_payload.base), | ||
| 514 | }); | ||
| 515 | try sema.requireFunctionBlock(block, inst.base.src); | ||
| 516 | try block.instructions.append(sema.gpa, result); | ||
| 517 | return result; | ||
| 518 | } | ||
| 519 | |||
| 520 | fn zirResolveInferredAlloc( | ||
| 521 | sema: *Sema, | ||
| 522 | block: *Scope.Block, | ||
| 523 | inst: zir.Inst.Index, | ||
| 524 | ) InnerError!*Inst { | ||
| 525 | const tracy = trace(@src()); | ||
| 526 | defer tracy.end(); | ||
| 527 | |||
| 528 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 529 | const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; | ||
| 530 | const ptr = sema.resolveInst(block, inst_data.operand); | ||
| 531 | const ptr_val = ptr.castTag(.constant).?.val; | ||
| 532 | const inferred_alloc = ptr_val.castTag(.inferred_alloc).?; | ||
| 533 | const peer_inst_list = inferred_alloc.data.stored_inst_list.items; | ||
| 534 | const final_elem_ty = try sema.resolvePeerTypes(block, peer_inst_list); | ||
| 535 | const var_is_mut = switch (ptr.ty.tag()) { | ||
| 536 | .inferred_alloc_const => false, | ||
| 537 | .inferred_alloc_mut => true, | ||
| 538 | else => unreachable, | ||
| 539 | }; | ||
| 540 | if (var_is_mut) { | ||
| 541 | try sema.validateVarType(block, ty_src, final_elem_ty); | ||
| 542 | } | ||
| 543 | const final_ptr_ty = try sema.mod.simplePtrType(block.arena, final_elem_ty, true, .One); | ||
| 544 | |||
| 545 | // Change it to a normal alloc. | ||
| 546 | ptr.ty = final_ptr_ty; | ||
| 547 | ptr.tag = .alloc; | ||
| 548 | |||
| 549 | return sema.mod.constVoid(block.arena, .unneeded); | ||
| 550 | } | ||
| 551 | |||
| 552 | fn zirStoreToBlockPtr( | ||
| 553 | sema: *Sema, | ||
| 554 | block: *Scope.Block, | ||
| 555 | inst: zir.Inst.Index, | ||
| 556 | ) InnerError!*Inst { | ||
| 557 | const tracy = trace(@src()); | ||
| 558 | defer tracy.end(); | ||
| 559 | |||
| 560 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 561 | const ptr = sema.resolveInst(bin_inst.lhs); | ||
| 562 | const value = sema.resolveInst(bin_inst.rhs); | ||
| 563 | const ptr_ty = try sema.mod.simplePtrType(block.arena, value.ty, true, .One); | ||
| 564 | // TODO detect when this store should be done at compile-time. For example, | ||
| 565 | // if expressions should force it when the condition is compile-time known. | ||
| 566 | try sema.requireRuntimeBlock(block, src); | ||
| 567 | const bitcasted_ptr = try block.addUnOp(inst.base.src, ptr_ty, .bitcast, ptr); | ||
| 568 | return mod.storePtr(scope, inst.base.src, bitcasted_ptr, value); | ||
| 569 | } | ||
| 570 | |||
| 571 | fn zirStoreToInferredPtr( | ||
| 572 | sema: *Sema, | ||
| 573 | block: *Scope.Block, | ||
| 574 | inst: zir.Inst.Index, | ||
| 575 | ) InnerError!*Inst { | ||
| 576 | const tracy = trace(@src()); | ||
| 577 | defer tracy.end(); | ||
| 578 | |||
| 579 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 580 | const ptr = sema.resolveInst(bin_inst.lhs); | ||
| 581 | const value = sema.resolveInst(bin_inst.rhs); | ||
| 582 | const inferred_alloc = ptr.castTag(.constant).?.val.castTag(.inferred_alloc).?; | ||
| 583 | // Add the stored instruction to the set we will use to resolve peer types | ||
| 584 | // for the inferred allocation. | ||
| 585 | try inferred_alloc.data.stored_inst_list.append(block.arena, value); | ||
| 586 | // Create a runtime bitcast instruction with exactly the type the pointer wants. | ||
| 587 | const ptr_ty = try sema.mod.simplePtrType(block.arena, value.ty, true, .One); | ||
| 588 | try sema.requireRuntimeBlock(block, src); | ||
| 589 | const bitcasted_ptr = try block.addUnOp(inst.base.src, ptr_ty, .bitcast, ptr); | ||
| 590 | return mod.storePtr(scope, inst.base.src, bitcasted_ptr, value); | ||
| 591 | } | ||
| 592 | |||
| 593 | fn zirSetEvalBranchQuota( | ||
| 594 | sema: *Sema, | ||
| 595 | block: *Scope.Block, | ||
| 596 | inst: zir.Inst.Index, | ||
| 597 | ) InnerError!*Inst { | ||
| 598 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 599 | const src = inst_data.src(); | ||
| 600 | try sema.requireFunctionBlock(block, src); | ||
| 601 | const quota = try sema.resolveAlreadyCoercedInt(block, src, inst_data.operand, u32); | ||
| 602 | if (b.branch_quota.* < quota) | ||
| 603 | b.branch_quota.* = quota; | ||
| 604 | return sema.mod.constVoid(block.arena, .unneeded); | ||
| 605 | } | ||
| 606 | |||
| 607 | fn zirStore(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 608 | const tracy = trace(@src()); | ||
| 609 | defer tracy.end(); | ||
| 610 | |||
| 611 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 612 | const ptr = sema.resolveInst(bin_inst.lhs); | ||
| 613 | const value = sema.resolveInst(bin_inst.rhs); | ||
| 614 | return mod.storePtr(scope, inst.base.src, ptr, value); | ||
| 615 | } | ||
| 616 | |||
| 617 | fn zirParamType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 618 | const tracy = trace(@src()); | ||
| 619 | defer tracy.end(); | ||
| 620 | |||
| 621 | const inst_data = sema.code.instructions.items(.data)[inst].param_type; | ||
| 622 | const fn_inst = sema.resolveInst(inst_data.callee); | ||
| 623 | const param_index = inst_data.param_index; | ||
| 624 | |||
| 625 | const fn_ty: Type = switch (fn_inst.ty.zigTypeTag()) { | ||
| 626 | .Fn => fn_inst.ty, | ||
| 627 | .BoundFn => { | ||
| 628 | return sema.mod.fail(&block.base, fn_inst.src, "TODO implement zirParamType for method call syntax", .{}); | ||
| 629 | }, | ||
| 630 | else => { | ||
| 631 | return sema.mod.fail(&block.base, fn_inst.src, "expected function, found '{}'", .{fn_inst.ty}); | ||
| 632 | }, | ||
| 633 | }; | ||
| 634 | |||
| 635 | const param_count = fn_ty.fnParamLen(); | ||
| 636 | if (param_index >= param_count) { | ||
| 637 | if (fn_ty.fnIsVarArgs()) { | ||
| 638 | return sema.mod.constType(block.arena, inst.base.src, Type.initTag(.var_args_param)); | ||
| 639 | } | ||
| 640 | return sema.mod.fail(&block.base, inst.base.src, "arg index {d} out of bounds; '{}' has {d} argument(s)", .{ | ||
| 641 | param_index, | ||
| 642 | fn_ty, | ||
| 643 | param_count, | ||
| 644 | }); | ||
| 645 | } | ||
| 646 | |||
| 647 | // TODO support generic functions | ||
| 648 | const param_type = fn_ty.fnParamType(param_index); | ||
| 649 | return sema.mod.constType(block.arena, inst.base.src, param_type); | ||
| 650 | } | ||
| 651 | |||
| 652 | fn zirStr(sema: *Sema, block: *Scope.Block, str_inst: zir.Inst.Index) InnerError!*Inst { | ||
| 653 | const tracy = trace(@src()); | ||
| 654 | defer tracy.end(); | ||
| 655 | |||
| 656 | // The bytes references memory inside the ZIR module, which is fine. Multiple | ||
| 657 | // anonymous Decls may have strings which point to within the same ZIR module. | ||
| 658 | const bytes = sema.code.instructions.items(.data)[inst].str.get(sema.code); | ||
| 659 | |||
| 660 | var new_decl_arena = std.heap.ArenaAllocator.init(sema.gpa); | ||
| 661 | errdefer new_decl_arena.deinit(); | ||
| 662 | |||
| 663 | const decl_ty = try Type.Tag.array_u8_sentinel_0.create(&new_decl_arena.allocator, bytes.len); | ||
| 664 | const decl_val = try Value.Tag.bytes.create(&new_decl_arena.allocator, bytes); | ||
| 665 | |||
| 666 | const new_decl = try sema.mod.createAnonymousDecl(&block.base, &new_decl_arena, .{ | ||
| 667 | .ty = decl_ty, | ||
| 668 | .val = decl_val, | ||
| 669 | }); | ||
| 670 | return sema.analyzeDeclRef(block, .unneeded, new_decl); | ||
| 671 | } | ||
| 672 | |||
| 673 | fn zirInt(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 674 | const tracy = trace(@src()); | ||
| 675 | defer tracy.end(); | ||
| 676 | |||
| 677 | return mod.constIntBig(scope, inst.base.src, Type.initTag(.comptime_int), inst.positionals.int); | ||
| 678 | } | ||
| 679 | |||
| 680 | fn zirCompileError(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 681 | const tracy = trace(@src()); | ||
| 682 | defer tracy.end(); | ||
| 683 | |||
| 684 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 685 | const src = inst_data.src(); | ||
| 686 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | ||
| 687 | const msg = try sema.resolveConstString(block, operand_src, inst_data.operand); | ||
| 688 | return sema.mod.fail(&block.base, src, "{s}", .{msg}); | ||
| 689 | } | ||
| 690 | |||
| 691 | fn zirCompileLog(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 692 | var managed = mod.compile_log_text.toManaged(mod.gpa); | ||
| 693 | defer mod.compile_log_text = managed.moveToUnmanaged(); | ||
| 694 | const writer = managed.writer(); | ||
| 695 | |||
| 696 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 697 | const extra = sema.code.extraData(zir.Inst.MultiOp, inst_data.payload_index); | ||
| 698 | for (sema.code.extra[extra.end..][0..extra.data.operands_len]) |arg_ref, i| { | ||
| 699 | if (i != 0) try writer.print(", ", .{}); | ||
| 700 | |||
| 701 | const arg = sema.resolveInst(block, arg_ref); | ||
| 702 | if (arg.value()) |val| { | ||
| 703 | try writer.print("@as({}, {})", .{ arg.ty, val }); | ||
| 704 | } else { | ||
| 705 | try writer.print("@as({}, [runtime value])", .{arg.ty}); | ||
| 706 | } | ||
| 707 | } | ||
| 708 | try writer.print("\n", .{}); | ||
| 709 | |||
| 710 | const gop = try mod.compile_log_decls.getOrPut(mod.gpa, scope.ownerDecl().?); | ||
| 711 | if (!gop.found_existing) { | ||
| 712 | gop.entry.value = .{ | ||
| 713 | .file_scope = block.getFileScope(), | ||
| 714 | .lazy = inst_data.src(), | ||
| 715 | }; | ||
| 716 | } | ||
| 717 | return sema.mod.constVoid(block.arena, .unneeded); | ||
| 718 | } | ||
| 719 | |||
| 720 | fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 721 | const tracy = trace(@src()); | ||
| 722 | defer tracy.end(); | ||
| 723 | |||
| 724 | // Reserve space for a Loop instruction so that generated Break instructions can | ||
| 725 | // point to it, even if it doesn't end up getting used because the code ends up being | ||
| 726 | // comptime evaluated. | ||
| 727 | const loop_inst = try parent_block.arena.create(Inst.Loop); | ||
| 728 | loop_inst.* = .{ | ||
| 729 | .base = .{ | ||
| 730 | .tag = Inst.Loop.base_tag, | ||
| 731 | .ty = Type.initTag(.noreturn), | ||
| 732 | .src = inst.base.src, | ||
| 733 | }, | ||
| 734 | .body = undefined, | ||
| 735 | }; | ||
| 736 | |||
| 737 | var child_block: Scope.Block = .{ | ||
| 738 | .parent = parent_block, | ||
| 739 | .inst_table = parent_block.inst_table, | ||
| 740 | .func = parent_block.func, | ||
| 741 | .owner_decl = parent_block.owner_decl, | ||
| 742 | .src_decl = parent_block.src_decl, | ||
| 743 | .instructions = .{}, | ||
| 744 | .arena = parent_block.arena, | ||
| 745 | .inlining = parent_block.inlining, | ||
| 746 | .is_comptime = parent_block.is_comptime, | ||
| 747 | .branch_quota = parent_block.branch_quota, | ||
| 748 | }; | ||
| 749 | defer child_block.instructions.deinit(mod.gpa); | ||
| 750 | |||
| 751 | try sema.body(&child_block, inst.positionals.body); | ||
| 752 | |||
| 753 | // Loop repetition is implied so the last instruction may or may not be a noreturn instruction. | ||
| 754 | |||
| 755 | try parent_block.instructions.append(mod.gpa, &loop_inst.base); | ||
| 756 | loop_inst.body = .{ .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items) }; | ||
| 757 | return &loop_inst.base; | ||
| 758 | } | ||
| 759 | |||
| 760 | fn zirBlockFlat(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index, is_comptime: bool) InnerError!*Inst { | ||
| 761 | const tracy = trace(@src()); | ||
| 762 | defer tracy.end(); | ||
| 763 | |||
| 764 | var child_block = parent_block.makeSubBlock(); | ||
| 765 | defer child_block.instructions.deinit(mod.gpa); | ||
| 766 | child_block.is_comptime = child_block.is_comptime or is_comptime; | ||
| 767 | |||
| 768 | try sema.body(&child_block, inst.positionals.body); | ||
| 769 | |||
| 770 | // Move the analyzed instructions into the parent block arena. | ||
| 771 | const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items); | ||
| 772 | try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); | ||
| 773 | |||
| 774 | // The result of a flat block is the last instruction. | ||
| 775 | const zir_inst_list = inst.positionals.body.instructions; | ||
| 776 | const last_zir_inst = zir_inst_list[zir_inst_list.len - 1]; | ||
| 777 | return sema.inst_map[last_zir_inst]; | ||
| 778 | } | ||
| 779 | |||
| 780 | fn zirBlock( | ||
| 781 | sema: *Sema, | ||
| 782 | parent_block: *Scope.Block, | ||
| 783 | inst: zir.Inst.Index, | ||
| 784 | is_comptime: bool, | ||
| 785 | ) InnerError!*Inst { | ||
| 786 | const tracy = trace(@src()); | ||
| 787 | defer tracy.end(); | ||
| 788 | |||
| 789 | // Reserve space for a Block instruction so that generated Break instructions can | ||
| 790 | // point to it, even if it doesn't end up getting used because the code ends up being | ||
| 791 | // comptime evaluated. | ||
| 792 | const block_inst = try parent_block.arena.create(Inst.Block); | ||
| 793 | block_inst.* = .{ | ||
| 794 | .base = .{ | ||
| 795 | .tag = Inst.Block.base_tag, | ||
| 796 | .ty = undefined, // Set after analysis. | ||
| 797 | .src = inst.base.src, | ||
| 798 | }, | ||
| 799 | .body = undefined, | ||
| 800 | }; | ||
| 801 | |||
| 802 | var child_block: Scope.Block = .{ | ||
| 803 | .parent = parent_block, | ||
| 804 | .inst_table = parent_block.inst_table, | ||
| 805 | .func = parent_block.func, | ||
| 806 | .owner_decl = parent_block.owner_decl, | ||
| 807 | .src_decl = parent_block.src_decl, | ||
| 808 | .instructions = .{}, | ||
| 809 | .arena = parent_block.arena, | ||
| 810 | // TODO @as here is working around a stage1 miscompilation bug :( | ||
| 811 | .label = @as(?Scope.Block.Label, Scope.Block.Label{ | ||
| 812 | .zir_block = inst, | ||
| 813 | .merges = .{ | ||
| 814 | .results = .{}, | ||
| 815 | .br_list = .{}, | ||
| 816 | .block_inst = block_inst, | ||
| 817 | }, | ||
| 818 | }), | ||
| 819 | .inlining = parent_block.inlining, | ||
| 820 | .is_comptime = is_comptime or parent_block.is_comptime, | ||
| 821 | .branch_quota = parent_block.branch_quota, | ||
| 822 | }; | ||
| 823 | const merges = &child_block.label.?.merges; | ||
| 824 | |||
| 825 | defer child_block.instructions.deinit(mod.gpa); | ||
| 826 | defer merges.results.deinit(mod.gpa); | ||
| 827 | defer merges.br_list.deinit(mod.gpa); | ||
| 828 | |||
| 829 | try sema.body(&child_block, inst.positionals.body); | ||
| 830 | |||
| 831 | return analyzeBlockBody(mod, scope, &child_block, merges); | ||
| 832 | } | ||
| 833 | |||
| 834 | fn analyzeBlockBody( | ||
| 835 | sema: *Sema, | ||
| 836 | parent_block: *Scope.Block, | ||
| 837 | child_block: *Scope.Block, | ||
| 838 | merges: *Scope.Block.Merges, | ||
| 839 | ) InnerError!*Inst { | ||
| 840 | const tracy = trace(@src()); | ||
| 841 | defer tracy.end(); | ||
| 842 | |||
| 843 | // Blocks must terminate with noreturn instruction. | ||
| 844 | assert(child_block.instructions.items.len != 0); | ||
| 845 | assert(child_block.instructions.items[child_block.instructions.items.len - 1].ty.isNoReturn()); | ||
| 846 | |||
| 847 | if (merges.results.items.len == 0) { | ||
| 848 | // No need for a block instruction. We can put the new instructions | ||
| 849 | // directly into the parent block. | ||
| 850 | const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items); | ||
| 851 | try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); | ||
| 852 | return copied_instructions[copied_instructions.len - 1]; | ||
| 853 | } | ||
| 854 | if (merges.results.items.len == 1) { | ||
| 855 | const last_inst_index = child_block.instructions.items.len - 1; | ||
| 856 | const last_inst = child_block.instructions.items[last_inst_index]; | ||
| 857 | if (last_inst.breakBlock()) |br_block| { | ||
| 858 | if (br_block == merges.block_inst) { | ||
| 859 | // No need for a block instruction. We can put the new instructions directly | ||
| 860 | // into the parent block. Here we omit the break instruction. | ||
| 861 | const copied_instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items[0..last_inst_index]); | ||
| 862 | try parent_block.instructions.appendSlice(mod.gpa, copied_instructions); | ||
| 863 | return merges.results.items[0]; | ||
| 864 | } | ||
| 865 | } | ||
| 866 | } | ||
| 867 | // It is impossible to have the number of results be > 1 in a comptime scope. | ||
| 868 | assert(!child_block.is_comptime); // Should already got a compile error in the condbr condition. | ||
| 869 | |||
| 870 | // Need to set the type and emit the Block instruction. This allows machine code generation | ||
| 871 | // to emit a jump instruction to after the block when it encounters the break. | ||
| 872 | try parent_block.instructions.append(mod.gpa, &merges.block_inst.base); | ||
| 873 | const resolved_ty = try sema.resolvePeerTypes(parent_block, merges.results.items); | ||
| 874 | merges.block_inst.base.ty = resolved_ty; | ||
| 875 | merges.block_inst.body = .{ | ||
| 876 | .instructions = try parent_block.arena.dupe(*Inst, child_block.instructions.items), | ||
| 877 | }; | ||
| 878 | // Now that the block has its type resolved, we need to go back into all the break | ||
| 879 | // instructions, and insert type coercion on the operands. | ||
| 880 | for (merges.br_list.items) |br| { | ||
| 881 | if (br.operand.ty.eql(resolved_ty)) { | ||
| 882 | // No type coercion needed. | ||
| 883 | continue; | ||
| 884 | } | ||
| 885 | var coerce_block = parent_block.makeSubBlock(); | ||
| 886 | defer coerce_block.instructions.deinit(mod.gpa); | ||
| 887 | const coerced_operand = try sema.coerce(&coerce_block.base, resolved_ty, br.operand); | ||
| 888 | // If no instructions were produced, such as in the case of a coercion of a | ||
| 889 | // constant value to a new type, we can simply point the br operand to it. | ||
| 890 | if (coerce_block.instructions.items.len == 0) { | ||
| 891 | br.operand = coerced_operand; | ||
| 892 | continue; | ||
| 893 | } | ||
| 894 | assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1] == coerced_operand); | ||
| 895 | // Here we depend on the br instruction having been over-allocated (if necessary) | ||
| 896 | // inide analyzeBreak so that it can be converted into a br_block_flat instruction. | ||
| 897 | const br_src = br.base.src; | ||
| 898 | const br_ty = br.base.ty; | ||
| 899 | const br_block_flat = @ptrCast(*Inst.BrBlockFlat, br); | ||
| 900 | br_block_flat.* = .{ | ||
| 901 | .base = .{ | ||
| 902 | .src = br_src, | ||
| 903 | .ty = br_ty, | ||
| 904 | .tag = .br_block_flat, | ||
| 905 | }, | ||
| 906 | .block = merges.block_inst, | ||
| 907 | .body = .{ | ||
| 908 | .instructions = try parent_block.arena.dupe(*Inst, coerce_block.instructions.items), | ||
| 909 | }, | ||
| 910 | }; | ||
| 911 | } | ||
| 912 | return &merges.block_inst.base; | ||
| 913 | } | ||
| 914 | |||
| 915 | fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 916 | const tracy = trace(@src()); | ||
| 917 | defer tracy.end(); | ||
| 918 | |||
| 919 | try sema.requireRuntimeBlock(block, src); | ||
| 920 | return block.addNoOp(inst.base.src, Type.initTag(.void), .breakpoint); | ||
| 921 | } | ||
| 922 | |||
| 923 | fn zirBreak(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 924 | const tracy = trace(@src()); | ||
| 925 | defer tracy.end(); | ||
| 926 | |||
| 927 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 928 | const operand = sema.resolveInst(block, bin_inst.rhs); | ||
| 929 | const zir_block = bin_inst.lhs; | ||
| 930 | return analyzeBreak(mod, block, sema.src, zir_block, operand); | ||
| 931 | } | ||
| 932 | |||
| 933 | fn zirBreakVoidTok(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 934 | const tracy = trace(@src()); | ||
| 935 | defer tracy.end(); | ||
| 936 | |||
| 937 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 938 | const zir_block = inst_data.operand; | ||
| 939 | const void_inst = try sema.mod.constVoid(block.arena, .unneeded); | ||
| 940 | return analyzeBreak(mod, block, inst_data.src(), zir_block, void_inst); | ||
| 941 | } | ||
| 942 | |||
| 943 | fn analyzeBreak( | ||
| 944 | sema: *Sema, | ||
| 945 | block: *Scope.Block, | ||
| 946 | src: LazySrcLoc, | ||
| 947 | zir_block: zir.Inst.Index, | ||
| 948 | operand: *Inst, | ||
| 949 | ) InnerError!*Inst { | ||
| 950 | var opt_block = scope.cast(Scope.Block); | ||
| 951 | while (opt_block) |block| { | ||
| 952 | if (block.label) |*label| { | ||
| 953 | if (label.zir_block == zir_block) { | ||
| 954 | try sema.requireFunctionBlock(block, src); | ||
| 955 | // Here we add a br instruction, but we over-allocate a little bit | ||
| 956 | // (if necessary) to make it possible to convert the instruction into | ||
| 957 | // a br_block_flat instruction later. | ||
| 958 | const br = @ptrCast(*Inst.Br, try b.arena.alignedAlloc( | ||
| 959 | u8, | ||
| 960 | Inst.convertable_br_align, | ||
| 961 | Inst.convertable_br_size, | ||
| 962 | )); | ||
| 963 | br.* = .{ | ||
| 964 | .base = .{ | ||
| 965 | .tag = .br, | ||
| 966 | .ty = Type.initTag(.noreturn), | ||
| 967 | .src = src, | ||
| 968 | }, | ||
| 969 | .operand = operand, | ||
| 970 | .block = label.merges.block_inst, | ||
| 971 | }; | ||
| 972 | try b.instructions.append(mod.gpa, &br.base); | ||
| 973 | try label.merges.results.append(mod.gpa, operand); | ||
| 974 | try label.merges.br_list.append(mod.gpa, br); | ||
| 975 | return &br.base; | ||
| 976 | } | ||
| 977 | } | ||
| 978 | opt_block = block.parent; | ||
| 979 | } else unreachable; | ||
| 980 | } | ||
| 981 | |||
| 982 | fn zirDbgStmtNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 983 | const tracy = trace(@src()); | ||
| 984 | defer tracy.end(); | ||
| 985 | |||
| 986 | if (b.is_comptime) { | ||
| 987 | return sema.mod.constVoid(block.arena, .unneeded); | ||
| 988 | } | ||
| 989 | |||
| 990 | const src_node = sema.code.instructions.items(.data)[inst].node; | ||
| 991 | const src: LazySrcLoc = .{ .node_offset = src_node }; | ||
| 992 | return block.addNoOp(src, Type.initTag(.void), .dbg_stmt); | ||
| 993 | } | ||
| 994 | |||
| 995 | fn zirDeclRef(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 996 | const tracy = trace(@src()); | ||
| 997 | defer tracy.end(); | ||
| 998 | |||
| 999 | const decl = sema.code.instructions.items(.data)[inst].decl; | ||
| 1000 | return sema.analyzeDeclRef(block, .unneeded, decl); | ||
| 1001 | } | ||
| 1002 | |||
| 1003 | fn zirDeclVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1004 | const tracy = trace(@src()); | ||
| 1005 | defer tracy.end(); | ||
| 1006 | |||
| 1007 | const decl = sema.code.instructions.items(.data)[inst].decl; | ||
| 1008 | return sema.analyzeDeclVal(block, .unneeded, decl); | ||
| 1009 | } | ||
| 1010 | |||
| 1011 | fn zirCallNone(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1012 | const tracy = trace(@src()); | ||
| 1013 | defer tracy.end(); | ||
| 1014 | |||
| 1015 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 1016 | const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node }; | ||
| 1017 | |||
| 1018 | return sema.analyzeCall(block, inst_data.operand, func_src, inst_data.src(), .auto, &.{}); | ||
| 1019 | } | ||
| 1020 | |||
| 1021 | fn zirCall( | ||
| 1022 | sema: *Sema, | ||
| 1023 | block: *Scope.Block, | ||
| 1024 | inst: zir.Inst.Index, | ||
| 1025 | modifier: std.builtin.CallOptions.Modifier, | ||
| 1026 | ) InnerError!*Inst { | ||
| 1027 | const tracy = trace(@src()); | ||
| 1028 | defer tracy.end(); | ||
| 1029 | |||
| 1030 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1031 | const func_src: LazySrcLoc = .{ .node_offset_call_func = inst_data.src_node }; | ||
| 1032 | const call_src = inst_data.src(); | ||
| 1033 | const extra = sema.code.extraData(zir.Inst.Call, inst_data.payload_index); | ||
| 1034 | const args = sema.code.extra[extra.end..][0..extra.data.args_len]; | ||
| 1035 | |||
| 1036 | return sema.analyzeCall(block, extra.data.callee, func_src, call_src, modifier, args); | ||
| 1037 | } | ||
| 1038 | |||
| 1039 | fn analyzeCall( | ||
| 1040 | sema: *Sema, | ||
| 1041 | block: *Scope.Block, | ||
| 1042 | zir_func: zir.Inst.Ref, | ||
| 1043 | func_src: LazySrcLoc, | ||
| 1044 | call_src: LazySrcLoc, | ||
| 1045 | modifier: std.builtin.CallOptions.Modifier, | ||
| 1046 | zir_args: []const Ref, | ||
| 1047 | ) InnerError!*ir.Inst { | ||
| 1048 | const func = sema.resolveInst(zir_func); | ||
| 1049 | |||
| 1050 | if (func.ty.zigTypeTag() != .Fn) | ||
| 1051 | return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func.ty}); | ||
| 1052 | |||
| 1053 | const cc = func.ty.fnCallingConvention(); | ||
| 1054 | if (cc == .Naked) { | ||
| 1055 | // TODO add error note: declared here | ||
| 1056 | return sema.mod.fail( | ||
| 1057 | &block.base, | ||
| 1058 | func_src, | ||
| 1059 | "unable to call function with naked calling convention", | ||
| 1060 | .{}, | ||
| 1061 | ); | ||
| 1062 | } | ||
| 1063 | const fn_params_len = func.ty.fnParamLen(); | ||
| 1064 | if (func.ty.fnIsVarArgs()) { | ||
| 1065 | assert(cc == .C); | ||
| 1066 | if (zir_args.len < fn_params_len) { | ||
| 1067 | // TODO add error note: declared here | ||
| 1068 | return sema.mod.fail( | ||
| 1069 | &block.base, | ||
| 1070 | func_src, | ||
| 1071 | "expected at least {d} argument(s), found {d}", | ||
| 1072 | .{ fn_params_len, zir_args.len }, | ||
| 1073 | ); | ||
| 1074 | } | ||
| 1075 | } else if (fn_params_len != zir_args.len) { | ||
| 1076 | // TODO add error note: declared here | ||
| 1077 | return sema.mod.fail( | ||
| 1078 | &block.base, | ||
| 1079 | func_src, | ||
| 1080 | "expected {d} argument(s), found {d}", | ||
| 1081 | .{ fn_params_len, zir_args.len }, | ||
| 1082 | ); | ||
| 1083 | } | ||
| 1084 | |||
| 1085 | if (modifier == .compile_time) { | ||
| 1086 | return sema.mod.fail(&block.base, call_src, "TODO implement comptime function calls", .{}); | ||
| 1087 | } | ||
| 1088 | if (modifier != .auto) { | ||
| 1089 | return sema.mod.fail(&block.base, call_src, "TODO implement call with modifier {}", .{inst.positionals.modifier}); | ||
| 1090 | } | ||
| 1091 | |||
| 1092 | // TODO handle function calls of generic functions | ||
| 1093 | const casted_args = try block.arena.alloc(*Inst, zir_args.len); | ||
| 1094 | for (zir_args) |zir_arg, i| { | ||
| 1095 | // the args are already casted to the result of a param type instruction. | ||
| 1096 | casted_args[i] = sema.resolveInst(block, zir_arg); | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | const ret_type = func.ty.fnReturnType(); | ||
| 1100 | |||
| 1101 | try sema.requireFunctionBlock(block, call_src); | ||
| 1102 | const is_comptime_call = b.is_comptime or modifier == .compile_time; | ||
| 1103 | const is_inline_call = is_comptime_call or modifier == .always_inline or | ||
| 1104 | func.ty.fnCallingConvention() == .Inline; | ||
| 1105 | if (is_inline_call) { | ||
| 1106 | const func_val = try sema.resolveConstValue(block, func_src, func); | ||
| 1107 | const module_fn = switch (func_val.tag()) { | ||
| 1108 | .function => func_val.castTag(.function).?.data, | ||
| 1109 | .extern_fn => return sema.mod.fail(&block.base, call_src, "{s} call of extern function", .{ | ||
| 1110 | @as([]const u8, if (is_comptime_call) "comptime" else "inline"), | ||
| 1111 | }), | ||
| 1112 | else => unreachable, | ||
| 1113 | }; | ||
| 1114 | |||
| 1115 | // Analyze the ZIR. The same ZIR gets analyzed into a runtime function | ||
| 1116 | // or an inlined call depending on what union tag the `label` field is | ||
| 1117 | // set to in the `Scope.Block`. | ||
| 1118 | // This block instruction will be used to capture the return value from the | ||
| 1119 | // inlined function. | ||
| 1120 | const block_inst = try block.arena.create(Inst.Block); | ||
| 1121 | block_inst.* = .{ | ||
| 1122 | .base = .{ | ||
| 1123 | .tag = Inst.Block.base_tag, | ||
| 1124 | .ty = ret_type, | ||
| 1125 | .src = call_src, | ||
| 1126 | }, | ||
| 1127 | .body = undefined, | ||
| 1128 | }; | ||
| 1129 | // If this is the top of the inline/comptime call stack, we use this data. | ||
| 1130 | // Otherwise we pass on the shared data from the parent scope. | ||
| 1131 | var shared_inlining: Scope.Block.Inlining.Shared = .{ | ||
| 1132 | .branch_count = 0, | ||
| 1133 | .caller = b.func, | ||
| 1134 | }; | ||
| 1135 | // This one is shared among sub-blocks within the same callee, but not | ||
| 1136 | // shared among the entire inline/comptime call stack. | ||
| 1137 | var inlining: Scope.Block.Inlining = .{ | ||
| 1138 | .shared = if (b.inlining) |inlining| inlining.shared else &shared_inlining, | ||
| 1139 | .param_index = 0, | ||
| 1140 | .casted_args = casted_args, | ||
| 1141 | .merges = .{ | ||
| 1142 | .results = .{}, | ||
| 1143 | .br_list = .{}, | ||
| 1144 | .block_inst = block_inst, | ||
| 1145 | }, | ||
| 1146 | }; | ||
| 1147 | var inst_table = Scope.Block.InstTable.init(mod.gpa); | ||
| 1148 | defer inst_table.deinit(); | ||
| 1149 | |||
| 1150 | var child_block: Scope.Block = .{ | ||
| 1151 | .parent = null, | ||
| 1152 | .inst_table = &inst_table, | ||
| 1153 | .func = module_fn, | ||
| 1154 | .owner_decl = scope.ownerDecl().?, | ||
| 1155 | .src_decl = module_fn.owner_decl, | ||
| 1156 | .instructions = .{}, | ||
| 1157 | .arena = block.arena, | ||
| 1158 | .label = null, | ||
| 1159 | .inlining = &inlining, | ||
| 1160 | .is_comptime = is_comptime_call, | ||
| 1161 | .branch_quota = b.branch_quota, | ||
| 1162 | }; | ||
| 1163 | |||
| 1164 | const merges = &child_block.inlining.?.merges; | ||
| 1165 | |||
| 1166 | defer child_block.instructions.deinit(mod.gpa); | ||
| 1167 | defer merges.results.deinit(mod.gpa); | ||
| 1168 | defer merges.br_list.deinit(mod.gpa); | ||
| 1169 | |||
| 1170 | try mod.emitBackwardBranch(&child_block, call_src); | ||
| 1171 | |||
| 1172 | // This will have return instructions analyzed as break instructions to | ||
| 1173 | // the block_inst above. | ||
| 1174 | try sema.body(&child_block, module_fn.zir); | ||
| 1175 | |||
| 1176 | return analyzeBlockBody(mod, scope, &child_block, merges); | ||
| 1177 | } | ||
| 1178 | |||
| 1179 | return block.addCall(call_src, ret_type, func, casted_args); | ||
| 1180 | } | ||
| 1181 | |||
| 1182 | fn zirIntType(sema: *Sema, block: *Scope.Block, inttype: zir.Inst.Index) InnerError!*Inst { | ||
| 1183 | const tracy = trace(@src()); | ||
| 1184 | defer tracy.end(); | ||
| 1185 | return sema.mod.fail(&block.base, inttype.base.src, "TODO implement inttype", .{}); | ||
| 1186 | } | ||
| 1187 | |||
| 1188 | fn zirOptionalType(sema: *Sema, block: *Scope.Block, optional: zir.Inst.Index) InnerError!*Inst { | ||
| 1189 | const tracy = trace(@src()); | ||
| 1190 | defer tracy.end(); | ||
| 1191 | |||
| 1192 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1193 | const child_type = try sema.resolveType(block, inst_data.operand); | ||
| 1194 | const opt_type = try mod.optionalType(block.arena, child_type); | ||
| 1195 | |||
| 1196 | return sema.mod.constType(block.arena, inst_data.src(), opt_type); | ||
| 1197 | } | ||
| 1198 | |||
| 1199 | fn zirOptionalTypeFromPtrElem(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1200 | const tracy = trace(@src()); | ||
| 1201 | defer tracy.end(); | ||
| 1202 | |||
| 1203 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1204 | const ptr = sema.resolveInst(block, inst_data.operand); | ||
| 1205 | const elem_ty = ptr.ty.elemType(); | ||
| 1206 | const opt_ty = try mod.optionalType(block.arena, elem_ty); | ||
| 1207 | |||
| 1208 | return sema.mod.constType(block.arena, inst_data.src(), opt_ty); | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | fn zirArrayType(sema: *Sema, block: *Scope.Block, array: zir.Inst.Index) InnerError!*Inst { | ||
| 1212 | const tracy = trace(@src()); | ||
| 1213 | defer tracy.end(); | ||
| 1214 | // TODO these should be lazily evaluated | ||
| 1215 | const len = try resolveInstConst(mod, scope, array.positionals.lhs); | ||
| 1216 | const elem_type = try sema.resolveType(block, array.positionals.rhs); | ||
| 1217 | |||
| 1218 | return sema.mod.constType(block.arena, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), null, elem_type)); | ||
| 1219 | } | ||
| 1220 | |||
| 1221 | fn zirArrayTypeSentinel(sema: *Sema, block: *Scope.Block, array: zir.Inst.Index) InnerError!*Inst { | ||
| 1222 | const tracy = trace(@src()); | ||
| 1223 | defer tracy.end(); | ||
| 1224 | // TODO these should be lazily evaluated | ||
| 1225 | const len = try resolveInstConst(mod, scope, array.positionals.len); | ||
| 1226 | const sentinel = try resolveInstConst(mod, scope, array.positionals.sentinel); | ||
| 1227 | const elem_type = try sema.resolveType(block, array.positionals.elem_type); | ||
| 1228 | |||
| 1229 | return sema.mod.constType(block.arena, array.base.src, try mod.arrayType(scope, len.val.toUnsignedInt(), sentinel.val, elem_type)); | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | fn zirErrorUnionType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1233 | const tracy = trace(@src()); | ||
| 1234 | defer tracy.end(); | ||
| 1235 | |||
| 1236 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1237 | const error_union = try sema.resolveType(block, bin_inst.lhs); | ||
| 1238 | const payload = try sema.resolveType(block, bin_inst.rhs); | ||
| 1239 | |||
| 1240 | if (error_union.zigTypeTag() != .ErrorSet) { | ||
| 1241 | return sema.mod.fail(&block.base, inst.base.src, "expected error set type, found {}", .{error_union.elemType()}); | ||
| 1242 | } | ||
| 1243 | |||
| 1244 | return sema.mod.constType(block.arena, inst.base.src, try mod.errorUnionType(scope, error_union, payload)); | ||
| 1245 | } | ||
| 1246 | |||
| 1247 | fn zirAnyframeType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1248 | const tracy = trace(@src()); | ||
| 1249 | defer tracy.end(); | ||
| 1250 | |||
| 1251 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 1252 | const src = inst_data.src(); | ||
| 1253 | const operand_src: LazySrcLoc = .{ .node_offset_anyframe_type = inst_data.src_node }; | ||
| 1254 | const return_type = try sema.resolveType(block, operand_src, inst_data.operand); | ||
| 1255 | const anyframe_type = try sema.mod.anyframeType(block.arena, return_type); | ||
| 1256 | |||
| 1257 | return sema.mod.constType(block.arena, src, anyframe_type); | ||
| 1258 | } | ||
| 1259 | |||
| 1260 | fn zirErrorSet(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1261 | const tracy = trace(@src()); | ||
| 1262 | defer tracy.end(); | ||
| 1263 | |||
| 1264 | // The owner Decl arena will store the hashmap. | ||
| 1265 | var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa); | ||
| 1266 | errdefer new_decl_arena.deinit(); | ||
| 1267 | |||
| 1268 | const payload = try new_decl_arena.allocator.create(Value.Payload.ErrorSet); | ||
| 1269 | payload.* = .{ | ||
| 1270 | .base = .{ .tag = .error_set }, | ||
| 1271 | .data = .{ | ||
| 1272 | .fields = .{}, | ||
| 1273 | .decl = undefined, // populated below | ||
| 1274 | }, | ||
| 1275 | }; | ||
| 1276 | try payload.data.fields.ensureCapacity(&new_decl_arena.allocator, @intCast(u32, inst.positionals.fields.len)); | ||
| 1277 | |||
| 1278 | for (inst.positionals.fields) |field_name| { | ||
| 1279 | const entry = try mod.getErrorValue(field_name); | ||
| 1280 | if (payload.data.fields.fetchPutAssumeCapacity(entry.key, {})) |_| { | ||
| 1281 | return sema.mod.fail(&block.base, inst.base.src, "duplicate error: '{s}'", .{field_name}); | ||
| 1282 | } | ||
| 1283 | } | ||
| 1284 | // TODO create name in format "error:line:column" | ||
| 1285 | const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{ | ||
| 1286 | .ty = Type.initTag(.type), | ||
| 1287 | .val = Value.initPayload(&payload.base), | ||
| 1288 | }); | ||
| 1289 | payload.data.decl = new_decl; | ||
| 1290 | return mod.analyzeDeclVal(scope, inst.base.src, new_decl); | ||
| 1291 | } | ||
| 1292 | |||
| 1293 | fn zirErrorValue(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1294 | const tracy = trace(@src()); | ||
| 1295 | defer tracy.end(); | ||
| 1296 | |||
| 1297 | // Create an anonymous error set type with only this error value, and return the value. | ||
| 1298 | const entry = try mod.getErrorValue(inst.positionals.name); | ||
| 1299 | const result_type = try Type.Tag.error_set_single.create(block.arena, entry.key); | ||
| 1300 | return sema.mod.constInst(scope, inst.base.src, .{ | ||
| 1301 | .ty = result_type, | ||
| 1302 | .val = try Value.Tag.@"error".create(block.arena, .{ | ||
| 1303 | .name = entry.key, | ||
| 1304 | }), | ||
| 1305 | }); | ||
| 1306 | } | ||
| 1307 | |||
| 1308 | fn zirMergeErrorSets(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1309 | const tracy = trace(@src()); | ||
| 1310 | defer tracy.end(); | ||
| 1311 | |||
| 1312 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1313 | const lhs_ty = try sema.resolveType(block, bin_inst.lhs); | ||
| 1314 | const rhs_ty = try sema.resolveType(block, bin_inst.rhs); | ||
| 1315 | if (rhs_ty.zigTypeTag() != .ErrorSet) | ||
| 1316 | return sema.mod.fail(&block.base, inst.positionals.rhs.src, "expected error set type, found {}", .{rhs_ty}); | ||
| 1317 | if (lhs_ty.zigTypeTag() != .ErrorSet) | ||
| 1318 | return sema.mod.fail(&block.base, inst.positionals.lhs.src, "expected error set type, found {}", .{lhs_ty}); | ||
| 1319 | |||
| 1320 | // anything merged with anyerror is anyerror | ||
| 1321 | if (lhs_ty.tag() == .anyerror or rhs_ty.tag() == .anyerror) | ||
| 1322 | return sema.mod.constInst(scope, inst.base.src, .{ | ||
| 1323 | .ty = Type.initTag(.type), | ||
| 1324 | .val = Value.initTag(.anyerror_type), | ||
| 1325 | }); | ||
| 1326 | // The declarations arena will store the hashmap. | ||
| 1327 | var new_decl_arena = std.heap.ArenaAllocator.init(mod.gpa); | ||
| 1328 | errdefer new_decl_arena.deinit(); | ||
| 1329 | |||
| 1330 | const payload = try new_decl_arena.allocator.create(Value.Payload.ErrorSet); | ||
| 1331 | payload.* = .{ | ||
| 1332 | .base = .{ .tag = .error_set }, | ||
| 1333 | .data = .{ | ||
| 1334 | .fields = .{}, | ||
| 1335 | .decl = undefined, // populated below | ||
| 1336 | }, | ||
| 1337 | }; | ||
| 1338 | try payload.data.fields.ensureCapacity(&new_decl_arena.allocator, @intCast(u32, switch (rhs_ty.tag()) { | ||
| 1339 | .error_set_single => 1, | ||
| 1340 | .error_set => rhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields.size, | ||
| 1341 | else => unreachable, | ||
| 1342 | } + switch (lhs_ty.tag()) { | ||
| 1343 | .error_set_single => 1, | ||
| 1344 | .error_set => lhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields.size, | ||
| 1345 | else => unreachable, | ||
| 1346 | })); | ||
| 1347 | |||
| 1348 | switch (lhs_ty.tag()) { | ||
| 1349 | .error_set_single => { | ||
| 1350 | const name = lhs_ty.castTag(.error_set_single).?.data; | ||
| 1351 | payload.data.fields.putAssumeCapacity(name, {}); | ||
| 1352 | }, | ||
| 1353 | .error_set => { | ||
| 1354 | var multiple = lhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields; | ||
| 1355 | var it = multiple.iterator(); | ||
| 1356 | while (it.next()) |entry| { | ||
| 1357 | payload.data.fields.putAssumeCapacity(entry.key, entry.value); | ||
| 1358 | } | ||
| 1359 | }, | ||
| 1360 | else => unreachable, | ||
| 1361 | } | ||
| 1362 | |||
| 1363 | switch (rhs_ty.tag()) { | ||
| 1364 | .error_set_single => { | ||
| 1365 | const name = rhs_ty.castTag(.error_set_single).?.data; | ||
| 1366 | payload.data.fields.putAssumeCapacity(name, {}); | ||
| 1367 | }, | ||
| 1368 | .error_set => { | ||
| 1369 | var multiple = rhs_ty.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields; | ||
| 1370 | var it = multiple.iterator(); | ||
| 1371 | while (it.next()) |entry| { | ||
| 1372 | payload.data.fields.putAssumeCapacity(entry.key, entry.value); | ||
| 1373 | } | ||
| 1374 | }, | ||
| 1375 | else => unreachable, | ||
| 1376 | } | ||
| 1377 | // TODO create name in format "error:line:column" | ||
| 1378 | const new_decl = try mod.createAnonymousDecl(scope, &new_decl_arena, .{ | ||
| 1379 | .ty = Type.initTag(.type), | ||
| 1380 | .val = Value.initPayload(&payload.base), | ||
| 1381 | }); | ||
| 1382 | payload.data.decl = new_decl; | ||
| 1383 | |||
| 1384 | return mod.analyzeDeclVal(scope, inst.base.src, new_decl); | ||
| 1385 | } | ||
| 1386 | |||
| 1387 | fn zirEnumLiteral(sema: *Sema, block: *Scope.Block, zir_inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1388 | const tracy = trace(@src()); | ||
| 1389 | defer tracy.end(); | ||
| 1390 | |||
| 1391 | const duped_name = try block.arena.dupe(u8, inst.positionals.name); | ||
| 1392 | return sema.mod.constInst(scope, inst.base.src, .{ | ||
| 1393 | .ty = Type.initTag(.enum_literal), | ||
| 1394 | .val = try Value.Tag.enum_literal.create(block.arena, duped_name), | ||
| 1395 | }); | ||
| 1396 | } | ||
| 1397 | |||
| 1398 | /// Pointer in, pointer out. | ||
| 1399 | fn zirOptionalPayloadPtr( | ||
| 1400 | sema: *Sema, | ||
| 1401 | block: *Scope.Block, | ||
| 1402 | inst: zir.Inst.Index, | ||
| 1403 | safety_check: bool, | ||
| 1404 | ) InnerError!*Inst { | ||
| 1405 | const tracy = trace(@src()); | ||
| 1406 | defer tracy.end(); | ||
| 1407 | |||
| 1408 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1409 | const optional_ptr = sema.resolveInst(block, inst_data.operand); | ||
| 1410 | assert(optional_ptr.ty.zigTypeTag() == .Pointer); | ||
| 1411 | const src = inst_data.src(); | ||
| 1412 | |||
| 1413 | const opt_type = optional_ptr.ty.elemType(); | ||
| 1414 | if (opt_type.zigTypeTag() != .Optional) { | ||
| 1415 | return sema.mod.fail(&block.base, src, "expected optional type, found {}", .{opt_type}); | ||
| 1416 | } | ||
| 1417 | |||
| 1418 | const child_type = try opt_type.optionalChildAlloc(block.arena); | ||
| 1419 | const child_pointer = try sema.mod.simplePtrType(block.arena, child_type, !optional_ptr.ty.isConstPtr(), .One); | ||
| 1420 | |||
| 1421 | if (optional_ptr.value()) |pointer_val| { | ||
| 1422 | const val = try pointer_val.pointerDeref(block.arena); | ||
| 1423 | if (val.isNull()) { | ||
| 1424 | return sema.mod.fail(&block.base, src, "unable to unwrap null", .{}); | ||
| 1425 | } | ||
| 1426 | // The same Value represents the pointer to the optional and the payload. | ||
| 1427 | return sema.mod.constInst(scope, src, .{ | ||
| 1428 | .ty = child_pointer, | ||
| 1429 | .val = pointer_val, | ||
| 1430 | }); | ||
| 1431 | } | ||
| 1432 | |||
| 1433 | try sema.requireRuntimeBlock(block, src); | ||
| 1434 | if (safety_check and block.wantSafety()) { | ||
| 1435 | const is_non_null = try block.addUnOp(src, Type.initTag(.bool), .is_non_null_ptr, optional_ptr); | ||
| 1436 | try mod.addSafetyCheck(b, is_non_null, .unwrap_null); | ||
| 1437 | } | ||
| 1438 | return block.addUnOp(src, child_pointer, .optional_payload_ptr, optional_ptr); | ||
| 1439 | } | ||
| 1440 | |||
| 1441 | /// Value in, value out. | ||
| 1442 | fn zirOptionalPayload( | ||
| 1443 | sema: *Sema, | ||
| 1444 | block: *Scope.Block, | ||
| 1445 | inst: zir.Inst.Index, | ||
| 1446 | safety_check: bool, | ||
| 1447 | ) InnerError!*Inst { | ||
| 1448 | const tracy = trace(@src()); | ||
| 1449 | defer tracy.end(); | ||
| 1450 | |||
| 1451 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1452 | const src = inst_data.src(); | ||
| 1453 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 1454 | const opt_type = operand.ty; | ||
| 1455 | if (opt_type.zigTypeTag() != .Optional) { | ||
| 1456 | return sema.mod.fail(&block.base, src, "expected optional type, found {}", .{opt_type}); | ||
| 1457 | } | ||
| 1458 | |||
| 1459 | const child_type = try opt_type.optionalChildAlloc(block.arena); | ||
| 1460 | |||
| 1461 | if (operand.value()) |val| { | ||
| 1462 | if (val.isNull()) { | ||
| 1463 | return sema.mod.fail(&block.base, src, "unable to unwrap null", .{}); | ||
| 1464 | } | ||
| 1465 | return sema.mod.constInst(scope, src, .{ | ||
| 1466 | .ty = child_type, | ||
| 1467 | .val = val, | ||
| 1468 | }); | ||
| 1469 | } | ||
| 1470 | |||
| 1471 | try sema.requireRuntimeBlock(block, src); | ||
| 1472 | if (safety_check and block.wantSafety()) { | ||
| 1473 | const is_non_null = try block.addUnOp(src, Type.initTag(.bool), .is_non_null, operand); | ||
| 1474 | try mod.addSafetyCheck(b, is_non_null, .unwrap_null); | ||
| 1475 | } | ||
| 1476 | return block.addUnOp(src, child_type, .optional_payload, operand); | ||
| 1477 | } | ||
| 1478 | |||
| 1479 | /// Value in, value out | ||
| 1480 | fn zirErrUnionPayload( | ||
| 1481 | sema: *Sema, | ||
| 1482 | block: *Scope.Block, | ||
| 1483 | inst: zir.Inst.Index, | ||
| 1484 | safety_check: bool, | ||
| 1485 | ) InnerError!*Inst { | ||
| 1486 | const tracy = trace(@src()); | ||
| 1487 | defer tracy.end(); | ||
| 1488 | |||
| 1489 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1490 | const src = inst_data.src(); | ||
| 1491 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 1492 | if (operand.ty.zigTypeTag() != .ErrorUnion) | ||
| 1493 | return sema.mod.fail(&block.base, operand.src, "expected error union type, found '{}'", .{operand.ty}); | ||
| 1494 | |||
| 1495 | if (operand.value()) |val| { | ||
| 1496 | if (val.getError()) |name| { | ||
| 1497 | return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name}); | ||
| 1498 | } | ||
| 1499 | const data = val.castTag(.error_union).?.data; | ||
| 1500 | return sema.mod.constInst(scope, src, .{ | ||
| 1501 | .ty = operand.ty.castTag(.error_union).?.data.payload, | ||
| 1502 | .val = data, | ||
| 1503 | }); | ||
| 1504 | } | ||
| 1505 | try sema.requireRuntimeBlock(block, src); | ||
| 1506 | if (safety_check and block.wantSafety()) { | ||
| 1507 | const is_non_err = try block.addUnOp(src, Type.initTag(.bool), .is_err, operand); | ||
| 1508 | try mod.addSafetyCheck(b, is_non_err, .unwrap_errunion); | ||
| 1509 | } | ||
| 1510 | return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_payload, operand); | ||
| 1511 | } | ||
| 1512 | |||
| 1513 | /// Pointer in, pointer out. | ||
| 1514 | fn zirErrUnionPayloadPtr( | ||
| 1515 | sema: *Sema, | ||
| 1516 | block: *Scope.Block, | ||
| 1517 | inst: zir.Inst.Index, | ||
| 1518 | safety_check: bool, | ||
| 1519 | ) InnerError!*Inst { | ||
| 1520 | const tracy = trace(@src()); | ||
| 1521 | defer tracy.end(); | ||
| 1522 | |||
| 1523 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1524 | const src = inst_data.src(); | ||
| 1525 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 1526 | assert(operand.ty.zigTypeTag() == .Pointer); | ||
| 1527 | |||
| 1528 | if (operand.ty.elemType().zigTypeTag() != .ErrorUnion) | ||
| 1529 | return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()}); | ||
| 1530 | |||
| 1531 | const operand_pointer_ty = try sema.mod.simplePtrType(block.arena, operand.ty.elemType().castTag(.error_union).?.data.payload, !operand.ty.isConstPtr(), .One); | ||
| 1532 | |||
| 1533 | if (operand.value()) |pointer_val| { | ||
| 1534 | const val = try pointer_val.pointerDeref(block.arena); | ||
| 1535 | if (val.getError()) |name| { | ||
| 1536 | return sema.mod.fail(&block.base, src, "caught unexpected error '{s}'", .{name}); | ||
| 1537 | } | ||
| 1538 | const data = val.castTag(.error_union).?.data; | ||
| 1539 | // The same Value represents the pointer to the error union and the payload. | ||
| 1540 | return sema.mod.constInst(scope, src, .{ | ||
| 1541 | .ty = operand_pointer_ty, | ||
| 1542 | .val = try Value.Tag.ref_val.create( | ||
| 1543 | block.arena, | ||
| 1544 | data, | ||
| 1545 | ), | ||
| 1546 | }); | ||
| 1547 | } | ||
| 1548 | |||
| 1549 | try sema.requireRuntimeBlock(block, src); | ||
| 1550 | if (safety_check and block.wantSafety()) { | ||
| 1551 | const is_non_err = try block.addUnOp(src, Type.initTag(.bool), .is_err, operand); | ||
| 1552 | try mod.addSafetyCheck(b, is_non_err, .unwrap_errunion); | ||
| 1553 | } | ||
| 1554 | return block.addUnOp(src, operand_pointer_ty, .unwrap_errunion_payload_ptr, operand); | ||
| 1555 | } | ||
| 1556 | |||
| 1557 | /// Value in, value out | ||
| 1558 | fn zirErrUnionCode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1559 | const tracy = trace(@src()); | ||
| 1560 | defer tracy.end(); | ||
| 1561 | |||
| 1562 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1563 | const src = inst_data.src(); | ||
| 1564 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 1565 | if (operand.ty.zigTypeTag() != .ErrorUnion) | ||
| 1566 | return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty}); | ||
| 1567 | |||
| 1568 | if (operand.value()) |val| { | ||
| 1569 | assert(val.getError() != null); | ||
| 1570 | const data = val.castTag(.error_union).?.data; | ||
| 1571 | return sema.mod.constInst(scope, src, .{ | ||
| 1572 | .ty = operand.ty.castTag(.error_union).?.data.error_set, | ||
| 1573 | .val = data, | ||
| 1574 | }); | ||
| 1575 | } | ||
| 1576 | |||
| 1577 | try sema.requireRuntimeBlock(block, src); | ||
| 1578 | return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err, operand); | ||
| 1579 | } | ||
| 1580 | |||
| 1581 | /// Pointer in, value out | ||
| 1582 | fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1583 | const tracy = trace(@src()); | ||
| 1584 | defer tracy.end(); | ||
| 1585 | |||
| 1586 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1587 | const src = inst_data.src(); | ||
| 1588 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 1589 | assert(operand.ty.zigTypeTag() == .Pointer); | ||
| 1590 | |||
| 1591 | if (operand.ty.elemType().zigTypeTag() != .ErrorUnion) | ||
| 1592 | return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()}); | ||
| 1593 | |||
| 1594 | if (operand.value()) |pointer_val| { | ||
| 1595 | const val = try pointer_val.pointerDeref(block.arena); | ||
| 1596 | assert(val.getError() != null); | ||
| 1597 | const data = val.castTag(.error_union).?.data; | ||
| 1598 | return sema.mod.constInst(scope, src, .{ | ||
| 1599 | .ty = operand.ty.elemType().castTag(.error_union).?.data.error_set, | ||
| 1600 | .val = data, | ||
| 1601 | }); | ||
| 1602 | } | ||
| 1603 | |||
| 1604 | try sema.requireRuntimeBlock(block, src); | ||
| 1605 | return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err_ptr, operand); | ||
| 1606 | } | ||
| 1607 | |||
| 1608 | fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1609 | const tracy = trace(@src()); | ||
| 1610 | defer tracy.end(); | ||
| 1611 | |||
| 1612 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 1613 | const src = inst_data.src(); | ||
| 1614 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 1615 | if (operand.ty.zigTypeTag() != .ErrorUnion) | ||
| 1616 | return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty}); | ||
| 1617 | if (operand.ty.castTag(.error_union).?.data.payload.zigTypeTag() != .Void) { | ||
| 1618 | return sema.mod.fail(&block.base, src, "expression value is ignored", .{}); | ||
| 1619 | } | ||
| 1620 | return sema.mod.constVoid(block.arena, .unneeded); | ||
| 1621 | } | ||
| 1622 | |||
| 1623 | fn zirFnType(sema: *Sema, block: *Scope.Block, fntype: zir.Inst.Index, var_args: bool) InnerError!*Inst { | ||
| 1624 | const tracy = trace(@src()); | ||
| 1625 | defer tracy.end(); | ||
| 1626 | |||
| 1627 | return fnTypeCommon( | ||
| 1628 | mod, | ||
| 1629 | scope, | ||
| 1630 | &fntype.base, | ||
| 1631 | fntype.positionals.param_types, | ||
| 1632 | fntype.positionals.return_type, | ||
| 1633 | .Unspecified, | ||
| 1634 | var_args, | ||
| 1635 | ); | ||
| 1636 | } | ||
| 1637 | |||
| 1638 | fn zirFnTypeCc(sema: *Sema, block: *Scope.Block, fntype: zir.Inst.Index, var_args: bool) InnerError!*Inst { | ||
| 1639 | const tracy = trace(@src()); | ||
| 1640 | defer tracy.end(); | ||
| 1641 | |||
| 1642 | const cc_tv = try resolveInstConst(mod, scope, fntype.positionals.cc); | ||
| 1643 | // TODO once we're capable of importing and analyzing decls from | ||
| 1644 | // std.builtin, this needs to change | ||
| 1645 | const cc_str = cc_tv.val.castTag(.enum_literal).?.data; | ||
| 1646 | const cc = std.meta.stringToEnum(std.builtin.CallingConvention, cc_str) orelse | ||
| 1647 | return sema.mod.fail(&block.base, fntype.positionals.cc.src, "Unknown calling convention {s}", .{cc_str}); | ||
| 1648 | return fnTypeCommon( | ||
| 1649 | mod, | ||
| 1650 | scope, | ||
| 1651 | &fntype.base, | ||
| 1652 | fntype.positionals.param_types, | ||
| 1653 | fntype.positionals.return_type, | ||
| 1654 | cc, | ||
| 1655 | var_args, | ||
| 1656 | ); | ||
| 1657 | } | ||
| 1658 | |||
| 1659 | fn fnTypeCommon( | ||
| 1660 | sema: *Sema, | ||
| 1661 | block: *Scope.Block, | ||
| 1662 | zir_inst: zir.Inst.Index, | ||
| 1663 | zir_param_types: []zir.Inst.Index, | ||
| 1664 | zir_return_type: zir.Inst.Index, | ||
| 1665 | cc: std.builtin.CallingConvention, | ||
| 1666 | var_args: bool, | ||
| 1667 | ) InnerError!*Inst { | ||
| 1668 | const return_type = try sema.resolveType(block, zir_return_type); | ||
| 1669 | |||
| 1670 | // Hot path for some common function types. | ||
| 1671 | if (zir_param_types.len == 0 and !var_args) { | ||
| 1672 | if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { | ||
| 1673 | return sema.mod.constType(block.arena, zir_inst.src, Type.initTag(.fn_noreturn_no_args)); | ||
| 1674 | } | ||
| 1675 | |||
| 1676 | if (return_type.zigTypeTag() == .Void and cc == .Unspecified) { | ||
| 1677 | return sema.mod.constType(block.arena, zir_inst.src, Type.initTag(.fn_void_no_args)); | ||
| 1678 | } | ||
| 1679 | |||
| 1680 | if (return_type.zigTypeTag() == .NoReturn and cc == .Naked) { | ||
| 1681 | return sema.mod.constType(block.arena, zir_inst.src, Type.initTag(.fn_naked_noreturn_no_args)); | ||
| 1682 | } | ||
| 1683 | |||
| 1684 | if (return_type.zigTypeTag() == .Void and cc == .C) { | ||
| 1685 | return sema.mod.constType(block.arena, zir_inst.src, Type.initTag(.fn_ccc_void_no_args)); | ||
| 1686 | } | ||
| 1687 | } | ||
| 1688 | |||
| 1689 | const param_types = try block.arena.alloc(Type, zir_param_types.len); | ||
| 1690 | for (zir_param_types) |param_type, i| { | ||
| 1691 | const resolved = try sema.resolveType(block, param_type); | ||
| 1692 | // TODO skip for comptime params | ||
| 1693 | if (!resolved.isValidVarType(false)) { | ||
| 1694 | return sema.mod.fail(&block.base, param_type.src, "parameter of type '{}' must be declared comptime", .{resolved}); | ||
| 1695 | } | ||
| 1696 | param_types[i] = resolved; | ||
| 1697 | } | ||
| 1698 | |||
| 1699 | const fn_ty = try Type.Tag.function.create(block.arena, .{ | ||
| 1700 | .param_types = param_types, | ||
| 1701 | .return_type = return_type, | ||
| 1702 | .cc = cc, | ||
| 1703 | .is_var_args = var_args, | ||
| 1704 | }); | ||
| 1705 | return sema.mod.constType(block.arena, zir_inst.src, fn_ty); | ||
| 1706 | } | ||
| 1707 | |||
| 1708 | fn zirAs(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1709 | const tracy = trace(@src()); | ||
| 1710 | defer tracy.end(); | ||
| 1711 | |||
| 1712 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1713 | const dest_type = try sema.resolveType(block, bin_inst.lhs); | ||
| 1714 | const tzir_inst = sema.resolveInst(block, bin_inst.rhs); | ||
| 1715 | return sema.coerce(scope, dest_type, tzir_inst); | ||
| 1716 | } | ||
| 1717 | |||
| 1718 | fn zirPtrtoint(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1719 | const tracy = trace(@src()); | ||
| 1720 | defer tracy.end(); | ||
| 1721 | |||
| 1722 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 1723 | const ptr = sema.resolveInst(block, inst_data.operand); | ||
| 1724 | if (ptr.ty.zigTypeTag() != .Pointer) { | ||
| 1725 | const ptr_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | ||
| 1726 | return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr.ty}); | ||
| 1727 | } | ||
| 1728 | // TODO handle known-pointer-address | ||
| 1729 | const src = inst_data.src(); | ||
| 1730 | try sema.requireRuntimeBlock(block, src); | ||
| 1731 | const ty = Type.initTag(.usize); | ||
| 1732 | return block.addUnOp(src, ty, .ptrtoint, ptr); | ||
| 1733 | } | ||
| 1734 | |||
| 1735 | fn zirFieldVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1736 | const tracy = trace(@src()); | ||
| 1737 | defer tracy.end(); | ||
| 1738 | |||
| 1739 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1740 | const src = inst_data.src(); | ||
| 1741 | const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; | ||
| 1742 | const extra = sema.code.extraData(zir.Inst.Field, inst_data.payload_index).data; | ||
| 1743 | const field_name = sema.code.string_bytes[extra.field_name_start..][0..extra.field_name_len]; | ||
| 1744 | const object = sema.resolveInst(block, extra.lhs); | ||
| 1745 | const object_ptr = try sema.analyzeRef(block, src, object); | ||
| 1746 | const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src); | ||
| 1747 | return sema.analyzeDeref(block, src, result_ptr, result_ptr.src); | ||
| 1748 | } | ||
| 1749 | |||
| 1750 | fn zirFieldPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1751 | const tracy = trace(@src()); | ||
| 1752 | defer tracy.end(); | ||
| 1753 | |||
| 1754 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1755 | const src = inst_data.src(); | ||
| 1756 | const field_name_src: LazySrcLoc = .{ .node_offset_field_name = inst_data.src_node }; | ||
| 1757 | const extra = sema.code.extraData(zir.Inst.Field, inst_data.payload_index).data; | ||
| 1758 | const field_name = sema.code.string_bytes[extra.field_name_start..][0..extra.field_name_len]; | ||
| 1759 | const object_ptr = sema.resolveInst(block, extra.lhs); | ||
| 1760 | return sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src); | ||
| 1761 | } | ||
| 1762 | |||
| 1763 | fn zirFieldValNamed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1764 | const tracy = trace(@src()); | ||
| 1765 | defer tracy.end(); | ||
| 1766 | |||
| 1767 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1768 | const src = inst_data.src(); | ||
| 1769 | const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | ||
| 1770 | const extra = sema.code.extraData(zir.Inst.FieldNamed, inst_data.payload_index).data; | ||
| 1771 | const object = sema.resolveInst(block, extra.lhs); | ||
| 1772 | const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); | ||
| 1773 | const object_ptr = try sema.analyzeRef(block, src, object); | ||
| 1774 | const result_ptr = try sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src); | ||
| 1775 | return sema.analyzeDeref(block, src, result_ptr, src); | ||
| 1776 | } | ||
| 1777 | |||
| 1778 | fn zirFieldPtrNamed(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1779 | const tracy = trace(@src()); | ||
| 1780 | defer tracy.end(); | ||
| 1781 | |||
| 1782 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1783 | const src = inst_data.src(); | ||
| 1784 | const field_name_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; | ||
| 1785 | const extra = sema.code.extraData(zir.Inst.FieldNamed, inst_data.payload_index).data; | ||
| 1786 | const object_ptr = sema.resolveInst(block, extra.lhs); | ||
| 1787 | const field_name = try sema.resolveConstString(block, field_name_src, extra.field_name); | ||
| 1788 | return sema.namedFieldPtr(block, src, object_ptr, field_name, field_name_src); | ||
| 1789 | } | ||
| 1790 | |||
| 1791 | fn zirIntcast(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1792 | const tracy = trace(@src()); | ||
| 1793 | defer tracy.end(); | ||
| 1794 | |||
| 1795 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1796 | const dest_type = try sema.resolveType(block, bin_inst.lhs); | ||
| 1797 | const operand = sema.resolveInst(bin_inst.rhs); | ||
| 1798 | |||
| 1799 | const dest_is_comptime_int = switch (dest_type.zigTypeTag()) { | ||
| 1800 | .ComptimeInt => true, | ||
| 1801 | .Int => false, | ||
| 1802 | else => return mod.fail( | ||
| 1803 | scope, | ||
| 1804 | inst.positionals.lhs.src, | ||
| 1805 | "expected integer type, found '{}'", | ||
| 1806 | .{ | ||
| 1807 | dest_type, | ||
| 1808 | }, | ||
| 1809 | ), | ||
| 1810 | }; | ||
| 1811 | |||
| 1812 | switch (operand.ty.zigTypeTag()) { | ||
| 1813 | .ComptimeInt, .Int => {}, | ||
| 1814 | else => return mod.fail( | ||
| 1815 | scope, | ||
| 1816 | inst.positionals.rhs.src, | ||
| 1817 | "expected integer type, found '{}'", | ||
| 1818 | .{operand.ty}, | ||
| 1819 | ), | ||
| 1820 | } | ||
| 1821 | |||
| 1822 | if (operand.value() != null) { | ||
| 1823 | return sema.coerce(scope, dest_type, operand); | ||
| 1824 | } else if (dest_is_comptime_int) { | ||
| 1825 | return sema.mod.fail(&block.base, inst.base.src, "unable to cast runtime value to 'comptime_int'", .{}); | ||
| 1826 | } | ||
| 1827 | |||
| 1828 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement analyze widen or shorten int", .{}); | ||
| 1829 | } | ||
| 1830 | |||
| 1831 | fn zirBitcast(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1832 | const tracy = trace(@src()); | ||
| 1833 | defer tracy.end(); | ||
| 1834 | |||
| 1835 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1836 | const dest_type = try sema.resolveType(block, bin_inst.lhs); | ||
| 1837 | const operand = sema.resolveInst(bin_inst.rhs); | ||
| 1838 | return mod.bitcast(scope, dest_type, operand); | ||
| 1839 | } | ||
| 1840 | |||
| 1841 | fn zirFloatcast(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1842 | const tracy = trace(@src()); | ||
| 1843 | defer tracy.end(); | ||
| 1844 | |||
| 1845 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1846 | const dest_type = try sema.resolveType(block, bin_inst.lhs); | ||
| 1847 | const operand = sema.resolveInst(bin_inst.rhs); | ||
| 1848 | |||
| 1849 | const dest_is_comptime_float = switch (dest_type.zigTypeTag()) { | ||
| 1850 | .ComptimeFloat => true, | ||
| 1851 | .Float => false, | ||
| 1852 | else => return mod.fail( | ||
| 1853 | scope, | ||
| 1854 | inst.positionals.lhs.src, | ||
| 1855 | "expected float type, found '{}'", | ||
| 1856 | .{ | ||
| 1857 | dest_type, | ||
| 1858 | }, | ||
| 1859 | ), | ||
| 1860 | }; | ||
| 1861 | |||
| 1862 | switch (operand.ty.zigTypeTag()) { | ||
| 1863 | .ComptimeFloat, .Float, .ComptimeInt => {}, | ||
| 1864 | else => return mod.fail( | ||
| 1865 | scope, | ||
| 1866 | inst.positionals.rhs.src, | ||
| 1867 | "expected float type, found '{}'", | ||
| 1868 | .{operand.ty}, | ||
| 1869 | ), | ||
| 1870 | } | ||
| 1871 | |||
| 1872 | if (operand.value() != null) { | ||
| 1873 | return sema.coerce(scope, dest_type, operand); | ||
| 1874 | } else if (dest_is_comptime_float) { | ||
| 1875 | return sema.mod.fail(&block.base, inst.base.src, "unable to cast runtime value to 'comptime_float'", .{}); | ||
| 1876 | } | ||
| 1877 | |||
| 1878 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement analyze widen or shorten float", .{}); | ||
| 1879 | } | ||
| 1880 | |||
| 1881 | fn zirElemVal(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1882 | const tracy = trace(@src()); | ||
| 1883 | defer tracy.end(); | ||
| 1884 | |||
| 1885 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1886 | const array = sema.resolveInst(block, bin_inst.lhs); | ||
| 1887 | const array_ptr = try sema.analyzeRef(block, sema.src, array); | ||
| 1888 | const elem_index = sema.resolveInst(block, bin_inst.rhs); | ||
| 1889 | const result_ptr = try sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src); | ||
| 1890 | return sema.analyzeDeref(block, sema.src, result_ptr, sema.src); | ||
| 1891 | } | ||
| 1892 | |||
| 1893 | fn zirElemValNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1894 | const tracy = trace(@src()); | ||
| 1895 | defer tracy.end(); | ||
| 1896 | |||
| 1897 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1898 | const src = inst_data.src(); | ||
| 1899 | const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node }; | ||
| 1900 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; | ||
| 1901 | const array = sema.resolveInst(block, extra.lhs); | ||
| 1902 | const array_ptr = try sema.analyzeRef(block, src, array); | ||
| 1903 | const elem_index = sema.resolveInst(block, extra.rhs); | ||
| 1904 | const result_ptr = try sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src); | ||
| 1905 | return sema.analyzeDeref(block, src, result_ptr, src); | ||
| 1906 | } | ||
| 1907 | |||
| 1908 | fn zirElemPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1909 | const tracy = trace(@src()); | ||
| 1910 | defer tracy.end(); | ||
| 1911 | |||
| 1912 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1913 | const array_ptr = sema.resolveInst(block, bin_inst.lhs); | ||
| 1914 | const elem_index = sema.resolveInst(block, bin_inst.rhs); | ||
| 1915 | return sema.elemPtr(block, sema.src, array_ptr, elem_index, sema.src); | ||
| 1916 | } | ||
| 1917 | |||
| 1918 | fn zirElemPtrNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1919 | const tracy = trace(@src()); | ||
| 1920 | defer tracy.end(); | ||
| 1921 | |||
| 1922 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1923 | const src = inst_data.src(); | ||
| 1924 | const elem_index_src: LazySrcLoc = .{ .node_offset_array_access_index = inst_data.src_node }; | ||
| 1925 | const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data; | ||
| 1926 | const array_ptr = sema.resolveInst(block, extra.lhs); | ||
| 1927 | const elem_index = sema.resolveInst(block, extra.rhs); | ||
| 1928 | return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src); | ||
| 1929 | } | ||
| 1930 | |||
| 1931 | fn zirSliceStart(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1932 | const tracy = trace(@src()); | ||
| 1933 | defer tracy.end(); | ||
| 1934 | |||
| 1935 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1936 | const src = inst_data.src(); | ||
| 1937 | const extra = sema.code.extraData(zir.Inst.SliceStart, inst_data.payload_index).data; | ||
| 1938 | const array_ptr = sema.resolveInst(extra.lhs); | ||
| 1939 | const start = sema.resolveInst(extra.start); | ||
| 1940 | |||
| 1941 | return sema.analyzeSlice(block, src, array_ptr, start, null, null, .unneeded); | ||
| 1942 | } | ||
| 1943 | |||
| 1944 | fn zirSliceEnd(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1945 | const tracy = trace(@src()); | ||
| 1946 | defer tracy.end(); | ||
| 1947 | |||
| 1948 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1949 | const src = inst_data.src(); | ||
| 1950 | const extra = sema.code.extraData(zir.Inst.SliceEnd, inst_data.payload_index).data; | ||
| 1951 | const array_ptr = sema.resolveInst(extra.lhs); | ||
| 1952 | const start = sema.resolveInst(extra.start); | ||
| 1953 | const end = sema.resolveInst(extra.end); | ||
| 1954 | |||
| 1955 | return sema.analyzeSlice(block, src, array_ptr, start, end, null, .unneeded); | ||
| 1956 | } | ||
| 1957 | |||
| 1958 | fn zirSliceSentinel(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1959 | const tracy = trace(@src()); | ||
| 1960 | defer tracy.end(); | ||
| 1961 | |||
| 1962 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 1963 | const src = inst_data.src(); | ||
| 1964 | const sentinel_src: LazySrcLoc = .{ .node_offset_slice_sentinel = inst_data.src_node }; | ||
| 1965 | const extra = sema.code.extraData(zir.Inst.SliceSentinel, inst_data.payload_index).data; | ||
| 1966 | const array_ptr = sema.resolveInst(extra.lhs); | ||
| 1967 | const start = sema.resolveInst(extra.start); | ||
| 1968 | const end = sema.resolveInst(extra.end); | ||
| 1969 | const sentinel = sema.resolveInst(extra.sentinel); | ||
| 1970 | |||
| 1971 | return sema.analyzeSlice(block, inst.base.src, array_ptr, start, end, sentinel, sentinel_src); | ||
| 1972 | } | ||
| 1973 | |||
| 1974 | fn zirSwitchRange(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 1975 | const tracy = trace(@src()); | ||
| 1976 | defer tracy.end(); | ||
| 1977 | |||
| 1978 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 1979 | const start = sema.resolveInst(bin_inst.lhs); | ||
| 1980 | const end = sema.resolveInst(bin_inst.rhs); | ||
| 1981 | |||
| 1982 | switch (start.ty.zigTypeTag()) { | ||
| 1983 | .Int, .ComptimeInt => {}, | ||
| 1984 | else => return sema.mod.constVoid(block.arena, .unneeded), | ||
| 1985 | } | ||
| 1986 | switch (end.ty.zigTypeTag()) { | ||
| 1987 | .Int, .ComptimeInt => {}, | ||
| 1988 | else => return sema.mod.constVoid(block.arena, .unneeded), | ||
| 1989 | } | ||
| 1990 | // .switch_range must be inside a comptime scope | ||
| 1991 | const start_val = start.value().?; | ||
| 1992 | const end_val = end.value().?; | ||
| 1993 | if (start_val.compare(.gte, end_val)) { | ||
| 1994 | return sema.mod.fail(&block.base, inst.base.src, "range start value must be smaller than the end value", .{}); | ||
| 1995 | } | ||
| 1996 | return sema.mod.constVoid(block.arena, .unneeded); | ||
| 1997 | } | ||
| 1998 | |||
| 1999 | fn zirSwitchBr( | ||
| 2000 | sema: *Sema, | ||
| 2001 | parent_block: *Scope.Block, | ||
| 2002 | inst: zir.Inst.Index, | ||
| 2003 | ref: bool, | ||
| 2004 | ) InnerError!*Inst { | ||
| 2005 | const tracy = trace(@src()); | ||
| 2006 | defer tracy.end(); | ||
| 2007 | |||
| 2008 | if (true) @panic("TODO rework with zir-memory-layout in mind"); | ||
| 2009 | |||
| 2010 | const target_ptr = sema.resolveInst(block, inst.positionals.target); | ||
| 2011 | const target = if (ref) | ||
| 2012 | try sema.analyzeDeref(block, inst.base.src, target_ptr, inst.positionals.target.src) | ||
| 2013 | else | ||
| 2014 | target_ptr; | ||
| 2015 | try validateSwitch(mod, scope, target, inst); | ||
| 2016 | |||
| 2017 | if (try mod.resolveDefinedValue(scope, target)) |target_val| { | ||
| 2018 | for (inst.positionals.cases) |case| { | ||
| 2019 | const resolved = sema.resolveInst(block, case.item); | ||
| 2020 | const casted = try sema.coerce(scope, target.ty, resolved); | ||
| 2021 | const item = try sema.resolveConstValue(parent_block, case_src, casted); | ||
| 2022 | |||
| 2023 | if (target_val.eql(item)) { | ||
| 2024 | try sema.body(scope.cast(Scope.Block).?, case.body); | ||
| 2025 | return mod.constNoReturn(scope, inst.base.src); | ||
| 2026 | } | ||
| 2027 | } | ||
| 2028 | try sema.body(scope.cast(Scope.Block).?, inst.positionals.else_body); | ||
| 2029 | return mod.constNoReturn(scope, inst.base.src); | ||
| 2030 | } | ||
| 2031 | |||
| 2032 | if (inst.positionals.cases.len == 0) { | ||
| 2033 | // no cases just analyze else_branch | ||
| 2034 | try sema.body(scope.cast(Scope.Block).?, inst.positionals.else_body); | ||
| 2035 | return mod.constNoReturn(scope, inst.base.src); | ||
| 2036 | } | ||
| 2037 | |||
| 2038 | try sema.requireRuntimeBlock(parent_block, inst.base.src); | ||
| 2039 | const cases = try parent_block.arena.alloc(Inst.SwitchBr.Case, inst.positionals.cases.len); | ||
| 2040 | |||
| 2041 | var case_block: Scope.Block = .{ | ||
| 2042 | .parent = parent_block, | ||
| 2043 | .inst_table = parent_block.inst_table, | ||
| 2044 | .func = parent_block.func, | ||
| 2045 | .owner_decl = parent_block.owner_decl, | ||
| 2046 | .src_decl = parent_block.src_decl, | ||
| 2047 | .instructions = .{}, | ||
| 2048 | .arena = parent_block.arena, | ||
| 2049 | .inlining = parent_block.inlining, | ||
| 2050 | .is_comptime = parent_block.is_comptime, | ||
| 2051 | .branch_quota = parent_block.branch_quota, | ||
| 2052 | }; | ||
| 2053 | defer case_block.instructions.deinit(mod.gpa); | ||
| 2054 | |||
| 2055 | for (inst.positionals.cases) |case, i| { | ||
| 2056 | // Reset without freeing. | ||
| 2057 | case_block.instructions.items.len = 0; | ||
| 2058 | |||
| 2059 | const resolved = sema.resolveInst(block, case.item); | ||
| 2060 | const casted = try sema.coerce(scope, target.ty, resolved); | ||
| 2061 | const item = try sema.resolveConstValue(parent_block, case_src, casted); | ||
| 2062 | |||
| 2063 | try sema.body(&case_block, case.body); | ||
| 2064 | |||
| 2065 | cases[i] = .{ | ||
| 2066 | .item = item, | ||
| 2067 | .body = .{ .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items) }, | ||
| 2068 | }; | ||
| 2069 | } | ||
| 2070 | |||
| 2071 | case_block.instructions.items.len = 0; | ||
| 2072 | try sema.body(&case_block, inst.positionals.else_body); | ||
| 2073 | |||
| 2074 | const else_body: ir.Body = .{ | ||
| 2075 | .instructions = try parent_block.arena.dupe(*Inst, case_block.instructions.items), | ||
| 2076 | }; | ||
| 2077 | |||
| 2078 | return mod.addSwitchBr(parent_block, inst.base.src, target, cases, else_body); | ||
| 2079 | } | ||
| 2080 | |||
| 2081 | fn validateSwitch(sema: *Sema, block: *Scope.Block, target: *Inst, inst: zir.Inst.Index) InnerError!void { | ||
| 2082 | // validate usage of '_' prongs | ||
| 2083 | if (inst.positionals.special_prong == .underscore and target.ty.zigTypeTag() != .Enum) { | ||
| 2084 | return sema.mod.fail(&block.base, inst.base.src, "'_' prong only allowed when switching on non-exhaustive enums", .{}); | ||
| 2085 | // TODO notes "'_' prong here" inst.positionals.cases[last].src | ||
| 2086 | } | ||
| 2087 | |||
| 2088 | // check that target type supports ranges | ||
| 2089 | if (inst.positionals.range) |range_inst| { | ||
| 2090 | switch (target.ty.zigTypeTag()) { | ||
| 2091 | .Int, .ComptimeInt => {}, | ||
| 2092 | else => { | ||
| 2093 | return sema.mod.fail(&block.base, target.src, "ranges not allowed when switching on type {}", .{target.ty}); | ||
| 2094 | // TODO notes "range used here" range_inst.src | ||
| 2095 | }, | ||
| 2096 | } | ||
| 2097 | } | ||
| 2098 | |||
| 2099 | // validate for duplicate items/missing else prong | ||
| 2100 | switch (target.ty.zigTypeTag()) { | ||
| 2101 | .Enum => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Enum", .{}), | ||
| 2102 | .ErrorSet => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .ErrorSet", .{}), | ||
| 2103 | .Union => return sema.mod.fail(&block.base, inst.base.src, "TODO validateSwitch .Union", .{}), | ||
| 2104 | .Int, .ComptimeInt => { | ||
| 2105 | var range_set = @import("RangeSet.zig").init(mod.gpa); | ||
| 2106 | defer range_set.deinit(); | ||
| 2107 | |||
| 2108 | for (inst.positionals.items) |item| { | ||
| 2109 | const maybe_src = if (item.castTag(.switch_range)) |range| blk: { | ||
| 2110 | const start_resolved = sema.resolveInst(block, range.positionals.lhs); | ||
| 2111 | const start_casted = try sema.coerce(scope, target.ty, start_resolved); | ||
| 2112 | const end_resolved = sema.resolveInst(block, range.positionals.rhs); | ||
| 2113 | const end_casted = try sema.coerce(scope, target.ty, end_resolved); | ||
| 2114 | |||
| 2115 | break :blk try range_set.add( | ||
| 2116 | try sema.resolveConstValue(block, range_start_src, start_casted), | ||
| 2117 | try sema.resolveConstValue(block, range_end_src, end_casted), | ||
| 2118 | item.src, | ||
| 2119 | ); | ||
| 2120 | } else blk: { | ||
| 2121 | const resolved = sema.resolveInst(block, item); | ||
| 2122 | const casted = try sema.coerce(scope, target.ty, resolved); | ||
| 2123 | const value = try sema.resolveConstValue(block, item_src, casted); | ||
| 2124 | break :blk try range_set.add(value, value, item.src); | ||
| 2125 | }; | ||
| 2126 | |||
| 2127 | if (maybe_src) |previous_src| { | ||
| 2128 | return sema.mod.fail(&block.base, item.src, "duplicate switch value", .{}); | ||
| 2129 | // TODO notes "previous value is here" previous_src | ||
| 2130 | } | ||
| 2131 | } | ||
| 2132 | |||
| 2133 | if (target.ty.zigTypeTag() == .Int) { | ||
| 2134 | var arena = std.heap.ArenaAllocator.init(mod.gpa); | ||
| 2135 | defer arena.deinit(); | ||
| 2136 | |||
| 2137 | const start = try target.ty.minInt(&arena, mod.getTarget()); | ||
| 2138 | const end = try target.ty.maxInt(&arena, mod.getTarget()); | ||
| 2139 | if (try range_set.spans(start, end)) { | ||
| 2140 | if (inst.positionals.special_prong == .@"else") { | ||
| 2141 | return sema.mod.fail(&block.base, inst.base.src, "unreachable else prong, all cases already handled", .{}); | ||
| 2142 | } | ||
| 2143 | return; | ||
| 2144 | } | ||
| 2145 | } | ||
| 2146 | |||
| 2147 | if (inst.positionals.special_prong != .@"else") { | ||
| 2148 | return sema.mod.fail(&block.base, inst.base.src, "switch must handle all possibilities", .{}); | ||
| 2149 | } | ||
| 2150 | }, | ||
| 2151 | .Bool => { | ||
| 2152 | var true_count: u8 = 0; | ||
| 2153 | var false_count: u8 = 0; | ||
| 2154 | for (inst.positionals.items) |item| { | ||
| 2155 | const resolved = sema.resolveInst(block, item); | ||
| 2156 | const casted = try sema.coerce(scope, Type.initTag(.bool), resolved); | ||
| 2157 | if ((try sema.resolveConstValue(block, item_src, casted)).toBool()) { | ||
| 2158 | true_count += 1; | ||
| 2159 | } else { | ||
| 2160 | false_count += 1; | ||
| 2161 | } | ||
| 2162 | |||
| 2163 | if (true_count + false_count > 2) { | ||
| 2164 | return sema.mod.fail(&block.base, item.src, "duplicate switch value", .{}); | ||
| 2165 | } | ||
| 2166 | } | ||
| 2167 | if ((true_count + false_count < 2) and inst.positionals.special_prong != .@"else") { | ||
| 2168 | return sema.mod.fail(&block.base, inst.base.src, "switch must handle all possibilities", .{}); | ||
| 2169 | } | ||
| 2170 | if ((true_count + false_count == 2) and inst.positionals.special_prong == .@"else") { | ||
| 2171 | return sema.mod.fail(&block.base, inst.base.src, "unreachable else prong, all cases already handled", .{}); | ||
| 2172 | } | ||
| 2173 | }, | ||
| 2174 | .EnumLiteral, .Void, .Fn, .Pointer, .Type => { | ||
| 2175 | if (inst.positionals.special_prong != .@"else") { | ||
| 2176 | return sema.mod.fail(&block.base, inst.base.src, "else prong required when switching on type '{}'", .{target.ty}); | ||
| 2177 | } | ||
| 2178 | |||
| 2179 | var seen_values = std.HashMap(Value, usize, Value.hash, Value.eql, std.hash_map.DefaultMaxLoadPercentage).init(mod.gpa); | ||
| 2180 | defer seen_values.deinit(); | ||
| 2181 | |||
| 2182 | for (inst.positionals.items) |item| { | ||
| 2183 | const resolved = sema.resolveInst(block, item); | ||
| 2184 | const casted = try sema.coerce(scope, target.ty, resolved); | ||
| 2185 | const val = try sema.resolveConstValue(block, item_src, casted); | ||
| 2186 | |||
| 2187 | if (try seen_values.fetchPut(val, item.src)) |prev| { | ||
| 2188 | return sema.mod.fail(&block.base, item.src, "duplicate switch value", .{}); | ||
| 2189 | // TODO notes "previous value here" prev.value | ||
| 2190 | } | ||
| 2191 | } | ||
| 2192 | }, | ||
| 2193 | |||
| 2194 | .ErrorUnion, | ||
| 2195 | .NoReturn, | ||
| 2196 | .Array, | ||
| 2197 | .Struct, | ||
| 2198 | .Undefined, | ||
| 2199 | .Null, | ||
| 2200 | .Optional, | ||
| 2201 | .BoundFn, | ||
| 2202 | .Opaque, | ||
| 2203 | .Vector, | ||
| 2204 | .Frame, | ||
| 2205 | .AnyFrame, | ||
| 2206 | .ComptimeFloat, | ||
| 2207 | .Float, | ||
| 2208 | => { | ||
| 2209 | return sema.mod.fail(&block.base, target.src, "invalid switch target type '{}'", .{target.ty}); | ||
| 2210 | }, | ||
| 2211 | } | ||
| 2212 | } | ||
| 2213 | |||
| 2214 | fn zirImport(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2215 | const tracy = trace(@src()); | ||
| 2216 | defer tracy.end(); | ||
| 2217 | |||
| 2218 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 2219 | const src = inst_data.src(); | ||
| 2220 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | ||
| 2221 | const operand = try sema.resolveConstString(block, operand_src, inst_data.operand); | ||
| 2222 | |||
| 2223 | const file_scope = sema.analyzeImport(block, src, operand) catch |err| switch (err) { | ||
| 2224 | error.ImportOutsidePkgPath => { | ||
| 2225 | return sema.mod.fail(&block.base, src, "import of file outside package path: '{s}'", .{operand}); | ||
| 2226 | }, | ||
| 2227 | error.FileNotFound => { | ||
| 2228 | return sema.mod.fail(&block.base, src, "unable to find '{s}'", .{operand}); | ||
| 2229 | }, | ||
| 2230 | else => { | ||
| 2231 | // TODO: make sure this gets retried and not cached | ||
| 2232 | return sema.mod.fail(&block.base, src, "unable to open '{s}': {s}", .{ operand, @errorName(err) }); | ||
| 2233 | }, | ||
| 2234 | }; | ||
| 2235 | return sema.mod.constType(block.arena, src, file_scope.root_container.ty); | ||
| 2236 | } | ||
| 2237 | |||
| 2238 | fn zirShl(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2239 | const tracy = trace(@src()); | ||
| 2240 | defer tracy.end(); | ||
| 2241 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement zirShl", .{}); | ||
| 2242 | } | ||
| 2243 | |||
| 2244 | fn zirShr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2245 | const tracy = trace(@src()); | ||
| 2246 | defer tracy.end(); | ||
| 2247 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement zirShr", .{}); | ||
| 2248 | } | ||
| 2249 | |||
| 2250 | fn zirBitwise(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2251 | const tracy = trace(@src()); | ||
| 2252 | defer tracy.end(); | ||
| 2253 | |||
| 2254 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 2255 | const lhs = sema.resolveInst(bin_inst.lhs); | ||
| 2256 | const rhs = sema.resolveInst(bin_inst.rhs); | ||
| 2257 | |||
| 2258 | const instructions = &[_]*Inst{ lhs, rhs }; | ||
| 2259 | const resolved_type = try sema.resolvePeerTypes(block, instructions); | ||
| 2260 | const casted_lhs = try sema.coerce(scope, resolved_type, lhs); | ||
| 2261 | const casted_rhs = try sema.coerce(scope, resolved_type, rhs); | ||
| 2262 | |||
| 2263 | const scalar_type = if (resolved_type.zigTypeTag() == .Vector) | ||
| 2264 | resolved_type.elemType() | ||
| 2265 | else | ||
| 2266 | resolved_type; | ||
| 2267 | |||
| 2268 | const scalar_tag = scalar_type.zigTypeTag(); | ||
| 2269 | |||
| 2270 | if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) { | ||
| 2271 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { | ||
| 2272 | return sema.mod.fail(&block.base, inst.base.src, "vector length mismatch: {d} and {d}", .{ | ||
| 2273 | lhs.ty.arrayLen(), | ||
| 2274 | rhs.ty.arrayLen(), | ||
| 2275 | }); | ||
| 2276 | } | ||
| 2277 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement support for vectors in zirBitwise", .{}); | ||
| 2278 | } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { | ||
| 2279 | return sema.mod.fail(&block.base, inst.base.src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ | ||
| 2280 | lhs.ty, | ||
| 2281 | rhs.ty, | ||
| 2282 | }); | ||
| 2283 | } | ||
| 2284 | |||
| 2285 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; | ||
| 2286 | |||
| 2287 | if (!is_int) { | ||
| 2288 | return sema.mod.fail(&block.base, inst.base.src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); | ||
| 2289 | } | ||
| 2290 | |||
| 2291 | if (casted_lhs.value()) |lhs_val| { | ||
| 2292 | if (casted_rhs.value()) |rhs_val| { | ||
| 2293 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | ||
| 2294 | return sema.mod.constInst(scope, inst.base.src, .{ | ||
| 2295 | .ty = resolved_type, | ||
| 2296 | .val = Value.initTag(.undef), | ||
| 2297 | }); | ||
| 2298 | } | ||
| 2299 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement comptime bitwise operations", .{}); | ||
| 2300 | } | ||
| 2301 | } | ||
| 2302 | |||
| 2303 | try sema.requireRuntimeBlock(block, inst.base.src); | ||
| 2304 | const ir_tag = switch (inst.base.tag) { | ||
| 2305 | .bit_and => Inst.Tag.bit_and, | ||
| 2306 | .bit_or => Inst.Tag.bit_or, | ||
| 2307 | .xor => Inst.Tag.xor, | ||
| 2308 | else => unreachable, | ||
| 2309 | }; | ||
| 2310 | |||
| 2311 | return mod.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs); | ||
| 2312 | } | ||
| 2313 | |||
| 2314 | fn zirBitNot(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2315 | const tracy = trace(@src()); | ||
| 2316 | defer tracy.end(); | ||
| 2317 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement zirBitNot", .{}); | ||
| 2318 | } | ||
| 2319 | |||
| 2320 | fn zirArrayCat(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2321 | const tracy = trace(@src()); | ||
| 2322 | defer tracy.end(); | ||
| 2323 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement zirArrayCat", .{}); | ||
| 2324 | } | ||
| 2325 | |||
| 2326 | fn zirArrayMul(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2327 | const tracy = trace(@src()); | ||
| 2328 | defer tracy.end(); | ||
| 2329 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement zirArrayMul", .{}); | ||
| 2330 | } | ||
| 2331 | |||
| 2332 | fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2333 | const tracy = trace(@src()); | ||
| 2334 | defer tracy.end(); | ||
| 2335 | |||
| 2336 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 2337 | const lhs = sema.resolveInst(bin_inst.lhs); | ||
| 2338 | const rhs = sema.resolveInst(bin_inst.rhs); | ||
| 2339 | |||
| 2340 | const instructions = &[_]*Inst{ lhs, rhs }; | ||
| 2341 | const resolved_type = try sema.resolvePeerTypes(block, instructions); | ||
| 2342 | const casted_lhs = try sema.coerce(scope, resolved_type, lhs); | ||
| 2343 | const casted_rhs = try sema.coerce(scope, resolved_type, rhs); | ||
| 2344 | |||
| 2345 | const scalar_type = if (resolved_type.zigTypeTag() == .Vector) | ||
| 2346 | resolved_type.elemType() | ||
| 2347 | else | ||
| 2348 | resolved_type; | ||
| 2349 | |||
| 2350 | const scalar_tag = scalar_type.zigTypeTag(); | ||
| 2351 | |||
| 2352 | if (lhs.ty.zigTypeTag() == .Vector and rhs.ty.zigTypeTag() == .Vector) { | ||
| 2353 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { | ||
| 2354 | return sema.mod.fail(&block.base, inst.base.src, "vector length mismatch: {d} and {d}", .{ | ||
| 2355 | lhs.ty.arrayLen(), | ||
| 2356 | rhs.ty.arrayLen(), | ||
| 2357 | }); | ||
| 2358 | } | ||
| 2359 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement support for vectors in zirBinOp", .{}); | ||
| 2360 | } else if (lhs.ty.zigTypeTag() == .Vector or rhs.ty.zigTypeTag() == .Vector) { | ||
| 2361 | return sema.mod.fail(&block.base, inst.base.src, "mixed scalar and vector operands to binary expression: '{}' and '{}'", .{ | ||
| 2362 | lhs.ty, | ||
| 2363 | rhs.ty, | ||
| 2364 | }); | ||
| 2365 | } | ||
| 2366 | |||
| 2367 | const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt; | ||
| 2368 | const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat; | ||
| 2369 | |||
| 2370 | if (!is_int and !(is_float and floatOpAllowed(inst.base.tag))) { | ||
| 2371 | return sema.mod.fail(&block.base, inst.base.src, "invalid operands to binary expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) }); | ||
| 2372 | } | ||
| 2373 | |||
| 2374 | if (casted_lhs.value()) |lhs_val| { | ||
| 2375 | if (casted_rhs.value()) |rhs_val| { | ||
| 2376 | if (lhs_val.isUndef() or rhs_val.isUndef()) { | ||
| 2377 | return sema.mod.constInst(scope, inst.base.src, .{ | ||
| 2378 | .ty = resolved_type, | ||
| 2379 | .val = Value.initTag(.undef), | ||
| 2380 | }); | ||
| 2381 | } | ||
| 2382 | return analyzeInstComptimeOp(mod, scope, scalar_type, inst, lhs_val, rhs_val); | ||
| 2383 | } | ||
| 2384 | } | ||
| 2385 | |||
| 2386 | try sema.requireRuntimeBlock(block, inst.base.src); | ||
| 2387 | const ir_tag: Inst.Tag = switch (inst.base.tag) { | ||
| 2388 | .add => .add, | ||
| 2389 | .addwrap => .addwrap, | ||
| 2390 | .sub => .sub, | ||
| 2391 | .subwrap => .subwrap, | ||
| 2392 | .mul => .mul, | ||
| 2393 | .mulwrap => .mulwrap, | ||
| 2394 | else => return sema.mod.fail(&block.base, inst.base.src, "TODO implement arithmetic for operand '{s}''", .{@tagName(inst.base.tag)}), | ||
| 2395 | }; | ||
| 2396 | |||
| 2397 | return mod.addBinOp(b, inst.base.src, scalar_type, ir_tag, casted_lhs, casted_rhs); | ||
| 2398 | } | ||
| 2399 | |||
| 2400 | /// Analyzes operands that are known at comptime | ||
| 2401 | fn analyzeInstComptimeOp(sema: *Sema, block: *Scope.Block, res_type: Type, inst: zir.Inst.Index, lhs_val: Value, rhs_val: Value) InnerError!*Inst { | ||
| 2402 | // incase rhs is 0, simply return lhs without doing any calculations | ||
| 2403 | // TODO Once division is implemented we should throw an error when dividing by 0. | ||
| 2404 | if (rhs_val.compareWithZero(.eq)) { | ||
| 2405 | return sema.mod.constInst(scope, inst.base.src, .{ | ||
| 2406 | .ty = res_type, | ||
| 2407 | .val = lhs_val, | ||
| 2408 | }); | ||
| 2409 | } | ||
| 2410 | const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt; | ||
| 2411 | |||
| 2412 | const value = switch (inst.base.tag) { | ||
| 2413 | .add => blk: { | ||
| 2414 | const val = if (is_int) | ||
| 2415 | try Module.intAdd(block.arena, lhs_val, rhs_val) | ||
| 2416 | else | ||
| 2417 | try mod.floatAdd(scope, res_type, inst.base.src, lhs_val, rhs_val); | ||
| 2418 | break :blk val; | ||
| 2419 | }, | ||
| 2420 | .sub => blk: { | ||
| 2421 | const val = if (is_int) | ||
| 2422 | try Module.intSub(block.arena, lhs_val, rhs_val) | ||
| 2423 | else | ||
| 2424 | try mod.floatSub(scope, res_type, inst.base.src, lhs_val, rhs_val); | ||
| 2425 | break :blk val; | ||
| 2426 | }, | ||
| 2427 | else => return sema.mod.fail(&block.base, inst.base.src, "TODO Implement arithmetic operand '{s}'", .{@tagName(inst.base.tag)}), | ||
| 2428 | }; | ||
| 2429 | |||
| 2430 | log.debug("{s}({}, {}) result: {}", .{ @tagName(inst.base.tag), lhs_val, rhs_val, value }); | ||
| 2431 | |||
| 2432 | return sema.mod.constInst(scope, inst.base.src, .{ | ||
| 2433 | .ty = res_type, | ||
| 2434 | .val = value, | ||
| 2435 | }); | ||
| 2436 | } | ||
| 2437 | |||
| 2438 | fn zirDeref(sema: *Sema, block: *Scope.Block, deref: zir.Inst.Index) InnerError!*Inst { | ||
| 2439 | const tracy = trace(@src()); | ||
| 2440 | defer tracy.end(); | ||
| 2441 | |||
| 2442 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 2443 | const src = inst_data.src(); | ||
| 2444 | const ptr_src: LazySrcLoc = .{ .node_offset_deref_ptr = inst_data.src_node }; | ||
| 2445 | const ptr = sema.resolveInst(block, inst_data.operand); | ||
| 2446 | return sema.analyzeDeref(block, src, ptr, ptr_src); | ||
| 2447 | } | ||
| 2448 | |||
| 2449 | fn zirAsm( | ||
| 2450 | sema: *Sema, | ||
| 2451 | block: *Scope.Block, | ||
| 2452 | assembly: zir.Inst.Index, | ||
| 2453 | is_volatile: bool, | ||
| 2454 | ) InnerError!*Inst { | ||
| 2455 | const tracy = trace(@src()); | ||
| 2456 | defer tracy.end(); | ||
| 2457 | |||
| 2458 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2459 | const src = inst_data.src(); | ||
| 2460 | const asm_source_src: LazySrcLoc = .{ .node_offset_asm_source = inst_data.src_node }; | ||
| 2461 | const ret_ty_src: LazySrcLoc = .{ .node_offset_asm_ret_ty = inst_data.src_node }; | ||
| 2462 | const extra = sema.code.extraData(zir.Inst.Asm, inst_data.payload_index); | ||
| 2463 | const return_type = try sema.resolveType(block, ret_ty_src, extra.data.return_type); | ||
| 2464 | const asm_source = try sema.resolveConstString(block, asm_source_src, extra.data.asm_source); | ||
| 2465 | |||
| 2466 | var extra_i = extra.end; | ||
| 2467 | const output = if (extra.data.output != 0) blk: { | ||
| 2468 | const name = sema.code.nullTerminatedString(sema.code.extra[extra_i]); | ||
| 2469 | extra_i += 1; | ||
| 2470 | break :blk .{ | ||
| 2471 | .name = name, | ||
| 2472 | .inst = try sema.resolveInst(block, extra.data.output), | ||
| 2473 | }; | ||
| 2474 | } else null; | ||
| 2475 | |||
| 2476 | const args = try block.arena.alloc(*Inst, extra.data.args.len); | ||
| 2477 | const inputs = try block.arena.alloc([]const u8, extra.data.args_len); | ||
| 2478 | const clobbers = try block.arena.alloc([]const u8, extra.data.clobbers_len); | ||
| 2479 | |||
| 2480 | for (args) |*arg| { | ||
| 2481 | const uncasted = sema.resolveInst(block, sema.code.extra[extra_i]); | ||
| 2482 | extra_i += 1; | ||
| 2483 | arg.* = try sema.coerce(block, Type.initTag(.usize), uncasted); | ||
| 2484 | } | ||
| 2485 | for (inputs) |*name| { | ||
| 2486 | name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]); | ||
| 2487 | extra_i += 1; | ||
| 2488 | } | ||
| 2489 | for (clobbers) |*name| { | ||
| 2490 | name.* = sema.code.nullTerminatedString(sema.code.extra[extra_i]); | ||
| 2491 | extra_i += 1; | ||
| 2492 | } | ||
| 2493 | |||
| 2494 | try sema.requireRuntimeBlock(block, src); | ||
| 2495 | const inst = try block.arena.create(Inst.Assembly); | ||
| 2496 | inst.* = .{ | ||
| 2497 | .base = .{ | ||
| 2498 | .tag = .assembly, | ||
| 2499 | .ty = return_type, | ||
| 2500 | .src = src, | ||
| 2501 | }, | ||
| 2502 | .asm_source = asm_source, | ||
| 2503 | .is_volatile = is_volatile, | ||
| 2504 | .output = if (output) |o| o.inst else null, | ||
| 2505 | .output_name = if (output) |o| o.name else null, | ||
| 2506 | .inputs = inputs, | ||
| 2507 | .clobbers = clobbers, | ||
| 2508 | .args = args, | ||
| 2509 | }; | ||
| 2510 | try block.instructions.append(mod.gpa, &inst.base); | ||
| 2511 | return &inst.base; | ||
| 2512 | } | ||
| 2513 | |||
| 2514 | fn zirCmp( | ||
| 2515 | sema: *Sema, | ||
| 2516 | block: *Scope.Block, | ||
| 2517 | inst: zir.Inst.Index, | ||
| 2518 | op: std.math.CompareOperator, | ||
| 2519 | ) InnerError!*Inst { | ||
| 2520 | const tracy = trace(@src()); | ||
| 2521 | defer tracy.end(); | ||
| 2522 | |||
| 2523 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 2524 | const lhs = sema.resolveInst(bin_inst.lhs); | ||
| 2525 | const rhs = sema.resolveInst(bin_inst.rhs); | ||
| 2526 | |||
| 2527 | const is_equality_cmp = switch (op) { | ||
| 2528 | .eq, .neq => true, | ||
| 2529 | else => false, | ||
| 2530 | }; | ||
| 2531 | const lhs_ty_tag = lhs.ty.zigTypeTag(); | ||
| 2532 | const rhs_ty_tag = rhs.ty.zigTypeTag(); | ||
| 2533 | if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) { | ||
| 2534 | // null == null, null != null | ||
| 2535 | return mod.constBool(block.arena, inst.base.src, op == .eq); | ||
| 2536 | } else if (is_equality_cmp and | ||
| 2537 | ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or | ||
| 2538 | rhs_ty_tag == .Null and lhs_ty_tag == .Optional)) | ||
| 2539 | { | ||
| 2540 | // comparing null with optionals | ||
| 2541 | const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs; | ||
| 2542 | return sema.analyzeIsNull(block, inst.base.src, opt_operand, op == .neq); | ||
| 2543 | } else if (is_equality_cmp and | ||
| 2544 | ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr()))) | ||
| 2545 | { | ||
| 2546 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement C pointer cmp", .{}); | ||
| 2547 | } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) { | ||
| 2548 | const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty; | ||
| 2549 | return sema.mod.fail(&block.base, inst.base.src, "comparison of '{}' with null", .{non_null_type}); | ||
| 2550 | } else if (is_equality_cmp and | ||
| 2551 | ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or | ||
| 2552 | (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union))) | ||
| 2553 | { | ||
| 2554 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{}); | ||
| 2555 | } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { | ||
| 2556 | if (!is_equality_cmp) { | ||
| 2557 | return sema.mod.fail(&block.base, inst.base.src, "{s} operator not allowed for errors", .{@tagName(op)}); | ||
| 2558 | } | ||
| 2559 | if (rhs.value()) |rval| { | ||
| 2560 | if (lhs.value()) |lval| { | ||
| 2561 | // TODO optimisation oppurtunity: evaluate if std.mem.eql is faster with the names, or calling to Module.getErrorValue to get the values and then compare them is faster | ||
| 2562 | return mod.constBool(block.arena, inst.base.src, std.mem.eql(u8, lval.castTag(.@"error").?.data.name, rval.castTag(.@"error").?.data.name) == (op == .eq)); | ||
| 2563 | } | ||
| 2564 | } | ||
| 2565 | try sema.requireRuntimeBlock(block, inst.base.src); | ||
| 2566 | return mod.addBinOp(b, inst.base.src, Type.initTag(.bool), if (op == .eq) .cmp_eq else .cmp_neq, lhs, rhs); | ||
| 2567 | } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) { | ||
| 2568 | // This operation allows any combination of integer and float types, regardless of the | ||
| 2569 | // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for | ||
| 2570 | // numeric types. | ||
| 2571 | return mod.cmpNumeric(scope, inst.base.src, lhs, rhs, op); | ||
| 2572 | } else if (lhs_ty_tag == .Type and rhs_ty_tag == .Type) { | ||
| 2573 | if (!is_equality_cmp) { | ||
| 2574 | return sema.mod.fail(&block.base, inst.base.src, "{s} operator not allowed for types", .{@tagName(op)}); | ||
| 2575 | } | ||
| 2576 | return mod.constBool(block.arena, inst.base.src, lhs.value().?.eql(rhs.value().?) == (op == .eq)); | ||
| 2577 | } | ||
| 2578 | return sema.mod.fail(&block.base, inst.base.src, "TODO implement more cmp analysis", .{}); | ||
| 2579 | } | ||
| 2580 | |||
| 2581 | fn zirTypeof(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2582 | const tracy = trace(@src()); | ||
| 2583 | defer tracy.end(); | ||
| 2584 | |||
| 2585 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 2586 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 2587 | return sema.mod.constType(block.arena, inst_data.src(), operand.ty); | ||
| 2588 | } | ||
| 2589 | |||
| 2590 | fn zirTypeofPeer(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2591 | const tracy = trace(@src()); | ||
| 2592 | defer tracy.end(); | ||
| 2593 | |||
| 2594 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | ||
| 2595 | const src = inst_data.src(); | ||
| 2596 | const extra = sema.code.extraData(zir.Inst.MultiOp, inst_data.payload_index); | ||
| 2597 | |||
| 2598 | const inst_list = try mod.gpa.alloc(*ir.Inst, extra.data.operands_len); | ||
| 2599 | defer mod.gpa.free(inst_list); | ||
| 2600 | |||
| 2601 | const src_list = try mod.gpa.alloc(LazySrcLoc, extra.data.operands_len); | ||
| 2602 | defer mod.gpa.free(src_list); | ||
| 2603 | |||
| 2604 | for (sema.code.extra[extra.end..][0..extra.data.operands_len]) |arg_ref, i| { | ||
| 2605 | inst_list[i] = sema.resolveInst(block, arg_ref); | ||
| 2606 | src_list[i] = .{ .node_offset_builtin_call_argn = inst_data.src_node }; | ||
| 2607 | } | ||
| 2608 | |||
| 2609 | const result_type = try sema.resolvePeerTypes(block, inst_list, src_list); | ||
| 2610 | return sema.mod.constType(block.arena, src, result_type); | ||
| 2611 | } | ||
| 2612 | |||
| 2613 | fn zirBoolNot(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2614 | const tracy = trace(@src()); | ||
| 2615 | defer tracy.end(); | ||
| 2616 | |||
| 2617 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 2618 | const src = inst_data.src(); | ||
| 2619 | const uncasted_operand = sema.resolveInst(block, inst_data.operand); | ||
| 2620 | |||
| 2621 | const bool_type = Type.initTag(.bool); | ||
| 2622 | const operand = try sema.coerce(scope, bool_type, uncasted_operand); | ||
| 2623 | if (try mod.resolveDefinedValue(scope, operand)) |val| { | ||
| 2624 | return mod.constBool(block.arena, src, !val.toBool()); | ||
| 2625 | } | ||
| 2626 | try sema.requireRuntimeBlock(block, src); | ||
| 2627 | return block.addUnOp(src, bool_type, .not, operand); | ||
| 2628 | } | ||
| 2629 | |||
| 2630 | fn zirBoolOp( | ||
| 2631 | sema: *Sema, | ||
| 2632 | block: *Scope.Block, | ||
| 2633 | inst: zir.Inst.Index, | ||
| 2634 | comptime is_bool_or: bool, | ||
| 2635 | ) InnerError!*Inst { | ||
| 2636 | const tracy = trace(@src()); | ||
| 2637 | defer tracy.end(); | ||
| 2638 | |||
| 2639 | const bool_type = Type.initTag(.bool); | ||
| 2640 | const bin_inst = sema.code.instructions.items(.data)[inst].bin; | ||
| 2641 | const uncasted_lhs = sema.resolveInst(bin_inst.lhs); | ||
| 2642 | const lhs = try sema.coerce(scope, bool_type, uncasted_lhs); | ||
| 2643 | const uncasted_rhs = sema.resolveInst(bin_inst.rhs); | ||
| 2644 | const rhs = try sema.coerce(scope, bool_type, uncasted_rhs); | ||
| 2645 | |||
| 2646 | if (lhs.value()) |lhs_val| { | ||
| 2647 | if (rhs.value()) |rhs_val| { | ||
| 2648 | if (is_bool_or) { | ||
| 2649 | return mod.constBool(block.arena, inst.base.src, lhs_val.toBool() or rhs_val.toBool()); | ||
| 2650 | } else { | ||
| 2651 | return mod.constBool(block.arena, inst.base.src, lhs_val.toBool() and rhs_val.toBool()); | ||
| 2652 | } | ||
| 2653 | } | ||
| 2654 | } | ||
| 2655 | try sema.requireRuntimeBlock(block, inst.base.src); | ||
| 2656 | const tag: ir.Inst.Tag = if (is_bool_or) .bool_or else .bool_and; | ||
| 2657 | return mod.addBinOp(b, inst.base.src, bool_type, tag, lhs, rhs); | ||
| 2658 | } | ||
| 2659 | |||
| 2660 | fn zirIsNull( | ||
| 2661 | sema: *Sema, | ||
| 2662 | block: *Scope.Block, | ||
| 2663 | inst: zir.Inst.Index, | ||
| 2664 | invert_logic: bool, | ||
| 2665 | ) InnerError!*Inst { | ||
| 2666 | const tracy = trace(@src()); | ||
| 2667 | defer tracy.end(); | ||
| 2668 | |||
| 2669 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 2670 | const src = inst_data.src(); | ||
| 2671 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 2672 | return sema.analyzeIsNull(block, src, operand, invert_logic); | ||
| 2673 | } | ||
| 2674 | |||
| 2675 | fn zirIsNullPtr( | ||
| 2676 | sema: *Sema, | ||
| 2677 | block: *Scope.Block, | ||
| 2678 | inst: zir.Inst.Index, | ||
| 2679 | invert_logic: bool, | ||
| 2680 | ) InnerError!*Inst { | ||
| 2681 | const tracy = trace(@src()); | ||
| 2682 | defer tracy.end(); | ||
| 2683 | |||
| 2684 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 2685 | const src = inst_data.src(); | ||
| 2686 | const ptr = sema.resolveInst(block, inst_data.operand); | ||
| 2687 | const loaded = try sema.analyzeDeref(block, src, ptr, src); | ||
| 2688 | return sema.analyzeIsNull(block, src, loaded, invert_logic); | ||
| 2689 | } | ||
| 2690 | |||
| 2691 | fn zirIsErr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2692 | const tracy = trace(@src()); | ||
| 2693 | defer tracy.end(); | ||
| 2694 | |||
| 2695 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 2696 | const operand = sema.resolveInst(block, inst_data.operand); | ||
| 2697 | return mod.analyzeIsErr(scope, inst_data.src(), operand); | ||
| 2698 | } | ||
| 2699 | |||
| 2700 | fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2701 | const tracy = trace(@src()); | ||
| 2702 | defer tracy.end(); | ||
| 2703 | |||
| 2704 | const inst_data = sema.code.instructions.items(.data)[inst].un_tok; | ||
| 2705 | const src = inst_data.src(); | ||
| 2706 | const ptr = sema.resolveInst(block, inst_data.operand); | ||
| 2707 | const loaded = try sema.analyzeDeref(block, src, ptr, src); | ||
| 2708 | return mod.analyzeIsErr(scope, src, loaded); | ||
| 2709 | } | ||
| 2710 | |||
| 2711 | fn zirCondbr(sema: *Sema, parent_block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2712 | const tracy = trace(@src()); | ||
| 2713 | defer tracy.end(); | ||
| 2714 | |||
| 2715 | const uncasted_cond = sema.resolveInst(block, inst.positionals.condition); | ||
| 2716 | const cond = try sema.coerce(scope, Type.initTag(.bool), uncasted_cond); | ||
| 2717 | |||
| 2718 | if (try mod.resolveDefinedValue(scope, cond)) |cond_val| { | ||
| 2719 | const body = if (cond_val.toBool()) &inst.positionals.then_body else &inst.positionals.else_body; | ||
| 2720 | try sema.body(parent_block, body.*); | ||
| 2721 | return mod.constNoReturn(scope, inst.base.src); | ||
| 2722 | } | ||
| 2723 | |||
| 2724 | var true_block: Scope.Block = .{ | ||
| 2725 | .parent = parent_block, | ||
| 2726 | .inst_table = parent_block.inst_table, | ||
| 2727 | .func = parent_block.func, | ||
| 2728 | .owner_decl = parent_block.owner_decl, | ||
| 2729 | .src_decl = parent_block.src_decl, | ||
| 2730 | .instructions = .{}, | ||
| 2731 | .arena = parent_block.arena, | ||
| 2732 | .inlining = parent_block.inlining, | ||
| 2733 | .is_comptime = parent_block.is_comptime, | ||
| 2734 | .branch_quota = parent_block.branch_quota, | ||
| 2735 | }; | ||
| 2736 | defer true_block.instructions.deinit(mod.gpa); | ||
| 2737 | try sema.body(&true_block, inst.positionals.then_body); | ||
| 2738 | |||
| 2739 | var false_block: Scope.Block = .{ | ||
| 2740 | .parent = parent_block, | ||
| 2741 | .inst_table = parent_block.inst_table, | ||
| 2742 | .func = parent_block.func, | ||
| 2743 | .owner_decl = parent_block.owner_decl, | ||
| 2744 | .src_decl = parent_block.src_decl, | ||
| 2745 | .instructions = .{}, | ||
| 2746 | .arena = parent_block.arena, | ||
| 2747 | .inlining = parent_block.inlining, | ||
| 2748 | .is_comptime = parent_block.is_comptime, | ||
| 2749 | .branch_quota = parent_block.branch_quota, | ||
| 2750 | }; | ||
| 2751 | defer false_block.instructions.deinit(mod.gpa); | ||
| 2752 | try sema.body(&false_block, inst.positionals.else_body); | ||
| 2753 | |||
| 2754 | const then_body: ir.Body = .{ .instructions = try block.arena.dupe(*Inst, true_block.instructions.items) }; | ||
| 2755 | const else_body: ir.Body = .{ .instructions = try block.arena.dupe(*Inst, false_block.instructions.items) }; | ||
| 2756 | return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body); | ||
| 2757 | } | ||
| 2758 | |||
| 2759 | fn zirUnreachable( | ||
| 2760 | sema: *Sema, | ||
| 2761 | block: *Scope.Block, | ||
| 2762 | zir_index: zir.Inst.Index, | ||
| 2763 | safety_check: bool, | ||
| 2764 | ) InnerError!*Inst { | ||
| 2765 | const tracy = trace(@src()); | ||
| 2766 | defer tracy.end(); | ||
| 2767 | |||
| 2768 | try sema.requireRuntimeBlock(block, zir_index.base.src); | ||
| 2769 | // TODO Add compile error for @optimizeFor occurring too late in a scope. | ||
| 2770 | if (safety_check and block.wantSafety()) { | ||
| 2771 | return mod.safetyPanic(b, zir_index.base.src, .unreach); | ||
| 2772 | } else { | ||
| 2773 | return block.addNoOp(zir_index.base.src, Type.initTag(.noreturn), .unreach); | ||
| 2774 | } | ||
| 2775 | } | ||
| 2776 | |||
| 2777 | fn zirRetTok(sema: *Sema, block: *Scope.Block, zir_inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2778 | @compileError("TODO"); | ||
| 2779 | } | ||
| 2780 | |||
| 2781 | fn zirRetNode(sema: *Sema, block: *Scope.Block, zir_inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2782 | @compileError("TODO"); | ||
| 2783 | } | ||
| 2784 | |||
| 2785 | fn floatOpAllowed(tag: zir.Inst.Tag) bool { | ||
| 2786 | // extend this swich as additional operators are implemented | ||
| 2787 | return switch (tag) { | ||
| 2788 | .add, .sub => true, | ||
| 2789 | else => false, | ||
| 2790 | }; | ||
| 2791 | } | ||
| 2792 | |||
| 2793 | fn zirPtrTypeSimple(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2794 | const tracy = trace(@src()); | ||
| 2795 | defer tracy.end(); | ||
| 2796 | |||
| 2797 | const inst_data = sema.code.instructions.items(.data)[inst].ptr_type_simple; | ||
| 2798 | const elem_type = try sema.resolveType(block, .unneeded, inst_data.elem_type); | ||
| 2799 | const ty = try sema.mod.ptrType( | ||
| 2800 | block.arena, | ||
| 2801 | elem_type, | ||
| 2802 | null, | ||
| 2803 | 0, | ||
| 2804 | 0, | ||
| 2805 | 0, | ||
| 2806 | inst_data.is_mutable, | ||
| 2807 | inst_data.is_allowzero, | ||
| 2808 | inst_data.is_volatile, | ||
| 2809 | inst_data.size, | ||
| 2810 | ); | ||
| 2811 | return sema.mod.constType(block.arena, .unneeded, ty); | ||
| 2812 | } | ||
| 2813 | |||
| 2814 | fn zirPtrType(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst { | ||
| 2815 | const tracy = trace(@src()); | ||
| 2816 | defer tracy.end(); | ||
| 2817 | |||
| 2818 | const inst_data = sema.code.instructions.items(.data)[inst].ptr_type; | ||
| 2819 | const extra = sema.code.extraData(zir.Inst.PtrType, inst_data.payload_index); | ||
| 2820 | |||
| 2821 | var extra_i = extra.end; | ||
| 2822 | |||
| 2823 | const sentinel = if (inst_data.flags.has_sentinel) blk: { | ||
| 2824 | const ref = sema.code.extra[extra_i]; | ||
| 2825 | extra_i += 1; | ||
| 2826 | break :blk (try sema.resolveInstConst(block, .unneeded, ref)).val; | ||
| 2827 | } else null; | ||
| 2828 | |||
| 2829 | const abi_align = if (inst_data.flags.has_align) blk: { | ||
| 2830 | const ref = sema.code.extra[extra_i]; | ||
| 2831 | extra_i += 1; | ||
| 2832 | break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u32); | ||
| 2833 | } else 0; | ||
| 2834 | |||
| 2835 | const bit_start = if (inst_data.flags.has_bit_start) blk: { | ||
| 2836 | const ref = sema.code.extra[extra_i]; | ||
| 2837 | extra_i += 1; | ||
| 2838 | break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u16); | ||
| 2839 | } else 0; | ||
| 2840 | |||
| 2841 | const bit_end = if (inst_data.flags.has_bit_end) blk: { | ||
| 2842 | const ref = sema.code.extra[extra_i]; | ||
| 2843 | extra_i += 1; | ||
| 2844 | break :blk try sema.resolveAlreadyCoercedInt(block, .unneeded, ref, u16); | ||
| 2845 | } else 0; | ||
| 2846 | |||
| 2847 | if (bit_end != 0 and bit_offset >= bit_end * 8) | ||
| 2848 | return sema.mod.fail(&block.base, inst.base.src, "bit offset starts after end of host integer", .{}); | ||
| 2849 | |||
| 2850 | const elem_type = try sema.resolveType(block, extra.data.elem_type); | ||
| 2851 | |||
| 2852 | const ty = try mod.ptrType( | ||
| 2853 | scope, | ||
| 2854 | elem_type, | ||
| 2855 | sentinel, | ||
| 2856 | abi_align, | ||
| 2857 | bit_start, | ||
| 2858 | bit_end, | ||
| 2859 | inst_data.flags.is_mutable, | ||
| 2860 | inst_data.flags.is_allowzero, | ||
| 2861 | inst_data.flags.is_volatile, | ||
| 2862 | inst_data.size, | ||
| 2863 | ); | ||
| 2864 | return sema.mod.constType(block.arena, .unneeded, ty); | ||
| 2865 | } | ||
| 2866 | |||
| 2867 | fn requireFunctionBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { | ||
| 2868 | if (sema.func == null) { | ||
| 2869 | return sema.mod.fail(&block.base, src, "instruction illegal outside function body", .{}); | ||
| 2870 | } | ||
| 2871 | } | ||
| 2872 | |||
| 2873 | fn requireRuntimeBlock(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { | ||
| 2874 | try sema.requireFunctionBlock(scope, src); | ||
| 2875 | if (block.is_comptime) { | ||
| 2876 | return sema.mod.fail(&block.base, src, "unable to resolve comptime value", .{}); | ||
| 2877 | } | ||
| 2878 | } | ||
| 2879 | |||
| 2880 | fn validateVarType(sema: *Module, block: *Scope.Block, src: LazySrcLoc, ty: Type) !void { | ||
| 2881 | if (!ty.isValidVarType(false)) { | ||
| 2882 | return mod.fail(&block.base, src, "variable of type '{}' must be const or comptime", .{ty}); | ||
| 2883 | } | ||
| 2884 | } | ||
| 2885 | |||
| 2886 | pub const PanicId = enum { | ||
| 2887 | unreach, | ||
| 2888 | unwrap_null, | ||
| 2889 | unwrap_errunion, | ||
| 2890 | }; | ||
| 2891 | |||
| 2892 | fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void { | ||
| 2893 | const block_inst = try parent_block.arena.create(Inst.Block); | ||
| 2894 | block_inst.* = .{ | ||
| 2895 | .base = .{ | ||
| 2896 | .tag = Inst.Block.base_tag, | ||
| 2897 | .ty = Type.initTag(.void), | ||
| 2898 | .src = ok.src, | ||
| 2899 | }, | ||
| 2900 | .body = .{ | ||
| 2901 | .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the condbr. | ||
| 2902 | }, | ||
| 2903 | }; | ||
| 2904 | |||
| 2905 | const ok_body: ir.Body = .{ | ||
| 2906 | .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the br_void. | ||
| 2907 | }; | ||
| 2908 | const br_void = try parent_block.arena.create(Inst.BrVoid); | ||
| 2909 | br_void.* = .{ | ||
| 2910 | .base = .{ | ||
| 2911 | .tag = .br_void, | ||
| 2912 | .ty = Type.initTag(.noreturn), | ||
| 2913 | .src = ok.src, | ||
| 2914 | }, | ||
| 2915 | .block = block_inst, | ||
| 2916 | }; | ||
| 2917 | ok_body.instructions[0] = &br_void.base; | ||
| 2918 | |||
| 2919 | var fail_block: Scope.Block = .{ | ||
| 2920 | .parent = parent_block, | ||
| 2921 | .inst_map = parent_block.inst_map, | ||
| 2922 | .func = parent_block.func, | ||
| 2923 | .owner_decl = parent_block.owner_decl, | ||
| 2924 | .src_decl = parent_block.src_decl, | ||
| 2925 | .instructions = .{}, | ||
| 2926 | .arena = parent_block.arena, | ||
| 2927 | .inlining = parent_block.inlining, | ||
| 2928 | .is_comptime = parent_block.is_comptime, | ||
| 2929 | .branch_quota = parent_block.branch_quota, | ||
| 2930 | }; | ||
| 2931 | |||
| 2932 | defer fail_block.instructions.deinit(mod.gpa); | ||
| 2933 | |||
| 2934 | _ = try mod.safetyPanic(&fail_block, ok.src, panic_id); | ||
| 2935 | |||
| 2936 | const fail_body: ir.Body = .{ .instructions = try parent_block.arena.dupe(*Inst, fail_block.instructions.items) }; | ||
| 2937 | |||
| 2938 | const condbr = try parent_block.arena.create(Inst.CondBr); | ||
| 2939 | condbr.* = .{ | ||
| 2940 | .base = .{ | ||
| 2941 | .tag = .condbr, | ||
| 2942 | .ty = Type.initTag(.noreturn), | ||
| 2943 | .src = ok.src, | ||
| 2944 | }, | ||
| 2945 | .condition = ok, | ||
| 2946 | .then_body = ok_body, | ||
| 2947 | .else_body = fail_body, | ||
| 2948 | }; | ||
| 2949 | block_inst.body.instructions[0] = &condbr.base; | ||
| 2950 | |||
| 2951 | try parent_block.instructions.append(mod.gpa, &block_inst.base); | ||
| 2952 | } | ||
| 2953 | |||
| 2954 | fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId) !*Inst { | ||
| 2955 | // TODO Once we have a panic function to call, call it here instead of breakpoint. | ||
| 2956 | _ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint); | ||
| 2957 | return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach); | ||
| 2958 | } | ||
| 2959 | |||
| 2960 | fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { | ||
| 2961 | const shared = block.inlining.?.shared; | ||
| 2962 | shared.branch_count += 1; | ||
| 2963 | if (shared.branch_count > block.branch_quota.*) { | ||
| 2964 | // TODO show the "called from here" stack | ||
| 2965 | return mod.fail(&block.base, src, "evaluation exceeded {d} backwards branches", .{ | ||
| 2966 | block.branch_quota.*, | ||
| 2967 | }); | ||
| 2968 | } | ||
| 2969 | } | ||
| 2970 | |||
| 2971 | fn namedFieldPtr( | ||
| 2972 | sema: *Sema, | ||
| 2973 | block: *Scope.Block, | ||
| 2974 | src: LazySrcLoc, | ||
| 2975 | object_ptr: *Inst, | ||
| 2976 | field_name: []const u8, | ||
| 2977 | field_name_src: LazySrcLoc, | ||
| 2978 | ) InnerError!*Inst { | ||
| 2979 | const elem_ty = switch (object_ptr.ty.zigTypeTag()) { | ||
| 2980 | .Pointer => object_ptr.ty.elemType(), | ||
| 2981 | else => return sema.mod.fail(&block.base, object_ptr.src, "expected pointer, found '{}'", .{object_ptr.ty}), | ||
| 2982 | }; | ||
| 2983 | switch (elem_ty.zigTypeTag()) { | ||
| 2984 | .Array => { | ||
| 2985 | if (mem.eql(u8, field_name, "len")) { | ||
| 2986 | return mod.constInst(scope, src, .{ | ||
| 2987 | .ty = Type.initTag(.single_const_pointer_to_comptime_int), | ||
| 2988 | .val = try Value.Tag.ref_val.create( | ||
| 2989 | scope.arena(), | ||
| 2990 | try Value.Tag.int_u64.create(scope.arena(), elem_ty.arrayLen()), | ||
| 2991 | ), | ||
| 2992 | }); | ||
| 2993 | } else { | ||
| 2994 | return mod.fail( | ||
| 2995 | scope, | ||
| 2996 | field_name_src, | ||
| 2997 | "no member named '{s}' in '{}'", | ||
| 2998 | .{ field_name, elem_ty }, | ||
| 2999 | ); | ||
| 3000 | } | ||
| 3001 | }, | ||
| 3002 | .Pointer => { | ||
| 3003 | const ptr_child = elem_ty.elemType(); | ||
| 3004 | switch (ptr_child.zigTypeTag()) { | ||
| 3005 | .Array => { | ||
| 3006 | if (mem.eql(u8, field_name, "len")) { | ||
| 3007 | return mod.constInst(scope, src, .{ | ||
| 3008 | .ty = Type.initTag(.single_const_pointer_to_comptime_int), | ||
| 3009 | .val = try Value.Tag.ref_val.create( | ||
| 3010 | scope.arena(), | ||
| 3011 | try Value.Tag.int_u64.create(scope.arena(), ptr_child.arrayLen()), | ||
| 3012 | ), | ||
| 3013 | }); | ||
| 3014 | } else { | ||
| 3015 | return mod.fail( | ||
| 3016 | scope, | ||
| 3017 | field_name_src, | ||
| 3018 | "no member named '{s}' in '{}'", | ||
| 3019 | .{ field_name, elem_ty }, | ||
| 3020 | ); | ||
| 3021 | } | ||
| 3022 | }, | ||
| 3023 | else => {}, | ||
| 3024 | } | ||
| 3025 | }, | ||
| 3026 | .Type => { | ||
| 3027 | _ = try sema.resolveConstValue(scope, object_ptr.src, object_ptr); | ||
| 3028 | const result = try sema.analyzeDeref(block, src, object_ptr, object_ptr.src); | ||
| 3029 | const val = result.value().?; | ||
| 3030 | const child_type = try val.toType(scope.arena()); | ||
| 3031 | switch (child_type.zigTypeTag()) { | ||
| 3032 | .ErrorSet => { | ||
| 3033 | var name: []const u8 = undefined; | ||
| 3034 | // TODO resolve inferred error sets | ||
| 3035 | if (val.castTag(.error_set)) |payload| | ||
| 3036 | name = (payload.data.fields.getEntry(field_name) orelse return sema.mod.fail(&block.base, src, "no error named '{s}' in '{}'", .{ field_name, child_type })).key | ||
| 3037 | else | ||
| 3038 | name = (try mod.getErrorValue(field_name)).key; | ||
| 3039 | |||
| 3040 | const result_type = if (child_type.tag() == .anyerror) | ||
| 3041 | try Type.Tag.error_set_single.create(scope.arena(), name) | ||
| 3042 | else | ||
| 3043 | child_type; | ||
| 3044 | |||
| 3045 | return mod.constInst(scope, src, .{ | ||
| 3046 | .ty = try mod.simplePtrType(scope.arena(), result_type, false, .One), | ||
| 3047 | .val = try Value.Tag.ref_val.create( | ||
| 3048 | scope.arena(), | ||
| 3049 | try Value.Tag.@"error".create(scope.arena(), .{ | ||
| 3050 | .name = name, | ||
| 3051 | }), | ||
| 3052 | ), | ||
| 3053 | }); | ||
| 3054 | }, | ||
| 3055 | .Struct => { | ||
| 3056 | const container_scope = child_type.getContainerScope(); | ||
| 3057 | if (mod.lookupDeclName(&container_scope.base, field_name)) |decl| { | ||
| 3058 | // TODO if !decl.is_pub and inDifferentFiles() "{} is private" | ||
| 3059 | return sema.analyzeDeclRef(block, src, decl); | ||
| 3060 | } | ||
| 3061 | |||
| 3062 | if (container_scope.file_scope == mod.root_scope) { | ||
| 3063 | return sema.mod.fail(&block.base, src, "root source file has no member called '{s}'", .{field_name}); | ||
| 3064 | } else { | ||
| 3065 | return sema.mod.fail(&block.base, src, "container '{}' has no member called '{s}'", .{ child_type, field_name }); | ||
| 3066 | } | ||
| 3067 | }, | ||
| 3068 | else => return sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{child_type}), | ||
| 3069 | } | ||
| 3070 | }, | ||
| 3071 | else => {}, | ||
| 3072 | } | ||
| 3073 | return sema.mod.fail(&block.base, src, "type '{}' does not support field access", .{elem_ty}); | ||
| 3074 | } | ||
| 3075 | |||
| 3076 | fn elemPtr( | ||
| 3077 | sema: *Sema, | ||
| 3078 | block: *Scope.Block, | ||
| 3079 | src: LazySrcLoc, | ||
| 3080 | array_ptr: *Inst, | ||
| 3081 | elem_index: *Inst, | ||
| 3082 | elem_index_src: LazySrcLoc, | ||
| 3083 | ) InnerError!*Inst { | ||
| 3084 | const elem_ty = switch (array_ptr.ty.zigTypeTag()) { | ||
| 3085 | .Pointer => array_ptr.ty.elemType(), | ||
| 3086 | else => return sema.mod.fail(&block.base, array_ptr.src, "expected pointer, found '{}'", .{array_ptr.ty}), | ||
| 3087 | }; | ||
| 3088 | if (!elem_ty.isIndexable()) { | ||
| 3089 | return sema.mod.fail(&block.base, src, "array access of non-array type '{}'", .{elem_ty}); | ||
| 3090 | } | ||
| 3091 | |||
| 3092 | if (elem_ty.isSinglePointer() and elem_ty.elemType().zigTypeTag() == .Array) { | ||
| 3093 | // we have to deref the ptr operand to get the actual array pointer | ||
| 3094 | const array_ptr_deref = try sema.analyzeDeref(block, src, array_ptr, array_ptr.src); | ||
| 3095 | if (array_ptr_deref.value()) |array_ptr_val| { | ||
| 3096 | if (elem_index.value()) |index_val| { | ||
| 3097 | // Both array pointer and index are compile-time known. | ||
| 3098 | const index_u64 = index_val.toUnsignedInt(); | ||
| 3099 | // @intCast here because it would have been impossible to construct a value that | ||
| 3100 | // required a larger index. | ||
| 3101 | const elem_ptr = try array_ptr_val.elemPtr(scope.arena(), @intCast(usize, index_u64)); | ||
| 3102 | const pointee_type = elem_ty.elemType().elemType(); | ||
| 3103 | |||
| 3104 | return mod.constInst(scope, src, .{ | ||
| 3105 | .ty = try Type.Tag.single_const_pointer.create(scope.arena(), pointee_type), | ||
| 3106 | .val = elem_ptr, | ||
| 3107 | }); | ||
| 3108 | } | ||
| 3109 | } | ||
| 3110 | } | ||
| 3111 | |||
| 3112 | return sema.mod.fail(&block.base, src, "TODO implement more analyze elemptr", .{}); | ||
| 3113 | } | ||
| 3114 | |||
| 3115 | fn coerce(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) InnerError!*Inst { | ||
| 3116 | if (dest_type.tag() == .var_args_param) { | ||
| 3117 | return sema.coerceVarArgParam(scope, inst); | ||
| 3118 | } | ||
| 3119 | // If the types are the same, we can return the operand. | ||
| 3120 | if (dest_type.eql(inst.ty)) | ||
| 3121 | return inst; | ||
| 3122 | |||
| 3123 | const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty); | ||
| 3124 | if (in_memory_result == .ok) { | ||
| 3125 | return sema.bitcast(scope, dest_type, inst); | ||
| 3126 | } | ||
| 3127 | |||
| 3128 | // undefined to anything | ||
| 3129 | if (inst.value()) |val| { | ||
| 3130 | if (val.isUndef() or inst.ty.zigTypeTag() == .Undefined) { | ||
| 3131 | return mod.constInst(scope.arena(), inst.src, .{ .ty = dest_type, .val = val }); | ||
| 3132 | } | ||
| 3133 | } | ||
| 3134 | assert(inst.ty.zigTypeTag() != .Undefined); | ||
| 3135 | |||
| 3136 | // null to ?T | ||
| 3137 | if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) { | ||
| 3138 | return mod.constInst(scope.arena(), inst.src, .{ .ty = dest_type, .val = Value.initTag(.null_value) }); | ||
| 3139 | } | ||
| 3140 | |||
| 3141 | // T to ?T | ||
| 3142 | if (dest_type.zigTypeTag() == .Optional) { | ||
| 3143 | var buf: Type.Payload.ElemType = undefined; | ||
| 3144 | const child_type = dest_type.optionalChild(&buf); | ||
| 3145 | if (child_type.eql(inst.ty)) { | ||
| 3146 | return mod.wrapOptional(scope, dest_type, inst); | ||
| 3147 | } else if (try sema.coerceNum(scope, child_type, inst)) |some| { | ||
| 3148 | return mod.wrapOptional(scope, dest_type, some); | ||
| 3149 | } | ||
| 3150 | } | ||
| 3151 | |||
| 3152 | // T to E!T or E to E!T | ||
| 3153 | if (dest_type.tag() == .error_union) { | ||
| 3154 | return try mod.wrapErrorUnion(scope, dest_type, inst); | ||
| 3155 | } | ||
| 3156 | |||
| 3157 | // Coercions where the source is a single pointer to an array. | ||
| 3158 | src_array_ptr: { | ||
| 3159 | if (!inst.ty.isSinglePointer()) break :src_array_ptr; | ||
| 3160 | const array_type = inst.ty.elemType(); | ||
| 3161 | if (array_type.zigTypeTag() != .Array) break :src_array_ptr; | ||
| 3162 | const array_elem_type = array_type.elemType(); | ||
| 3163 | if (inst.ty.isConstPtr() and !dest_type.isConstPtr()) break :src_array_ptr; | ||
| 3164 | if (inst.ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr; | ||
| 3165 | |||
| 3166 | const dst_elem_type = dest_type.elemType(); | ||
| 3167 | switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) { | ||
| 3168 | .ok => {}, | ||
| 3169 | .no_match => break :src_array_ptr, | ||
| 3170 | } | ||
| 3171 | |||
| 3172 | switch (dest_type.ptrSize()) { | ||
| 3173 | .Slice => { | ||
| 3174 | // *[N]T to []T | ||
| 3175 | return sema.coerceArrayPtrToSlice(scope, dest_type, inst); | ||
| 3176 | }, | ||
| 3177 | .C => { | ||
| 3178 | // *[N]T to [*c]T | ||
| 3179 | return sema.coerceArrayPtrToMany(scope, dest_type, inst); | ||
| 3180 | }, | ||
| 3181 | .Many => { | ||
| 3182 | // *[N]T to [*]T | ||
| 3183 | // *[N:s]T to [*:s]T | ||
| 3184 | const src_sentinel = array_type.sentinel(); | ||
| 3185 | const dst_sentinel = dest_type.sentinel(); | ||
| 3186 | if (src_sentinel == null and dst_sentinel == null) | ||
| 3187 | return sema.coerceArrayPtrToMany(scope, dest_type, inst); | ||
| 3188 | |||
| 3189 | if (src_sentinel) |src_s| { | ||
| 3190 | if (dst_sentinel) |dst_s| { | ||
| 3191 | if (src_s.eql(dst_s)) { | ||
| 3192 | return sema.coerceArrayPtrToMany(scope, dest_type, inst); | ||
| 3193 | } | ||
| 3194 | } | ||
| 3195 | } | ||
| 3196 | }, | ||
| 3197 | .One => {}, | ||
| 3198 | } | ||
| 3199 | } | ||
| 3200 | |||
| 3201 | // comptime known number to other number | ||
| 3202 | if (try sema.coerceNum(scope, dest_type, inst)) |some| | ||
| 3203 | return some; | ||
| 3204 | |||
| 3205 | // integer widening | ||
| 3206 | if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) { | ||
| 3207 | assert(inst.value() == null); // handled above | ||
| 3208 | |||
| 3209 | const src_info = inst.ty.intInfo(mod.getTarget()); | ||
| 3210 | const dst_info = dest_type.intInfo(mod.getTarget()); | ||
| 3211 | if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or | ||
| 3212 | // small enough unsigned ints can get casted to large enough signed ints | ||
| 3213 | (src_info.signedness == .signed and dst_info.signedness == .unsigned and dst_info.bits > src_info.bits)) | ||
| 3214 | { | ||
| 3215 | try sema.requireRuntimeBlock(block, inst.src); | ||
| 3216 | return mod.addUnOp(b, inst.src, dest_type, .intcast, inst); | ||
| 3217 | } | ||
| 3218 | } | ||
| 3219 | |||
| 3220 | // float widening | ||
| 3221 | if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) { | ||
| 3222 | assert(inst.value() == null); // handled above | ||
| 3223 | |||
| 3224 | const src_bits = inst.ty.floatBits(mod.getTarget()); | ||
| 3225 | const dst_bits = dest_type.floatBits(mod.getTarget()); | ||
| 3226 | if (dst_bits >= src_bits) { | ||
| 3227 | try sema.requireRuntimeBlock(block, inst.src); | ||
| 3228 | return mod.addUnOp(b, inst.src, dest_type, .floatcast, inst); | ||
| 3229 | } | ||
| 3230 | } | ||
| 3231 | |||
| 3232 | return sema.mod.fail(&block.base, inst.src, "expected {}, found {}", .{ dest_type, inst.ty }); | ||
| 3233 | } | ||
| 3234 | |||
| 3235 | const InMemoryCoercionResult = enum { | ||
| 3236 | ok, | ||
| 3237 | no_match, | ||
| 3238 | }; | ||
| 3239 | |||
| 3240 | fn coerceInMemoryAllowed(dest_type: Type, src_type: Type) InMemoryCoercionResult { | ||
| 3241 | if (dest_type.eql(src_type)) | ||
| 3242 | return .ok; | ||
| 3243 | |||
| 3244 | // TODO: implement more of this function | ||
| 3245 | |||
| 3246 | return .no_match; | ||
| 3247 | } | ||
| 3248 | |||
| 3249 | fn coerceNum(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) InnerError!?*Inst { | ||
| 3250 | const val = inst.value() orelse return null; | ||
| 3251 | const src_zig_tag = inst.ty.zigTypeTag(); | ||
| 3252 | const dst_zig_tag = dest_type.zigTypeTag(); | ||
| 3253 | |||
| 3254 | if (dst_zig_tag == .ComptimeInt or dst_zig_tag == .Int) { | ||
| 3255 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { | ||
| 3256 | if (val.floatHasFraction()) { | ||
| 3257 | return sema.mod.fail(&block.base, inst.src, "fractional component prevents float value {} from being casted to type '{}'", .{ val, inst.ty }); | ||
| 3258 | } | ||
| 3259 | return sema.mod.fail(&block.base, inst.src, "TODO float to int", .{}); | ||
| 3260 | } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { | ||
| 3261 | if (!val.intFitsInType(dest_type, mod.getTarget())) { | ||
| 3262 | return sema.mod.fail(&block.base, inst.src, "type {} cannot represent integer value {}", .{ inst.ty, val }); | ||
| 3263 | } | ||
| 3264 | return mod.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 3265 | } | ||
| 3266 | } else if (dst_zig_tag == .ComptimeFloat or dst_zig_tag == .Float) { | ||
| 3267 | if (src_zig_tag == .Float or src_zig_tag == .ComptimeFloat) { | ||
| 3268 | const res = val.floatCast(scope.arena(), dest_type, mod.getTarget()) catch |err| switch (err) { | ||
| 3269 | error.Overflow => return mod.fail( | ||
| 3270 | scope, | ||
| 3271 | inst.src, | ||
| 3272 | "cast of value {} to type '{}' loses information", | ||
| 3273 | .{ val, dest_type }, | ||
| 3274 | ), | ||
| 3275 | error.OutOfMemory => return error.OutOfMemory, | ||
| 3276 | }; | ||
| 3277 | return mod.constInst(scope, inst.src, .{ .ty = dest_type, .val = res }); | ||
| 3278 | } else if (src_zig_tag == .Int or src_zig_tag == .ComptimeInt) { | ||
| 3279 | return sema.mod.fail(&block.base, inst.src, "TODO int to float", .{}); | ||
| 3280 | } | ||
| 3281 | } | ||
| 3282 | return null; | ||
| 3283 | } | ||
| 3284 | |||
| 3285 | fn coerceVarArgParam(sema: *Sema, block: *Scope.Block, inst: *Inst) !*Inst { | ||
| 3286 | switch (inst.ty.zigTypeTag()) { | ||
| 3287 | .ComptimeInt, .ComptimeFloat => return sema.mod.fail(&block.base, inst.src, "integer and float literals in var args function must be casted", .{}), | ||
| 3288 | else => {}, | ||
| 3289 | } | ||
| 3290 | // TODO implement more of this function. | ||
| 3291 | return inst; | ||
| 3292 | } | ||
| 3293 | |||
| 3294 | fn storePtr(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, ptr: *Inst, uncasted_value: *Inst) !*Inst { | ||
| 3295 | if (ptr.ty.isConstPtr()) | ||
| 3296 | return sema.mod.fail(&block.base, src, "cannot assign to constant", .{}); | ||
| 3297 | |||
| 3298 | const elem_ty = ptr.ty.elemType(); | ||
| 3299 | const value = try sema.coerce(scope, elem_ty, uncasted_value); | ||
| 3300 | if (elem_ty.onePossibleValue() != null) | ||
| 3301 | return sema.mod.constVoid(block.arena, .unneeded); | ||
| 3302 | |||
| 3303 | // TODO handle comptime pointer writes | ||
| 3304 | // TODO handle if the element type requires comptime | ||
| 3305 | |||
| 3306 | try sema.requireRuntimeBlock(block, src); | ||
| 3307 | return mod.addBinOp(b, src, Type.initTag(.void), .store, ptr, value); | ||
| 3308 | } | ||
| 3309 | |||
| 3310 | fn bitcast(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { | ||
| 3311 | if (inst.value()) |val| { | ||
| 3312 | // Keep the comptime Value representation; take the new type. | ||
| 3313 | return mod.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 3314 | } | ||
| 3315 | // TODO validate the type size and other compile errors | ||
| 3316 | try sema.requireRuntimeBlock(block, inst.src); | ||
| 3317 | return mod.addUnOp(b, inst.src, dest_type, .bitcast, inst); | ||
| 3318 | } | ||
| 3319 | |||
| 3320 | fn coerceArrayPtrToSlice(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { | ||
| 3321 | if (inst.value()) |val| { | ||
| 3322 | // The comptime Value representation is compatible with both types. | ||
| 3323 | return mod.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 3324 | } | ||
| 3325 | return sema.mod.fail(&block.base, inst.src, "TODO implement coerceArrayPtrToSlice runtime instruction", .{}); | ||
| 3326 | } | ||
| 3327 | |||
| 3328 | fn coerceArrayPtrToMany(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { | ||
| 3329 | if (inst.value()) |val| { | ||
| 3330 | // The comptime Value representation is compatible with both types. | ||
| 3331 | return mod.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 3332 | } | ||
| 3333 | return sema.mod.fail(&block.base, inst.src, "TODO implement coerceArrayPtrToMany runtime instruction", .{}); | ||
| 3334 | } | ||
| 3335 | |||
| 3336 | fn analyzeDeclVal(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) InnerError!*Inst { | ||
| 3337 | const decl_ref = try sema.analyzeDeclRef(block, src, decl); | ||
| 3338 | return sema.analyzeDeref(block, src, decl_ref, src); | ||
| 3339 | } | ||
| 3340 | |||
| 3341 | fn analyzeDeclRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, decl: *Decl) InnerError!*Inst { | ||
| 3342 | const scope_decl = scope.ownerDecl().?; | ||
| 3343 | try mod.declareDeclDependency(scope_decl, decl); | ||
| 3344 | mod.ensureDeclAnalyzed(decl) catch |err| { | ||
| 3345 | if (scope.cast(Scope.Block)) |block| { | ||
| 3346 | if (block.func) |func| { | ||
| 3347 | func.state = .dependency_failure; | ||
| 3348 | } else { | ||
| 3349 | block.owner_decl.analysis = .dependency_failure; | ||
| 3350 | } | ||
| 3351 | } else { | ||
| 3352 | scope_decl.analysis = .dependency_failure; | ||
| 3353 | } | ||
| 3354 | return err; | ||
| 3355 | }; | ||
| 3356 | |||
| 3357 | const decl_tv = try decl.typedValue(); | ||
| 3358 | if (decl_tv.val.tag() == .variable) { | ||
| 3359 | return mod.analyzeVarRef(scope, src, decl_tv); | ||
| 3360 | } | ||
| 3361 | return mod.constInst(scope.arena(), src, .{ | ||
| 3362 | .ty = try mod.simplePtrType(scope.arena(), decl_tv.ty, false, .One), | ||
| 3363 | .val = try Value.Tag.decl_ref.create(scope.arena(), decl), | ||
| 3364 | }); | ||
| 3365 | } | ||
| 3366 | |||
| 3367 | fn analyzeVarRef(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, tv: TypedValue) InnerError!*Inst { | ||
| 3368 | const variable = tv.val.castTag(.variable).?.data; | ||
| 3369 | |||
| 3370 | const ty = try mod.simplePtrType(scope.arena(), tv.ty, variable.is_mutable, .One); | ||
| 3371 | if (!variable.is_mutable and !variable.is_extern) { | ||
| 3372 | return mod.constInst(scope.arena(), src, .{ | ||
| 3373 | .ty = ty, | ||
| 3374 | .val = try Value.Tag.ref_val.create(scope.arena(), variable.init), | ||
| 3375 | }); | ||
| 3376 | } | ||
| 3377 | |||
| 3378 | try sema.requireRuntimeBlock(block, src); | ||
| 3379 | const inst = try b.arena.create(Inst.VarPtr); | ||
| 3380 | inst.* = .{ | ||
| 3381 | .base = .{ | ||
| 3382 | .tag = .varptr, | ||
| 3383 | .ty = ty, | ||
| 3384 | .src = src, | ||
| 3385 | }, | ||
| 3386 | .variable = variable, | ||
| 3387 | }; | ||
| 3388 | try b.instructions.append(mod.gpa, &inst.base); | ||
| 3389 | return &inst.base; | ||
| 3390 | } | ||
| 3391 | |||
| 3392 | fn analyzeRef( | ||
| 3393 | sema: *Sema, | ||
| 3394 | block: *Scope.Block, | ||
| 3395 | src: LazySrcLoc, | ||
| 3396 | operand: *Inst, | ||
| 3397 | ) InnerError!*Inst { | ||
| 3398 | const ptr_type = try mod.simplePtrType(scope.arena(), operand.ty, false, .One); | ||
| 3399 | |||
| 3400 | if (operand.value()) |val| { | ||
| 3401 | return mod.constInst(scope.arena(), src, .{ | ||
| 3402 | .ty = ptr_type, | ||
| 3403 | .val = try Value.Tag.ref_val.create(scope.arena(), val), | ||
| 3404 | }); | ||
| 3405 | } | ||
| 3406 | |||
| 3407 | try sema.requireRuntimeBlock(block, src); | ||
| 3408 | return block.addUnOp(src, ptr_type, .ref, operand); | ||
| 3409 | } | ||
| 3410 | |||
| 3411 | fn analyzeDeref( | ||
| 3412 | sema: *Sema, | ||
| 3413 | block: *Scope.Block, | ||
| 3414 | src: LazySrcLoc, | ||
| 3415 | ptr: *Inst, | ||
| 3416 | ptr_src: LazySrcLoc, | ||
| 3417 | ) InnerError!*Inst { | ||
| 3418 | const elem_ty = switch (ptr.ty.zigTypeTag()) { | ||
| 3419 | .Pointer => ptr.ty.elemType(), | ||
| 3420 | else => return sema.mod.fail(&block.base, ptr_src, "expected pointer, found '{}'", .{ptr.ty}), | ||
| 3421 | }; | ||
| 3422 | if (ptr.value()) |val| { | ||
| 3423 | return mod.constInst(scope.arena(), src, .{ | ||
| 3424 | .ty = elem_ty, | ||
| 3425 | .val = try val.pointerDeref(scope.arena()), | ||
| 3426 | }); | ||
| 3427 | } | ||
| 3428 | |||
| 3429 | try sema.requireRuntimeBlock(block, src); | ||
| 3430 | return mod.addUnOp(b, src, elem_ty, .load, ptr); | ||
| 3431 | } | ||
| 3432 | |||
| 3433 | fn analyzeIsNull( | ||
| 3434 | sema: *Sema, | ||
| 3435 | block: *Scope.Block, | ||
| 3436 | src: LazySrcLoc, | ||
| 3437 | operand: *Inst, | ||
| 3438 | invert_logic: bool, | ||
| 3439 | ) InnerError!*Inst { | ||
| 3440 | if (operand.value()) |opt_val| { | ||
| 3441 | const is_null = opt_val.isNull(); | ||
| 3442 | const bool_value = if (invert_logic) !is_null else is_null; | ||
| 3443 | return mod.constBool(block.arena, src, bool_value); | ||
| 3444 | } | ||
| 3445 | try sema.requireRuntimeBlock(block, src); | ||
| 3446 | const inst_tag: Inst.Tag = if (invert_logic) .is_non_null else .is_null; | ||
| 3447 | return mod.addUnOp(b, src, Type.initTag(.bool), inst_tag, operand); | ||
| 3448 | } | ||
| 3449 | |||
| 3450 | fn analyzeIsErr(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, operand: *Inst) InnerError!*Inst { | ||
| 3451 | const ot = operand.ty.zigTypeTag(); | ||
| 3452 | if (ot != .ErrorSet and ot != .ErrorUnion) return mod.constBool(block.arena, src, false); | ||
| 3453 | if (ot == .ErrorSet) return mod.constBool(block.arena, src, true); | ||
| 3454 | assert(ot == .ErrorUnion); | ||
| 3455 | if (operand.value()) |err_union| { | ||
| 3456 | return mod.constBool(block.arena, src, err_union.getError() != null); | ||
| 3457 | } | ||
| 3458 | try sema.requireRuntimeBlock(block, src); | ||
| 3459 | return mod.addUnOp(b, src, Type.initTag(.bool), .is_err, operand); | ||
| 3460 | } | ||
| 3461 | |||
| 3462 | fn analyzeSlice( | ||
| 3463 | sema: *Sema, | ||
| 3464 | block: *Scope.Block, | ||
| 3465 | src: LazySrcLoc, | ||
| 3466 | array_ptr: *Inst, | ||
| 3467 | start: *Inst, | ||
| 3468 | end_opt: ?*Inst, | ||
| 3469 | sentinel_opt: ?*Inst, | ||
| 3470 | sentinel_src: LazySrcLoc, | ||
| 3471 | ) InnerError!*Inst { | ||
| 3472 | const ptr_child = switch (array_ptr.ty.zigTypeTag()) { | ||
| 3473 | .Pointer => array_ptr.ty.elemType(), | ||
| 3474 | else => return sema.mod.fail(&block.base, src, "expected pointer, found '{}'", .{array_ptr.ty}), | ||
| 3475 | }; | ||
| 3476 | |||
| 3477 | var array_type = ptr_child; | ||
| 3478 | const elem_type = switch (ptr_child.zigTypeTag()) { | ||
| 3479 | .Array => ptr_child.elemType(), | ||
| 3480 | .Pointer => blk: { | ||
| 3481 | if (ptr_child.isSinglePointer()) { | ||
| 3482 | if (ptr_child.elemType().zigTypeTag() == .Array) { | ||
| 3483 | array_type = ptr_child.elemType(); | ||
| 3484 | break :blk ptr_child.elemType().elemType(); | ||
| 3485 | } | ||
| 3486 | |||
| 3487 | return sema.mod.fail(&block.base, src, "slice of single-item pointer", .{}); | ||
| 3488 | } | ||
| 3489 | break :blk ptr_child.elemType(); | ||
| 3490 | }, | ||
| 3491 | else => return sema.mod.fail(&block.base, src, "slice of non-array type '{}'", .{ptr_child}), | ||
| 3492 | }; | ||
| 3493 | |||
| 3494 | const slice_sentinel = if (sentinel_opt) |sentinel| blk: { | ||
| 3495 | const casted = try sema.coerce(scope, elem_type, sentinel); | ||
| 3496 | break :blk try sema.resolveConstValue(block, sentinel_src, casted); | ||
| 3497 | } else null; | ||
| 3498 | |||
| 3499 | var return_ptr_size: std.builtin.TypeInfo.Pointer.Size = .Slice; | ||
| 3500 | var return_elem_type = elem_type; | ||
| 3501 | if (end_opt) |end| { | ||
| 3502 | if (end.value()) |end_val| { | ||
| 3503 | if (start.value()) |start_val| { | ||
| 3504 | const start_u64 = start_val.toUnsignedInt(); | ||
| 3505 | const end_u64 = end_val.toUnsignedInt(); | ||
| 3506 | if (start_u64 > end_u64) { | ||
| 3507 | return sema.mod.fail(&block.base, src, "out of bounds slice", .{}); | ||
| 3508 | } | ||
| 3509 | |||
| 3510 | const len = end_u64 - start_u64; | ||
| 3511 | const array_sentinel = if (array_type.zigTypeTag() == .Array and end_u64 == array_type.arrayLen()) | ||
| 3512 | array_type.sentinel() | ||
| 3513 | else | ||
| 3514 | slice_sentinel; | ||
| 3515 | return_elem_type = try mod.arrayType(scope, len, array_sentinel, elem_type); | ||
| 3516 | return_ptr_size = .One; | ||
| 3517 | } | ||
| 3518 | } | ||
| 3519 | } | ||
| 3520 | const return_type = try mod.ptrType( | ||
| 3521 | scope, | ||
| 3522 | return_elem_type, | ||
| 3523 | if (end_opt == null) slice_sentinel else null, | ||
| 3524 | 0, // TODO alignment | ||
| 3525 | 0, | ||
| 3526 | 0, | ||
| 3527 | !ptr_child.isConstPtr(), | ||
| 3528 | ptr_child.isAllowzeroPtr(), | ||
| 3529 | ptr_child.isVolatilePtr(), | ||
| 3530 | return_ptr_size, | ||
| 3531 | ); | ||
| 3532 | |||
| 3533 | return sema.mod.fail(&block.base, src, "TODO implement analysis of slice", .{}); | ||
| 3534 | } | ||
| 3535 | |||
| 3536 | fn analyzeImport(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, target_string: []const u8) !*Scope.File { | ||
| 3537 | const cur_pkg = scope.getFileScope().pkg; | ||
| 3538 | const cur_pkg_dir_path = cur_pkg.root_src_directory.path orelse "."; | ||
| 3539 | const found_pkg = cur_pkg.table.get(target_string); | ||
| 3540 | |||
| 3541 | const resolved_path = if (found_pkg) |pkg| | ||
| 3542 | try std.fs.path.resolve(mod.gpa, &[_][]const u8{ pkg.root_src_directory.path orelse ".", pkg.root_src_path }) | ||
| 3543 | else | ||
| 3544 | try std.fs.path.resolve(mod.gpa, &[_][]const u8{ cur_pkg_dir_path, target_string }); | ||
| 3545 | errdefer mod.gpa.free(resolved_path); | ||
| 3546 | |||
| 3547 | if (mod.import_table.get(resolved_path)) |some| { | ||
| 3548 | mod.gpa.free(resolved_path); | ||
| 3549 | return some; | ||
| 3550 | } | ||
| 3551 | |||
| 3552 | if (found_pkg == null) { | ||
| 3553 | const resolved_root_path = try std.fs.path.resolve(mod.gpa, &[_][]const u8{cur_pkg_dir_path}); | ||
| 3554 | defer mod.gpa.free(resolved_root_path); | ||
| 3555 | |||
| 3556 | if (!mem.startsWith(u8, resolved_path, resolved_root_path)) { | ||
| 3557 | return error.ImportOutsidePkgPath; | ||
| 3558 | } | ||
| 3559 | } | ||
| 3560 | |||
| 3561 | // TODO Scope.Container arena for ty and sub_file_path | ||
| 3562 | const file_scope = try mod.gpa.create(Scope.File); | ||
| 3563 | errdefer mod.gpa.destroy(file_scope); | ||
| 3564 | const struct_ty = try Type.Tag.empty_struct.create(mod.gpa, &file_scope.root_container); | ||
| 3565 | errdefer mod.gpa.destroy(struct_ty.castTag(.empty_struct).?); | ||
| 3566 | |||
| 3567 | file_scope.* = .{ | ||
| 3568 | .sub_file_path = resolved_path, | ||
| 3569 | .source = .{ .unloaded = {} }, | ||
| 3570 | .tree = undefined, | ||
| 3571 | .status = .never_loaded, | ||
| 3572 | .pkg = found_pkg orelse cur_pkg, | ||
| 3573 | .root_container = .{ | ||
| 3574 | .file_scope = file_scope, | ||
| 3575 | .decls = .{}, | ||
| 3576 | .ty = struct_ty, | ||
| 3577 | }, | ||
| 3578 | }; | ||
| 3579 | mod.analyzeContainer(&file_scope.root_container) catch |err| switch (err) { | ||
| 3580 | error.AnalysisFail => { | ||
| 3581 | assert(mod.comp.totalErrorCount() != 0); | ||
| 3582 | }, | ||
| 3583 | else => |e| return e, | ||
| 3584 | }; | ||
| 3585 | try mod.import_table.put(mod.gpa, file_scope.sub_file_path, file_scope); | ||
| 3586 | return file_scope; | ||
| 3587 | } | ||
| 3588 | |||
| 3589 | /// Asserts that lhs and rhs types are both numeric. | ||
| 3590 | fn cmpNumeric( | ||
| 3591 | sema: *Sema, | ||
| 3592 | block: *Scope.Block, | ||
| 3593 | src: LazySrcLoc, | ||
| 3594 | lhs: *Inst, | ||
| 3595 | rhs: *Inst, | ||
| 3596 | op: std.math.CompareOperator, | ||
| 3597 | ) InnerError!*Inst { | ||
| 3598 | assert(lhs.ty.isNumeric()); | ||
| 3599 | assert(rhs.ty.isNumeric()); | ||
| 3600 | |||
| 3601 | const lhs_ty_tag = lhs.ty.zigTypeTag(); | ||
| 3602 | const rhs_ty_tag = rhs.ty.zigTypeTag(); | ||
| 3603 | |||
| 3604 | if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) { | ||
| 3605 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { | ||
| 3606 | return sema.mod.fail(&block.base, src, "vector length mismatch: {d} and {d}", .{ | ||
| 3607 | lhs.ty.arrayLen(), | ||
| 3608 | rhs.ty.arrayLen(), | ||
| 3609 | }); | ||
| 3610 | } | ||
| 3611 | return sema.mod.fail(&block.base, src, "TODO implement support for vectors in cmpNumeric", .{}); | ||
| 3612 | } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) { | ||
| 3613 | return sema.mod.fail(&block.base, src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ | ||
| 3614 | lhs.ty, | ||
| 3615 | rhs.ty, | ||
| 3616 | }); | ||
| 3617 | } | ||
| 3618 | |||
| 3619 | if (lhs.value()) |lhs_val| { | ||
| 3620 | if (rhs.value()) |rhs_val| { | ||
| 3621 | return mod.constBool(block.arena, src, Value.compare(lhs_val, op, rhs_val)); | ||
| 3622 | } | ||
| 3623 | } | ||
| 3624 | |||
| 3625 | // TODO handle comparisons against lazy zero values | ||
| 3626 | // Some values can be compared against zero without being runtime known or without forcing | ||
| 3627 | // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to | ||
| 3628 | // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout | ||
| 3629 | // of this function if we don't need to. | ||
| 3630 | |||
| 3631 | // It must be a runtime comparison. | ||
| 3632 | try sema.requireRuntimeBlock(block, src); | ||
| 3633 | // For floats, emit a float comparison instruction. | ||
| 3634 | const lhs_is_float = switch (lhs_ty_tag) { | ||
| 3635 | .Float, .ComptimeFloat => true, | ||
| 3636 | else => false, | ||
| 3637 | }; | ||
| 3638 | const rhs_is_float = switch (rhs_ty_tag) { | ||
| 3639 | .Float, .ComptimeFloat => true, | ||
| 3640 | else => false, | ||
| 3641 | }; | ||
| 3642 | if (lhs_is_float and rhs_is_float) { | ||
| 3643 | // Implicit cast the smaller one to the larger one. | ||
| 3644 | const dest_type = x: { | ||
| 3645 | if (lhs_ty_tag == .ComptimeFloat) { | ||
| 3646 | break :x rhs.ty; | ||
| 3647 | } else if (rhs_ty_tag == .ComptimeFloat) { | ||
| 3648 | break :x lhs.ty; | ||
| 3649 | } | ||
| 3650 | if (lhs.ty.floatBits(mod.getTarget()) >= rhs.ty.floatBits(mod.getTarget())) { | ||
| 3651 | break :x lhs.ty; | ||
| 3652 | } else { | ||
| 3653 | break :x rhs.ty; | ||
| 3654 | } | ||
| 3655 | }; | ||
| 3656 | const casted_lhs = try sema.coerce(scope, dest_type, lhs); | ||
| 3657 | const casted_rhs = try sema.coerce(scope, dest_type, rhs); | ||
| 3658 | return mod.addBinOp(b, src, dest_type, Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs); | ||
| 3659 | } | ||
| 3660 | // For mixed unsigned integer sizes, implicit cast both operands to the larger integer. | ||
| 3661 | // For mixed signed and unsigned integers, implicit cast both operands to a signed | ||
| 3662 | // integer with + 1 bit. | ||
| 3663 | // For mixed floats and integers, extract the integer part from the float, cast that to | ||
| 3664 | // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float, | ||
| 3665 | // add/subtract 1. | ||
| 3666 | const lhs_is_signed = if (lhs.value()) |lhs_val| | ||
| 3667 | lhs_val.compareWithZero(.lt) | ||
| 3668 | else | ||
| 3669 | (lhs.ty.isFloat() or lhs.ty.isSignedInt()); | ||
| 3670 | const rhs_is_signed = if (rhs.value()) |rhs_val| | ||
| 3671 | rhs_val.compareWithZero(.lt) | ||
| 3672 | else | ||
| 3673 | (rhs.ty.isFloat() or rhs.ty.isSignedInt()); | ||
| 3674 | const dest_int_is_signed = lhs_is_signed or rhs_is_signed; | ||
| 3675 | |||
| 3676 | var dest_float_type: ?Type = null; | ||
| 3677 | |||
| 3678 | var lhs_bits: usize = undefined; | ||
| 3679 | if (lhs.value()) |lhs_val| { | ||
| 3680 | if (lhs_val.isUndef()) | ||
| 3681 | return mod.constUndef(scope, src, Type.initTag(.bool)); | ||
| 3682 | const is_unsigned = if (lhs_is_float) x: { | ||
| 3683 | var bigint_space: Value.BigIntSpace = undefined; | ||
| 3684 | var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(mod.gpa); | ||
| 3685 | defer bigint.deinit(); | ||
| 3686 | const zcmp = lhs_val.orderAgainstZero(); | ||
| 3687 | if (lhs_val.floatHasFraction()) { | ||
| 3688 | switch (op) { | ||
| 3689 | .eq => return mod.constBool(block.arena, src, false), | ||
| 3690 | .neq => return mod.constBool(block.arena, src, true), | ||
| 3691 | else => {}, | ||
| 3692 | } | ||
| 3693 | if (zcmp == .lt) { | ||
| 3694 | try bigint.addScalar(bigint.toConst(), -1); | ||
| 3695 | } else { | ||
| 3696 | try bigint.addScalar(bigint.toConst(), 1); | ||
| 3697 | } | ||
| 3698 | } | ||
| 3699 | lhs_bits = bigint.toConst().bitCountTwosComp(); | ||
| 3700 | break :x (zcmp != .lt); | ||
| 3701 | } else x: { | ||
| 3702 | lhs_bits = lhs_val.intBitCountTwosComp(); | ||
| 3703 | break :x (lhs_val.orderAgainstZero() != .lt); | ||
| 3704 | }; | ||
| 3705 | lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed); | ||
| 3706 | } else if (lhs_is_float) { | ||
| 3707 | dest_float_type = lhs.ty; | ||
| 3708 | } else { | ||
| 3709 | const int_info = lhs.ty.intInfo(mod.getTarget()); | ||
| 3710 | lhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed); | ||
| 3711 | } | ||
| 3712 | |||
| 3713 | var rhs_bits: usize = undefined; | ||
| 3714 | if (rhs.value()) |rhs_val| { | ||
| 3715 | if (rhs_val.isUndef()) | ||
| 3716 | return mod.constUndef(scope, src, Type.initTag(.bool)); | ||
| 3717 | const is_unsigned = if (rhs_is_float) x: { | ||
| 3718 | var bigint_space: Value.BigIntSpace = undefined; | ||
| 3719 | var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(mod.gpa); | ||
| 3720 | defer bigint.deinit(); | ||
| 3721 | const zcmp = rhs_val.orderAgainstZero(); | ||
| 3722 | if (rhs_val.floatHasFraction()) { | ||
| 3723 | switch (op) { | ||
| 3724 | .eq => return mod.constBool(block.arena, src, false), | ||
| 3725 | .neq => return mod.constBool(block.arena, src, true), | ||
| 3726 | else => {}, | ||
| 3727 | } | ||
| 3728 | if (zcmp == .lt) { | ||
| 3729 | try bigint.addScalar(bigint.toConst(), -1); | ||
| 3730 | } else { | ||
| 3731 | try bigint.addScalar(bigint.toConst(), 1); | ||
| 3732 | } | ||
| 3733 | } | ||
| 3734 | rhs_bits = bigint.toConst().bitCountTwosComp(); | ||
| 3735 | break :x (zcmp != .lt); | ||
| 3736 | } else x: { | ||
| 3737 | rhs_bits = rhs_val.intBitCountTwosComp(); | ||
| 3738 | break :x (rhs_val.orderAgainstZero() != .lt); | ||
| 3739 | }; | ||
| 3740 | rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed); | ||
| 3741 | } else if (rhs_is_float) { | ||
| 3742 | dest_float_type = rhs.ty; | ||
| 3743 | } else { | ||
| 3744 | const int_info = rhs.ty.intInfo(mod.getTarget()); | ||
| 3745 | rhs_bits = int_info.bits + @boolToInt(int_info.signedness == .unsigned and dest_int_is_signed); | ||
| 3746 | } | ||
| 3747 | |||
| 3748 | const dest_type = if (dest_float_type) |ft| ft else blk: { | ||
| 3749 | const max_bits = std.math.max(lhs_bits, rhs_bits); | ||
| 3750 | const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) { | ||
| 3751 | error.Overflow => return sema.mod.fail(&block.base, src, "{d} exceeds maximum integer bit count", .{max_bits}), | ||
| 3752 | }; | ||
| 3753 | break :blk try mod.makeIntType(scope, dest_int_is_signed, casted_bits); | ||
| 3754 | }; | ||
| 3755 | const casted_lhs = try sema.coerce(scope, dest_type, lhs); | ||
| 3756 | const casted_rhs = try sema.coerce(scope, dest_type, rhs); | ||
| 3757 | |||
| 3758 | return mod.addBinOp(b, src, Type.initTag(.bool), Inst.Tag.fromCmpOp(op), casted_lhs, casted_rhs); | ||
| 3759 | } | ||
| 3760 | |||
| 3761 | fn wrapOptional(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { | ||
| 3762 | if (inst.value()) |val| { | ||
| 3763 | return mod.constInst(scope.arena(), inst.src, .{ .ty = dest_type, .val = val }); | ||
| 3764 | } | ||
| 3765 | |||
| 3766 | try sema.requireRuntimeBlock(block, inst.src); | ||
| 3767 | return mod.addUnOp(b, inst.src, dest_type, .wrap_optional, inst); | ||
| 3768 | } | ||
| 3769 | |||
| 3770 | fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { | ||
| 3771 | // TODO deal with inferred error sets | ||
| 3772 | const err_union = dest_type.castTag(.error_union).?; | ||
| 3773 | if (inst.value()) |val| { | ||
| 3774 | const to_wrap = if (inst.ty.zigTypeTag() != .ErrorSet) blk: { | ||
| 3775 | _ = try sema.coerce(scope, err_union.data.payload, inst); | ||
| 3776 | break :blk val; | ||
| 3777 | } else switch (err_union.data.error_set.tag()) { | ||
| 3778 | .anyerror => val, | ||
| 3779 | .error_set_single => blk: { | ||
| 3780 | const n = err_union.data.error_set.castTag(.error_set_single).?.data; | ||
| 3781 | if (!mem.eql(u8, val.castTag(.@"error").?.data.name, n)) | ||
| 3782 | return sema.mod.fail(&block.base, inst.src, "expected type '{}', found type '{}'", .{ err_union.data.error_set, inst.ty }); | ||
| 3783 | break :blk val; | ||
| 3784 | }, | ||
| 3785 | .error_set => blk: { | ||
| 3786 | const f = err_union.data.error_set.castTag(.error_set).?.data.typed_value.most_recent.typed_value.val.castTag(.error_set).?.data.fields; | ||
| 3787 | if (f.get(val.castTag(.@"error").?.data.name) == null) | ||
| 3788 | return sema.mod.fail(&block.base, inst.src, "expected type '{}', found type '{}'", .{ err_union.data.error_set, inst.ty }); | ||
| 3789 | break :blk val; | ||
| 3790 | }, | ||
| 3791 | else => unreachable, | ||
| 3792 | }; | ||
| 3793 | |||
| 3794 | return mod.constInst(scope.arena(), inst.src, .{ | ||
| 3795 | .ty = dest_type, | ||
| 3796 | // creating a SubValue for the error_union payload | ||
| 3797 | .val = try Value.Tag.error_union.create( | ||
| 3798 | scope.arena(), | ||
| 3799 | to_wrap, | ||
| 3800 | ), | ||
| 3801 | }); | ||
| 3802 | } | ||
| 3803 | |||
| 3804 | try sema.requireRuntimeBlock(block, inst.src); | ||
| 3805 | |||
| 3806 | // we are coercing from E to E!T | ||
| 3807 | if (inst.ty.zigTypeTag() == .ErrorSet) { | ||
| 3808 | var coerced = try sema.coerce(scope, err_union.data.error_set, inst); | ||
| 3809 | return mod.addUnOp(b, inst.src, dest_type, .wrap_errunion_err, coerced); | ||
| 3810 | } else { | ||
| 3811 | var coerced = try sema.coerce(scope, err_union.data.payload, inst); | ||
| 3812 | return mod.addUnOp(b, inst.src, dest_type, .wrap_errunion_payload, coerced); | ||
| 3813 | } | ||
| 3814 | } | ||
| 3815 | |||
| 3816 | fn resolvePeerTypes(sema: *Sema, block: *Scope.Block, instructions: []*Inst) !Type { | ||
| 3817 | if (instructions.len == 0) | ||
| 3818 | return Type.initTag(.noreturn); | ||
| 3819 | |||
| 3820 | if (instructions.len == 1) | ||
| 3821 | return instructions[0].ty; | ||
| 3822 | |||
| 3823 | var chosen = instructions[0]; | ||
| 3824 | for (instructions[1..]) |candidate| { | ||
| 3825 | if (candidate.ty.eql(chosen.ty)) | ||
| 3826 | continue; | ||
| 3827 | if (candidate.ty.zigTypeTag() == .NoReturn) | ||
| 3828 | continue; | ||
| 3829 | if (chosen.ty.zigTypeTag() == .NoReturn) { | ||
| 3830 | chosen = candidate; | ||
| 3831 | continue; | ||
| 3832 | } | ||
| 3833 | if (candidate.ty.zigTypeTag() == .Undefined) | ||
| 3834 | continue; | ||
| 3835 | if (chosen.ty.zigTypeTag() == .Undefined) { | ||
| 3836 | chosen = candidate; | ||
| 3837 | continue; | ||
| 3838 | } | ||
| 3839 | if (chosen.ty.isInt() and | ||
| 3840 | candidate.ty.isInt() and | ||
| 3841 | chosen.ty.isSignedInt() == candidate.ty.isSignedInt()) | ||
| 3842 | { | ||
| 3843 | if (chosen.ty.intInfo(mod.getTarget()).bits < candidate.ty.intInfo(mod.getTarget()).bits) { | ||
| 3844 | chosen = candidate; | ||
| 3845 | } | ||
| 3846 | continue; | ||
| 3847 | } | ||
| 3848 | if (chosen.ty.isFloat() and candidate.ty.isFloat()) { | ||
| 3849 | if (chosen.ty.floatBits(mod.getTarget()) < candidate.ty.floatBits(mod.getTarget())) { | ||
| 3850 | chosen = candidate; | ||
| 3851 | } | ||
| 3852 | continue; | ||
| 3853 | } | ||
| 3854 | |||
| 3855 | if (chosen.ty.zigTypeTag() == .ComptimeInt and candidate.ty.isInt()) { | ||
| 3856 | chosen = candidate; | ||
| 3857 | continue; | ||
| 3858 | } | ||
| 3859 | |||
| 3860 | if (chosen.ty.isInt() and candidate.ty.zigTypeTag() == .ComptimeInt) { | ||
| 3861 | continue; | ||
| 3862 | } | ||
| 3863 | |||
| 3864 | // TODO error notes pointing out each type | ||
| 3865 | return sema.mod.fail(&block.base, candidate.src, "incompatible types: '{}' and '{}'", .{ chosen.ty, candidate.ty }); | ||
| 3866 | } | ||
| 3867 | |||
| 3868 | return chosen.ty; | ||
| 3869 | } | ||