| 1 | //! Semantic analysis of ZIR instructions. |
| 2 | //! Shared to every Block. Stored on the stack. |
| 3 | //! State used for compiling a ZIR into AIR. |
| 4 | //! Transforms untyped ZIR instructions into semantically-analyzed AIR instructions. |
| 5 | //! Does type checking, comptime control flow, and safety-check generation. |
| 6 | //! This is the the heart of the Zig compiler. |
| 7 | |
| 8 | const std = @import("std"); |
| 9 | const math = std.math; |
| 10 | const mem = std.mem; |
| 11 | const Allocator = mem.Allocator; |
| 12 | const assert = std.debug.assert; |
| 13 | const log = std.log.scoped(.sema); |
| 14 | |
| 15 | const Sema = @This(); |
| 16 | const Value = @import("Value.zig"); |
| 17 | const MutableValue = @import("mutable_value.zig").MutableValue; |
| 18 | const Type = @import("Type.zig"); |
| 19 | const Air = @import("Air.zig"); |
| 20 | const Zir = std.zig.Zir; |
| 21 | const Zcu = @import("Zcu.zig"); |
| 22 | const Namespace = Zcu.Namespace; |
| 23 | const CompileError = Zcu.CompileError; |
| 24 | const SemaError = Zcu.SemaError; |
| 25 | const LazySrcLoc = Zcu.LazySrcLoc; |
| 26 | const RangeSet = @import("RangeSet.zig"); |
| 27 | const target_util = @import("target.zig"); |
| 28 | const crash_report = @import("crash_report.zig"); |
| 29 | const build_options = @import("build_options"); |
| 30 | const Compilation = @import("Compilation.zig"); |
| 31 | const InternPool = @import("InternPool.zig"); |
| 32 | const Alignment = InternPool.Alignment; |
| 33 | const AnalUnit = InternPool.AnalUnit; |
| 34 | const ComptimeAllocIndex = InternPool.ComptimeAllocIndex; |
| 35 | const Cache = std.Build.Cache; |
| 36 | const LowerZon = @import("Sema/LowerZon.zig"); |
| 37 | const arith = @import("Sema/arith.zig"); |
| 38 | const Module = @import("Module.zig"); |
| 39 | |
| 40 | pt: Zcu.PerThread, |
| 41 | /// Alias to `zcu.gpa`. |
| 42 | gpa: Allocator, |
| 43 | /// Points to the temporary arena allocator of the Sema. |
| 44 | /// This arena will be cleared when the sema is destroyed. |
| 45 | arena: Allocator, |
| 46 | code: Zir, |
| 47 | air_instructions: std.MultiArrayList(Air.Inst) = .{}, |
| 48 | air_extra: std.ArrayList(u32) = .empty, |
| 49 | /// Maps ZIR to AIR. |
| 50 | inst_map: InstMap = .{}, |
| 51 | /// The "owner" of a `Sema` represents the root "thing" that is being analyzed. |
| 52 | /// This does not change throughout the entire lifetime of a `Sema`. For instance, |
| 53 | /// when analyzing a runtime function body, this is always `func` of that function, |
| 54 | /// even if an inline/comptime function call is being analyzed. |
| 55 | owner: AnalUnit, |
| 56 | /// The function this ZIR code is the body of, according to the source code. |
| 57 | /// This starts out the same as `sema.owner.func` if applicable, and then diverges |
| 58 | /// in the case of an inline or comptime function call. |
| 59 | /// This could be `none`, a `func_decl`, or a `func_instance`. |
| 60 | func_index: InternPool.Index, |
| 61 | /// Whether the type of func_index has a calling convention of `.naked`. |
| 62 | func_is_naked: bool, |
| 63 | /// Used to restore the error return trace when returning a non-error from a function. |
| 64 | error_return_trace_index_on_fn_entry: Air.Inst.Ref = .none, |
| 65 | comptime_err_ret_trace: *std.array_list.Managed(LazySrcLoc), |
| 66 | /// When semantic analysis needs to know the return type of the function whose body |
| 67 | /// is being analyzed, this `Type` should be used instead of going through `func`. |
| 68 | /// This will correctly handle the case of a comptime/inline function call of a |
| 69 | /// generic function which uses a type expression for the return type. |
| 70 | /// The type will be `void` in the case that `func` is `null`. |
| 71 | fn_ret_ty: Type, |
| 72 | /// In case of the return type being an error union with an inferred error |
| 73 | /// set, this is the inferred error set. `null` otherwise. Allocated with |
| 74 | /// `Sema.arena`. |
| 75 | fn_ret_ty_ies: ?*InferredErrorSet, |
| 76 | branch_quota: u32 = default_branch_quota, |
| 77 | branch_count: u32 = 0, |
| 78 | /// Populated when returning `error.ComptimeBreak`. Used to communicate the |
| 79 | /// break instruction up the stack to find the corresponding Block. |
| 80 | comptime_break_inst: Zir.Inst.Index = undefined, |
| 81 | /// These are lazily created runtime blocks from block_inline instructions. |
| 82 | /// They are created when an break_inline passes through a runtime condition, because |
| 83 | /// Sema must convert comptime control flow to runtime control flow, which means |
| 84 | /// breaking from a block. |
| 85 | post_hoc_blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, *LabeledBlock) = .empty, |
| 86 | /// Populated with the last compile error created. |
| 87 | err: ?*Zcu.ErrorMsg = null, |
| 88 | |
| 89 | /// The temporary arena is used for the memory of the `InferredAlloc` values |
| 90 | /// here so the values can be dropped without any cleanup. |
| 91 | unresolved_inferred_allocs: std.array_hash_map.Auto(Air.Inst.Index, InferredAlloc) = .empty, |
| 92 | |
| 93 | /// Links every pointer derived from a base `alloc` back to that `alloc`. Used |
| 94 | /// to detect comptime-known `const`s. |
| 95 | /// TODO: ZIR liveness analysis would allow us to remove elements from this map. |
| 96 | base_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, Air.Inst.Index) = .empty, |
| 97 | |
| 98 | /// Runtime `alloc`s are placed in this map to track all comptime-known writes |
| 99 | /// before the corresponding `make_ptr_const` instruction. |
| 100 | /// If any store to the alloc depends on a runtime condition or stores a runtime |
| 101 | /// value, the corresponding element in this map is erased, to indicate that the |
| 102 | /// alloc is not comptime-known. |
| 103 | /// If the alloc remains in this map when `make_ptr_const` is reached, its value |
| 104 | /// is comptime-known, and all stores to the pointer must be applied at comptime |
| 105 | /// to determine the comptime value. |
| 106 | /// Backed by gpa. |
| 107 | maybe_comptime_allocs: std.AutoHashMapUnmanaged(Air.Inst.Index, MaybeComptimeAlloc) = .empty, |
| 108 | |
| 109 | /// Comptime-mutable allocs, and any comptime allocs which reference it, are |
| 110 | /// stored as elements of this array. |
| 111 | /// Pointers to such memory are represented via an index into this array. |
| 112 | /// Backed by gpa. |
| 113 | comptime_allocs: std.ArrayList(ComptimeAlloc) = .empty, |
| 114 | |
| 115 | /// A list of exports performed by this analysis. After this `Sema` terminates, |
| 116 | /// these are flushed to `Zcu.single_exports` or `Zcu.multi_exports`. |
| 117 | exports: std.ArrayList(Zcu.Export) = .empty, |
| 118 | |
| 119 | /// All references registered so far by this `Sema`. This is a temporary duplicate |
| 120 | /// of data stored in `Zcu.all_references`. It exists to avoid adding references to |
| 121 | /// a given `AnalUnit` multiple times. |
| 122 | references: std.array_hash_map.Auto(AnalUnit, void) = .empty, |
| 123 | type_references: std.array_hash_map.Auto(InternPool.Index, void) = .empty, |
| 124 | |
| 125 | /// All dependencies registered so far by this `Sema`. This is a temporary duplicate |
| 126 | /// of the main dependency data. It exists to avoid adding dependencies to a given |
| 127 | /// `AnalUnit` multiple times. |
| 128 | dependencies: std.array_hash_map.Auto(InternPool.Dependee, void) = .empty, |
| 129 | |
| 130 | /// Whether memoization of this call is permitted. Operations with side effects global |
| 131 | /// to the `Sema`, such as `@setEvalBranchQuota`, set this to `false`. It is observed |
| 132 | /// by `analyzeCall`. |
| 133 | allow_memoize: bool = true, |
| 134 | |
| 135 | /// The largest quota requested by `@setEvalBranchQuota` within the comptime call |
| 136 | /// currently being analyzed. |
| 137 | quota_request: u32 = 0, |
| 138 | |
| 139 | /// The `BranchHint` for the current branch of runtime control flow. |
| 140 | /// This state is on `Sema` so that `cold` hints can be propagated up through blocks with less special handling. |
| 141 | branch_hint: ?std.lang.BranchHint = null, |
| 142 | |
| 143 | const RuntimeIndex = enum(u32) { |
| 144 | zero = 0, |
| 145 | comptime_field_ptr = std.math.maxInt(u32), |
| 146 | _, |
| 147 | |
| 148 | pub fn increment(ri: *RuntimeIndex) void { |
| 149 | ri.* = @fromBackingInt(@intCast(@backingInt(ri.*) + 1)); |
| 150 | } |
| 151 | }; |
| 152 | |
| 153 | const MaybeComptimeAlloc = struct { |
| 154 | /// The runtime index of the `alloc` instruction. |
| 155 | runtime_index: RuntimeIndex, |
| 156 | /// Backed by sema.arena. Tracks all comptime-known stores to this `alloc`. Due to |
| 157 | /// RLS, a single comptime-known allocation may have arbitrarily many stores. |
| 158 | /// This list also contains `set_union_tag`, `optional_payload_ptr_set`, and |
| 159 | /// `errunion_payload_ptr_set` instructions. |
| 160 | /// If the instruction is one of these three tags, `src` may be `.unneeded`. |
| 161 | stores: std.MultiArrayList(struct { |
| 162 | inst: Air.Inst.Index, |
| 163 | src: LazySrcLoc, |
| 164 | }) = .{}, |
| 165 | }; |
| 166 | |
| 167 | const ComptimeAlloc = struct { |
| 168 | val: MutableValue, |
| 169 | is_const: bool, |
| 170 | src: LazySrcLoc, |
| 171 | /// `.none` indicates that the alignment is the natural alignment of `val`. |
| 172 | alignment: Alignment, |
| 173 | /// This is the `runtime_index` at the point of this allocation. If an store |
| 174 | /// to this alloc ever occurs with a runtime index greater than this one, it |
| 175 | /// is behind a runtime condition, so a compile error will be emitted. |
| 176 | runtime_index: RuntimeIndex, |
| 177 | }; |
| 178 | |
| 179 | /// Asserts that `ty` is not an OPV type. |
| 180 | /// `src` may be `null` if `is_const` will be set. |
| 181 | fn newComptimeAlloc(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type, alignment: Alignment) !ComptimeAllocIndex { |
| 182 | const pt = sema.pt; |
| 183 | |
| 184 | switch (ty.classify(pt.zcu)) { |
| 185 | .no_possible_value => unreachable, |
| 186 | .one_possible_value => unreachable, |
| 187 | else => {}, |
| 188 | } |
| 189 | |
| 190 | const idx = sema.comptime_allocs.items.len; |
| 191 | try sema.comptime_allocs.append(sema.gpa, .{ |
| 192 | .val = .{ .interned = (try pt.undefValue(ty)).toIntern() }, |
| 193 | .is_const = false, |
| 194 | .src = src, |
| 195 | .alignment = alignment, |
| 196 | .runtime_index = block.runtime_index, |
| 197 | }); |
| 198 | return @fromBackingInt(@intCast(idx)); |
| 199 | } |
| 200 | |
| 201 | pub fn getComptimeAlloc(sema: *Sema, idx: ComptimeAllocIndex) *ComptimeAlloc { |
| 202 | return &sema.comptime_allocs.items[@backingInt(idx)]; |
| 203 | } |
| 204 | |
| 205 | pub const default_branch_quota = 1000; |
| 206 | |
| 207 | pub const InferredErrorSet = struct { |
| 208 | /// The function body from which this error set originates. |
| 209 | /// This is `none` in the case of a comptime/inline function call, corresponding to |
| 210 | /// `InternPool.Index.adhoc_inferred_error_set_type`. |
| 211 | /// The function's resolved error set is not set until analysis of the |
| 212 | /// function body completes. |
| 213 | func: InternPool.Index, |
| 214 | /// All currently known errors that this error set contains. This includes |
| 215 | /// direct additions via `return error.Foo;`, and possibly also errors that |
| 216 | /// are returned from any dependent functions. |
| 217 | errors: NameMap = .{}, |
| 218 | /// Other inferred error sets which this inferred error set should include. |
| 219 | inferred_error_sets: std.array_hash_map.Auto(InternPool.Index, void) = .empty, |
| 220 | /// The regular error set created by resolving this inferred error set. |
| 221 | resolved: InternPool.Index = .none, |
| 222 | |
| 223 | pub const NameMap = std.array_hash_map.Auto(InternPool.NullTerminatedString, void); |
| 224 | |
| 225 | pub fn addErrorSet( |
| 226 | self: *InferredErrorSet, |
| 227 | err_set_ty: Type, |
| 228 | ip: *InternPool, |
| 229 | arena: Allocator, |
| 230 | ) !void { |
| 231 | switch (err_set_ty.toIntern()) { |
| 232 | .anyerror_type => self.resolved = .anyerror_type, |
| 233 | .adhoc_inferred_error_set_type => {}, // Adding an inferred error set to itself. |
| 234 | |
| 235 | else => switch (ip.indexToKey(err_set_ty.toIntern())) { |
| 236 | .error_set_type => |error_set_type| { |
| 237 | for (error_set_type.names.get(ip)) |name| { |
| 238 | try self.errors.put(arena, name, {}); |
| 239 | } |
| 240 | }, |
| 241 | .inferred_error_set_type => { |
| 242 | try self.inferred_error_sets.put(arena, err_set_ty.toIntern(), {}); |
| 243 | }, |
| 244 | else => unreachable, |
| 245 | }, |
| 246 | } |
| 247 | } |
| 248 | }; |
| 249 | |
| 250 | /// Stores the mapping from `Zir.Inst.Index -> Air.Inst.Ref`, which is used by sema to resolve |
| 251 | /// instructions during analysis. |
| 252 | /// Instead of a hash table approach, InstMap is simply a slice that is indexed into using the |
| 253 | /// zir instruction index and a start offset. An index is not present in the map if the value |
| 254 | /// at the index is `Air.Inst.Ref.none`. |
| 255 | /// `ensureSpaceForInstructions` can be called to force InstMap to have a mapped range that |
| 256 | /// includes all instructions in a slice. After calling this function, `putAssumeCapacity*` can |
| 257 | /// be called safely for any of the instructions passed in. |
| 258 | pub const InstMap = struct { |
| 259 | items: []Air.Inst.Ref = &[_]Air.Inst.Ref{}, |
| 260 | start: Zir.Inst.Index = @fromBackingInt(@intCast(0)), |
| 261 | |
| 262 | pub fn deinit(map: InstMap, allocator: mem.Allocator) void { |
| 263 | allocator.free(map.items); |
| 264 | } |
| 265 | |
| 266 | pub fn get(map: InstMap, key: Zir.Inst.Index) ?Air.Inst.Ref { |
| 267 | if (!map.contains(key)) return null; |
| 268 | return map.items[@backingInt(key) - @backingInt(map.start)]; |
| 269 | } |
| 270 | |
| 271 | pub fn putAssumeCapacity( |
| 272 | map: *InstMap, |
| 273 | key: Zir.Inst.Index, |
| 274 | ref: Air.Inst.Ref, |
| 275 | ) void { |
| 276 | map.items[@backingInt(key) - @backingInt(map.start)] = ref; |
| 277 | } |
| 278 | |
| 279 | pub fn putAssumeCapacityNoClobber( |
| 280 | map: *InstMap, |
| 281 | key: Zir.Inst.Index, |
| 282 | ref: Air.Inst.Ref, |
| 283 | ) void { |
| 284 | assert(!map.contains(key)); |
| 285 | map.putAssumeCapacity(key, ref); |
| 286 | } |
| 287 | |
| 288 | pub const GetOrPutResult = struct { |
| 289 | value_ptr: *Air.Inst.Ref, |
| 290 | found_existing: bool, |
| 291 | }; |
| 292 | |
| 293 | pub fn getOrPutAssumeCapacity( |
| 294 | map: *InstMap, |
| 295 | key: Zir.Inst.Index, |
| 296 | ) GetOrPutResult { |
| 297 | const index = @backingInt(key) - @backingInt(map.start); |
| 298 | return GetOrPutResult{ |
| 299 | .value_ptr = &map.items[index], |
| 300 | .found_existing = map.items[index] != .none, |
| 301 | }; |
| 302 | } |
| 303 | |
| 304 | pub fn remove(map: InstMap, key: Zir.Inst.Index) bool { |
| 305 | if (!map.contains(key)) return false; |
| 306 | map.items[@backingInt(key) - @backingInt(map.start)] = .none; |
| 307 | return true; |
| 308 | } |
| 309 | |
| 310 | pub fn contains(map: InstMap, key: Zir.Inst.Index) bool { |
| 311 | return map.items[@backingInt(key) - @backingInt(map.start)] != .none; |
| 312 | } |
| 313 | |
| 314 | pub fn ensureSpaceForInstructions( |
| 315 | map: *InstMap, |
| 316 | allocator: mem.Allocator, |
| 317 | insts: []const Zir.Inst.Index, |
| 318 | ) !void { |
| 319 | const start, const end = mem.minMax(u32, @ptrCast(insts)); |
| 320 | const map_start = @backingInt(map.start); |
| 321 | if (map_start <= start and end < map.items.len + map_start) |
| 322 | return; |
| 323 | |
| 324 | const old_start = if (map.items.len == 0) start else map_start; |
| 325 | var better_capacity = map.items.len; |
| 326 | var better_start = old_start; |
| 327 | while (true) { |
| 328 | const extra_capacity = better_capacity / 2 + 16; |
| 329 | better_capacity += extra_capacity; |
| 330 | better_start -|= @intCast(extra_capacity / 2); |
| 331 | if (better_start <= start and end < better_capacity + better_start) |
| 332 | break; |
| 333 | } |
| 334 | |
| 335 | const start_diff = old_start - better_start; |
| 336 | const new_items = try allocator.alloc(Air.Inst.Ref, better_capacity); |
| 337 | @memset(new_items[0..start_diff], .none); |
| 338 | @memcpy(new_items[start_diff..][0..map.items.len], map.items); |
| 339 | @memset(new_items[start_diff + map.items.len ..], .none); |
| 340 | |
| 341 | allocator.free(map.items); |
| 342 | map.items = new_items; |
| 343 | map.start = @fromBackingInt(@intCast(better_start)); |
| 344 | } |
| 345 | }; |
| 346 | |
| 347 | /// This is the context needed to semantically analyze ZIR instructions and |
| 348 | /// produce AIR instructions. |
| 349 | /// This is a temporary structure stored on the stack; references to it are valid only |
| 350 | /// during semantic analysis of the block. |
| 351 | pub const Block = struct { |
| 352 | parent: ?*Block, |
| 353 | /// Shared among all child blocks. |
| 354 | sema: *Sema, |
| 355 | /// The namespace to use for lookups from this source block |
| 356 | namespace: InternPool.NamespaceIndex, |
| 357 | /// The AIR instructions generated for this block. |
| 358 | instructions: std.ArrayList(Air.Inst.Index), |
| 359 | // `param` instructions are collected here to be used by the `func` instruction. |
| 360 | /// When doing a generic function instantiation, this array collects a type |
| 361 | /// for each *runtime-known* parameter. This array corresponds to the instance |
| 362 | /// function type, while `Sema.comptime_args` corresponds to the generic owner |
| 363 | /// function type. |
| 364 | /// This memory is allocated by a parent `Sema` in the temporary arena, and is |
| 365 | /// used to add a `func_instance` into the `InternPool`. |
| 366 | params: std.MultiArrayList(Param) = .{}, |
| 367 | |
| 368 | label: ?*Label = null, |
| 369 | inlining: ?*Inlining, |
| 370 | /// If runtime_index is not 0 then one of these is guaranteed to be non null. |
| 371 | runtime_cond: ?LazySrcLoc = null, |
| 372 | runtime_loop: ?LazySrcLoc = null, |
| 373 | /// Non zero if a non-inline loop or a runtime conditional have been encountered. |
| 374 | /// Stores to comptime variables are only allowed when var.runtime_index <= runtime_index. |
| 375 | runtime_index: RuntimeIndex = .zero, |
| 376 | inline_block: Zir.Inst.OptionalIndex = .none, |
| 377 | |
| 378 | comptime_reason: ?BlockComptimeReason = null, |
| 379 | is_typeof: bool = false, |
| 380 | |
| 381 | /// Keep track of the active error return trace index around blocks so that we can correctly |
| 382 | /// pop the error trace upon block exit. |
| 383 | error_return_trace_index: Air.Inst.Ref = .none, |
| 384 | |
| 385 | /// when null, it is determined by the owner modules build mode, changed by @setRuntimeSafety |
| 386 | want_safety: ?bool = null, |
| 387 | |
| 388 | /// What mode to generate float operations in, set by @setFloatMode |
| 389 | float_mode: std.lang.FloatMode = .strict, |
| 390 | |
| 391 | /// If not `null`, this boolean is set when a `dbg_var_ptr`, `dbg_var_val`, or `dbg_arg_inline`. |
| 392 | /// instruction is emitted. It signals that the innermost lexically |
| 393 | /// enclosing `block`/`block_inline` should be translated into a real AIR |
| 394 | /// `block` in order for codegen to match lexical scoping for debug vars. |
| 395 | need_debug_scope: ?*bool = null, |
| 396 | |
| 397 | /// Relative source locations encountered while traversing this block should be |
| 398 | /// treated as relative to the AST node of this ZIR instruction. |
| 399 | src_base_inst: InternPool.TrackedInst.Index, |
| 400 | |
| 401 | /// The name of the current "context" for naming namespace types. |
| 402 | /// The interpretation of this depends on the name strategy in ZIR, but the name |
| 403 | /// is always incorporated into the type name somehow. |
| 404 | /// See `Sema.setTypeName`. |
| 405 | type_name_ctx: InternPool.NullTerminatedString, |
| 406 | type_fqn_ctx: InternPool.NullTerminatedString, |
| 407 | |
| 408 | /// Create a `LazySrcLoc` based on an `Offset` from the code being analyzed in this block. |
| 409 | /// Specifically, the given `Offset` is treated as relative to `block.src_base_inst`. |
| 410 | pub fn src(block: Block, offset: LazySrcLoc.Offset) LazySrcLoc { |
| 411 | return .{ |
| 412 | .base_node_inst = block.src_base_inst, |
| 413 | .offset = offset, |
| 414 | }; |
| 415 | } |
| 416 | |
| 417 | fn isComptime(block: Block) bool { |
| 418 | return block.comptime_reason != null; |
| 419 | } |
| 420 | |
| 421 | pub fn builtinCallArgSrc(block: *Block, builtin_call_node: std.zig.Ast.Node.Offset, arg_index: u32) LazySrcLoc { |
| 422 | return block.src(.{ .node_offset_builtin_call_arg = .{ |
| 423 | .builtin_call_node = builtin_call_node, |
| 424 | .arg_index = arg_index, |
| 425 | } }); |
| 426 | } |
| 427 | |
| 428 | pub fn nodeOffset(block: Block, node_offset: std.zig.Ast.Node.Offset) LazySrcLoc { |
| 429 | return block.src(LazySrcLoc.Offset.nodeOffset(node_offset)); |
| 430 | } |
| 431 | |
| 432 | fn tokenOffset(block: Block, tok_offset: std.zig.Ast.TokenOffset) LazySrcLoc { |
| 433 | return block.src(.{ .token_offset = tok_offset }); |
| 434 | } |
| 435 | |
| 436 | const Param = struct { |
| 437 | /// `none` means `anytype`. |
| 438 | ty: InternPool.Index, |
| 439 | is_comptime: bool, |
| 440 | name: Zir.NullTerminatedString, |
| 441 | }; |
| 442 | |
| 443 | /// This `Block` maps a block ZIR instruction to the corresponding |
| 444 | /// AIR instruction for break instruction analysis. |
| 445 | pub const Label = struct { |
| 446 | zir_block: Zir.Inst.Index, |
| 447 | merges: Merges, |
| 448 | }; |
| 449 | |
| 450 | /// This `Block` indicates that an inline function call is happening |
| 451 | /// and return instructions should be analyzed as a break instruction |
| 452 | /// to this AIR block instruction. |
| 453 | /// It is shared among all the blocks in an inline or comptime called |
| 454 | /// function. |
| 455 | pub const Inlining = struct { |
| 456 | call_block: *Block, |
| 457 | call_src: LazySrcLoc, |
| 458 | func: InternPool.Index, |
| 459 | |
| 460 | /// Populated lazily by `refFrame`. |
| 461 | ref_frame: Zcu.InlineReferenceFrame.Index.Optional = .none, |
| 462 | |
| 463 | /// If `true`, the following fields are `undefined`. This doesn't represent a true inline |
| 464 | /// call, but rather a generic call analyzing the instantiation's generic type bodies. |
| 465 | is_generic_instantiation: bool, |
| 466 | |
| 467 | has_comptime_args: bool, |
| 468 | comptime_result: Air.Inst.Ref, |
| 469 | merges: Merges, |
| 470 | |
| 471 | fn refFrame(inlining: *Inlining, zcu: *Zcu) Allocator.Error!Zcu.InlineReferenceFrame.Index { |
| 472 | if (inlining.ref_frame == .none) { |
| 473 | inlining.ref_frame = (try zcu.addInlineReferenceFrame(.{ |
| 474 | .callee = inlining.func, |
| 475 | .call_src = inlining.call_src, |
| 476 | .parent = if (inlining.call_block.inlining) |parent_inlining| p: { |
| 477 | break :p (try parent_inlining.refFrame(zcu)).toOptional(); |
| 478 | } else .none, |
| 479 | })).toOptional(); |
| 480 | } |
| 481 | return inlining.ref_frame.unwrap().?; |
| 482 | } |
| 483 | }; |
| 484 | |
| 485 | pub const Merges = struct { |
| 486 | block_inst: Air.Inst.Index, |
| 487 | /// Separate array list from break_inst_list so that it can be passed directly |
| 488 | /// to resolvePeerTypes. |
| 489 | results: std.ArrayList(Air.Inst.Ref), |
| 490 | /// Keeps track of the break instructions so that the operand can be replaced |
| 491 | /// if we need to add type coercion at the end of block analysis. |
| 492 | /// Same indexes, capacity, length as `results`. |
| 493 | br_list: std.ArrayList(Air.Inst.Index), |
| 494 | /// Keeps the source location of the rhs operand of the break instruction, |
| 495 | /// to enable more precise compile errors. |
| 496 | /// Same indexes, capacity, length as `results`. |
| 497 | src_locs: std.ArrayList(?LazySrcLoc), |
| 498 | /// Most blocks do not utilize this field. When it is used, its use is |
| 499 | /// contextual. The possible uses are as follows: |
| 500 | /// * for a `switch_block[_ref]`, this refers to dummy `br` instructions |
| 501 | /// which correspond to `switch_continue` ZIR. The switch logic will |
| 502 | /// rewrite these to appropriate AIR switch dispatches. |
| 503 | extra_insts: std.ArrayList(Air.Inst.Index) = .empty, |
| 504 | /// Same indexes, capacity, length as `extra_insts`. |
| 505 | extra_src_locs: std.ArrayList(LazySrcLoc) = .empty, |
| 506 | |
| 507 | pub fn deinit(merges: *@This(), allocator: Allocator) void { |
| 508 | merges.results.deinit(allocator); |
| 509 | merges.br_list.deinit(allocator); |
| 510 | merges.src_locs.deinit(allocator); |
| 511 | merges.extra_insts.deinit(allocator); |
| 512 | merges.extra_src_locs.deinit(allocator); |
| 513 | } |
| 514 | }; |
| 515 | |
| 516 | pub fn makeSubBlock(parent: *Block) Block { |
| 517 | return .{ |
| 518 | .parent = parent, |
| 519 | .sema = parent.sema, |
| 520 | .namespace = parent.namespace, |
| 521 | .instructions = .empty, |
| 522 | .label = null, |
| 523 | .inlining = parent.inlining, |
| 524 | .comptime_reason = parent.comptime_reason, |
| 525 | .is_typeof = parent.is_typeof, |
| 526 | .runtime_cond = parent.runtime_cond, |
| 527 | .runtime_loop = parent.runtime_loop, |
| 528 | .runtime_index = parent.runtime_index, |
| 529 | .want_safety = parent.want_safety, |
| 530 | .float_mode = parent.float_mode, |
| 531 | .error_return_trace_index = parent.error_return_trace_index, |
| 532 | .need_debug_scope = parent.need_debug_scope, |
| 533 | .src_base_inst = parent.src_base_inst, |
| 534 | .type_name_ctx = parent.type_name_ctx, |
| 535 | .type_fqn_ctx = parent.type_fqn_ctx, |
| 536 | }; |
| 537 | } |
| 538 | |
| 539 | fn wantSafeTypes(block: *const Block) bool { |
| 540 | return block.want_safety orelse switch (block.ownerModule().optimize_mode) { |
| 541 | .debug => true, |
| 542 | .safe => true, |
| 543 | .fast => false, |
| 544 | .small => false, |
| 545 | }; |
| 546 | } |
| 547 | |
| 548 | fn wantSafety(block: *const Block) bool { |
| 549 | if (block.isComptime()) return false; // runtime safety checks are pointless in comptime blocks |
| 550 | return block.want_safety orelse switch (block.ownerModule().optimize_mode) { |
| 551 | .debug => true, |
| 552 | .safe => true, |
| 553 | .fast => false, |
| 554 | .small => false, |
| 555 | }; |
| 556 | } |
| 557 | |
| 558 | pub fn getFileScope(block: *Block, zcu: *Zcu) *Zcu.File { |
| 559 | return zcu.fileByIndex(getFileScopeIndex(block, zcu)); |
| 560 | } |
| 561 | |
| 562 | pub fn getFileScopeIndex(block: *Block, zcu: *Zcu) Zcu.File.Index { |
| 563 | return zcu.namespacePtr(block.namespace).file_scope; |
| 564 | } |
| 565 | |
| 566 | fn addTy( |
| 567 | block: *Block, |
| 568 | tag: Air.Inst.Tag, |
| 569 | ty: Type, |
| 570 | ) error{OutOfMemory}!Air.Inst.Ref { |
| 571 | return block.addInst(.{ |
| 572 | .tag = tag, |
| 573 | .data = .{ .ty = ty }, |
| 574 | }); |
| 575 | } |
| 576 | |
| 577 | fn addTyOp( |
| 578 | block: *Block, |
| 579 | tag: Air.Inst.Tag, |
| 580 | ty: Type, |
| 581 | operand: Air.Inst.Ref, |
| 582 | ) error{OutOfMemory}!Air.Inst.Ref { |
| 583 | return block.addInst(.{ |
| 584 | .tag = tag, |
| 585 | .data = .{ .ty_op = .{ |
| 586 | .ty = ty, |
| 587 | .operand = operand, |
| 588 | } }, |
| 589 | }); |
| 590 | } |
| 591 | |
| 592 | fn addNoOp(block: *Block, tag: Air.Inst.Tag) error{OutOfMemory}!Air.Inst.Ref { |
| 593 | return block.addInst(.{ |
| 594 | .tag = tag, |
| 595 | .data = .{ .no_op = {} }, |
| 596 | }); |
| 597 | } |
| 598 | |
| 599 | fn addUnOp( |
| 600 | block: *Block, |
| 601 | tag: Air.Inst.Tag, |
| 602 | operand: Air.Inst.Ref, |
| 603 | ) error{OutOfMemory}!Air.Inst.Ref { |
| 604 | return block.addInst(.{ |
| 605 | .tag = tag, |
| 606 | .data = .{ .un_op = operand }, |
| 607 | }); |
| 608 | } |
| 609 | |
| 610 | fn addBr( |
| 611 | block: *Block, |
| 612 | target_block: Air.Inst.Index, |
| 613 | operand: Air.Inst.Ref, |
| 614 | ) error{OutOfMemory}!Air.Inst.Ref { |
| 615 | return block.addInst(.{ |
| 616 | .tag = .br, |
| 617 | .data = .{ .br = .{ |
| 618 | .block_inst = target_block, |
| 619 | .operand = operand, |
| 620 | } }, |
| 621 | }); |
| 622 | } |
| 623 | |
| 624 | fn addBinOp( |
| 625 | block: *Block, |
| 626 | tag: Air.Inst.Tag, |
| 627 | lhs: Air.Inst.Ref, |
| 628 | rhs: Air.Inst.Ref, |
| 629 | ) error{OutOfMemory}!Air.Inst.Ref { |
| 630 | return block.addInst(.{ |
| 631 | .tag = tag, |
| 632 | .data = .{ .bin_op = .{ |
| 633 | .lhs = lhs, |
| 634 | .rhs = rhs, |
| 635 | } }, |
| 636 | }); |
| 637 | } |
| 638 | |
| 639 | fn addStructFieldPtr( |
| 640 | block: *Block, |
| 641 | struct_ptr: Air.Inst.Ref, |
| 642 | field_index: u32, |
| 643 | ptr_field_ty: Type, |
| 644 | ) !Air.Inst.Ref { |
| 645 | const tag: Air.Inst.Tag = switch (field_index) { |
| 646 | 0 => .struct_field_ptr_index_0, |
| 647 | 1 => .struct_field_ptr_index_1, |
| 648 | 2 => .struct_field_ptr_index_2, |
| 649 | 3 => .struct_field_ptr_index_3, |
| 650 | else => { |
| 651 | return block.addInst(.{ |
| 652 | .tag = .struct_field_ptr, |
| 653 | .data = .{ .ty_pl = .{ |
| 654 | .ty = ptr_field_ty, |
| 655 | .payload = try block.sema.addExtra(Air.StructField{ |
| 656 | .struct_operand = struct_ptr, |
| 657 | .field_index = field_index, |
| 658 | }), |
| 659 | } }, |
| 660 | }); |
| 661 | }, |
| 662 | }; |
| 663 | return block.addInst(.{ |
| 664 | .tag = tag, |
| 665 | .data = .{ .ty_op = .{ |
| 666 | .ty = ptr_field_ty, |
| 667 | .operand = struct_ptr, |
| 668 | } }, |
| 669 | }); |
| 670 | } |
| 671 | |
| 672 | fn addStructFieldVal( |
| 673 | block: *Block, |
| 674 | struct_val: Air.Inst.Ref, |
| 675 | field_index: u32, |
| 676 | field_ty: Type, |
| 677 | ) !Air.Inst.Ref { |
| 678 | return block.addInst(.{ |
| 679 | .tag = .agg_field_val, |
| 680 | .data = .{ .ty_pl = .{ |
| 681 | .ty = field_ty, |
| 682 | .payload = try block.sema.addExtra(Air.StructField{ |
| 683 | .struct_operand = struct_val, |
| 684 | .field_index = field_index, |
| 685 | }), |
| 686 | } }, |
| 687 | }); |
| 688 | } |
| 689 | |
| 690 | fn addSliceElemPtr( |
| 691 | block: *Block, |
| 692 | slice: Air.Inst.Ref, |
| 693 | elem_index: Air.Inst.Ref, |
| 694 | elem_ptr_ty: Type, |
| 695 | ) !Air.Inst.Ref { |
| 696 | return block.addInst(.{ |
| 697 | .tag = .slice_elem_ptr, |
| 698 | .data = .{ .ty_pl = .{ |
| 699 | .ty = elem_ptr_ty, |
| 700 | .payload = try block.sema.addExtra(Air.Bin{ |
| 701 | .lhs = slice, |
| 702 | .rhs = elem_index, |
| 703 | }), |
| 704 | } }, |
| 705 | }); |
| 706 | } |
| 707 | |
| 708 | fn addPtrElemPtr( |
| 709 | block: *Block, |
| 710 | array_ptr: Air.Inst.Ref, |
| 711 | elem_index: Air.Inst.Ref, |
| 712 | elem_ptr_ty: Type, |
| 713 | ) !Air.Inst.Ref { |
| 714 | return block.addInst(.{ |
| 715 | .tag = .ptr_elem_ptr, |
| 716 | .data = .{ .ty_pl = .{ |
| 717 | .ty = elem_ptr_ty, |
| 718 | .payload = try block.sema.addExtra(Air.Bin{ |
| 719 | .lhs = array_ptr, |
| 720 | .rhs = elem_index, |
| 721 | }), |
| 722 | } }, |
| 723 | }); |
| 724 | } |
| 725 | |
| 726 | fn addCmpVector(block: *Block, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref, cmp_op: std.math.CompareOperator) !Air.Inst.Ref { |
| 727 | const sema = block.sema; |
| 728 | const pt = sema.pt; |
| 729 | const zcu = pt.zcu; |
| 730 | return block.addInst(.{ |
| 731 | .tag = if (block.float_mode == .optimized) .cmp_vector_optimized else .cmp_vector, |
| 732 | .data = .{ .ty_pl = .{ |
| 733 | .ty = (try pt.vectorType(.{ |
| 734 | .len = sema.typeOf(lhs).vectorLen(zcu), |
| 735 | .child = .bool_type, |
| 736 | })), |
| 737 | .payload = try sema.addExtra(Air.VectorCmp{ |
| 738 | .lhs = lhs, |
| 739 | .rhs = rhs, |
| 740 | .op = Air.VectorCmp.encodeOp(cmp_op), |
| 741 | }), |
| 742 | } }, |
| 743 | }); |
| 744 | } |
| 745 | |
| 746 | fn addReduce(block: *Block, operand: Air.Inst.Ref, operation: std.lang.ReduceOp) !Air.Inst.Ref { |
| 747 | const sema = block.sema; |
| 748 | const zcu = sema.pt.zcu; |
| 749 | const allow_optimized = switch (sema.typeOf(operand).childType(zcu).zigTypeTag(zcu)) { |
| 750 | .float => true, |
| 751 | .bool, .int => false, |
| 752 | else => unreachable, |
| 753 | }; |
| 754 | return block.addInst(.{ |
| 755 | .tag = if (allow_optimized and block.float_mode == .optimized) .reduce_optimized else .reduce, |
| 756 | .data = .{ .reduce = .{ |
| 757 | .operand = operand, |
| 758 | .operation = operation, |
| 759 | } }, |
| 760 | }); |
| 761 | } |
| 762 | |
| 763 | fn addAggregateInit( |
| 764 | block: *Block, |
| 765 | aggregate_ty: Type, |
| 766 | elements: []const Air.Inst.Ref, |
| 767 | ) !Air.Inst.Ref { |
| 768 | const sema = block.sema; |
| 769 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, elements.len); |
| 770 | const extra_index: u32 = @intCast(sema.air_extra.items.len); |
| 771 | sema.appendRefsAssumeCapacity(elements); |
| 772 | |
| 773 | return block.addInst(.{ |
| 774 | .tag = .aggregate_init, |
| 775 | .data = .{ .ty_pl = .{ |
| 776 | .ty = aggregate_ty, |
| 777 | .payload = extra_index, |
| 778 | } }, |
| 779 | }); |
| 780 | } |
| 781 | |
| 782 | fn addUnionInit( |
| 783 | block: *Block, |
| 784 | union_ty: Type, |
| 785 | field_index: u32, |
| 786 | init: Air.Inst.Ref, |
| 787 | ) !Air.Inst.Ref { |
| 788 | return block.addInst(.{ |
| 789 | .tag = .union_init, |
| 790 | .data = .{ .ty_pl = .{ |
| 791 | .ty = union_ty, |
| 792 | .payload = try block.sema.addExtra(Air.UnionInit{ |
| 793 | .field_index = field_index, |
| 794 | .init = init, |
| 795 | }), |
| 796 | } }, |
| 797 | }); |
| 798 | } |
| 799 | |
| 800 | pub fn addInst(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref { |
| 801 | return (try block.addInstAsIndex(inst)).toRef(); |
| 802 | } |
| 803 | |
| 804 | pub fn addInstAsIndex(block: *Block, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index { |
| 805 | const sema = block.sema; |
| 806 | const gpa = sema.gpa; |
| 807 | |
| 808 | try sema.air_instructions.ensureUnusedCapacity(gpa, 1); |
| 809 | try block.instructions.ensureUnusedCapacity(gpa, 1); |
| 810 | |
| 811 | const result_index: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len)); |
| 812 | sema.air_instructions.appendAssumeCapacity(inst); |
| 813 | block.instructions.appendAssumeCapacity(result_index); |
| 814 | return result_index; |
| 815 | } |
| 816 | |
| 817 | /// Insert an instruction into the block at `index`. Moves all following |
| 818 | /// instructions forward in the block to make room. Operation is O(N). |
| 819 | pub fn insertInst(block: *Block, index: Air.Inst.Index, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Ref { |
| 820 | return (try block.insertInstAsIndex(index, inst)).toRef(); |
| 821 | } |
| 822 | |
| 823 | pub fn insertInstAsIndex(block: *Block, index: Air.Inst.Index, inst: Air.Inst) error{OutOfMemory}!Air.Inst.Index { |
| 824 | const sema = block.sema; |
| 825 | const gpa = sema.gpa; |
| 826 | |
| 827 | try sema.air_instructions.ensureUnusedCapacity(gpa, 1); |
| 828 | |
| 829 | const result_index: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len)); |
| 830 | sema.air_instructions.appendAssumeCapacity(inst); |
| 831 | |
| 832 | try block.instructions.insert(gpa, @backingInt(index), result_index); |
| 833 | return result_index; |
| 834 | } |
| 835 | |
| 836 | pub fn ownerModule(block: Block) *Module { |
| 837 | const zcu = block.sema.pt.zcu; |
| 838 | return zcu.namespacePtr(block.namespace).fileScope(zcu).mod.?; |
| 839 | } |
| 840 | |
| 841 | fn trackZir(block: *Block, inst: Zir.Inst.Index) Allocator.Error!InternPool.TrackedInst.Index { |
| 842 | const pt = block.sema.pt; |
| 843 | const comp = pt.zcu.comp; |
| 844 | block.sema.code.assertTrackable(inst); |
| 845 | return pt.zcu.intern_pool.trackZir(comp.gpa, comp.io, pt.tid, .{ |
| 846 | .file = block.getFileScopeIndex(pt.zcu), |
| 847 | .inst = inst, |
| 848 | }); |
| 849 | } |
| 850 | |
| 851 | /// Returns the `*Block` that should be passed to `Sema.failWithOwnedErrorMsg`, because all inline |
| 852 | /// calls below it have already been reported with "called at comptime from here" notes. |
| 853 | fn explainWhyBlockIsComptime(start_block: *Block, err_msg: *Zcu.ErrorMsg) !*Block { |
| 854 | const sema = start_block.sema; |
| 855 | var block = start_block; |
| 856 | while (true) { |
| 857 | switch (block.comptime_reason.?) { |
| 858 | .inlining_parent => { |
| 859 | const inlining = block.inlining.?; |
| 860 | try sema.errNote(inlining.call_src, err_msg, "called at comptime from here", .{}); |
| 861 | block = inlining.call_block; |
| 862 | }, |
| 863 | .reason => |r| { |
| 864 | try r.r.explain(sema, r.src, err_msg); |
| 865 | return block; |
| 866 | }, |
| 867 | } |
| 868 | } |
| 869 | } |
| 870 | }; |
| 871 | |
| 872 | /// Represents the reason we are resolving a value or evaluating code at comptime. |
| 873 | /// Most reasons are represented by a `std.zig.SimpleComptimeReason`, which provides a plain message. |
| 874 | const ComptimeReason = union(enum) { |
| 875 | /// Evaluating at comptime for a reason in the `std.zig.SimpleComptimeReason` enum. |
| 876 | simple: std.zig.SimpleComptimeReason, |
| 877 | |
| 878 | /// Evaluating at comptime because of a comptime-only type. This field is separate so that |
| 879 | /// the type in question can be included in the error message. AstGen could never emit this |
| 880 | /// reason, because it knows nothing of types. |
| 881 | /// The format string looks like "foo '{f}' bar", where "{f}" is the comptime-only type. |
| 882 | /// We will then explain why this type is comptime-only. |
| 883 | comptime_only: struct { |
| 884 | ty: Type, |
| 885 | msg: enum { |
| 886 | union_init, |
| 887 | struct_init, |
| 888 | tuple_init, |
| 889 | }, |
| 890 | }, |
| 891 | |
| 892 | /// Like `comptime_only`, but for a parameter type. |
| 893 | /// Includes a "parameter type declared here" note. |
| 894 | comptime_only_param_ty: struct { |
| 895 | ty: Type, |
| 896 | param_ty_src: LazySrcLoc, |
| 897 | }, |
| 898 | |
| 899 | /// Like `comptime_only`, but for a return type. |
| 900 | /// Includes a "return type declared here" note. |
| 901 | comptime_only_ret_ty: struct { |
| 902 | ty: Type, |
| 903 | is_generic_inst: bool, |
| 904 | ret_ty_src: LazySrcLoc, |
| 905 | }, |
| 906 | |
| 907 | /// Evaluating at comptime because we're evaluating an argument to a parameter marked `comptime`. |
| 908 | comptime_param: struct { |
| 909 | comptime_src: LazySrcLoc, |
| 910 | }, |
| 911 | |
| 912 | fn explain(reason: ComptimeReason, sema: *Sema, src: LazySrcLoc, err_msg: *Zcu.ErrorMsg) !void { |
| 913 | switch (reason) { |
| 914 | .simple => |simple| { |
| 915 | try sema.errNote(src, err_msg, "{s}", .{simple.message()}); |
| 916 | }, |
| 917 | .comptime_only => |co| { |
| 918 | const pre, const post = switch (co.msg) { |
| 919 | .union_init => .{ "initializer of comptime-only union", "must be comptime-known" }, |
| 920 | .struct_init => .{ "initializer of comptime-only struct", "must be comptime-known" }, |
| 921 | .tuple_init => .{ "initializer of comptime-only tuple", "must be comptime-known" }, |
| 922 | }; |
| 923 | try sema.errNote(src, err_msg, "{s} '{f}' {s}", .{ pre, co.ty.fmt(sema.pt), post }); |
| 924 | try sema.explainWhyTypeIsComptime(err_msg, src, co.ty); |
| 925 | }, |
| 926 | .comptime_only_param_ty => |co| { |
| 927 | try sema.errNote(src, err_msg, "argument to parameter with comptime-only type '{f}' must be comptime-known", .{co.ty.fmt(sema.pt)}); |
| 928 | try sema.errNote(co.param_ty_src, err_msg, "parameter type declared here", .{}); |
| 929 | try sema.explainWhyTypeIsComptime(err_msg, src, co.ty); |
| 930 | }, |
| 931 | .comptime_only_ret_ty => |co| { |
| 932 | const function_with: []const u8 = if (co.is_generic_inst) "generic function instantiated with" else "function with"; |
| 933 | try sema.errNote(src, err_msg, "call to {s} comptime-only return type '{f}' is evaluated at comptime", .{ function_with, co.ty.fmt(sema.pt) }); |
| 934 | try sema.errNote(co.ret_ty_src, err_msg, "return type declared here", .{}); |
| 935 | try sema.explainWhyTypeIsComptime(err_msg, src, co.ty); |
| 936 | }, |
| 937 | .comptime_param => |cp| { |
| 938 | try sema.errNote(src, err_msg, "argument to comptime parameter must be comptime-known", .{}); |
| 939 | try sema.errNote(cp.comptime_src, err_msg, "parameter declared comptime here", .{}); |
| 940 | }, |
| 941 | } |
| 942 | } |
| 943 | }; |
| 944 | |
| 945 | /// Represents the reason a `Block` is being evaluated at comptime. |
| 946 | const BlockComptimeReason = union(enum) { |
| 947 | /// This block inherits being comptime-only from the `inlining` call site. |
| 948 | inlining_parent, |
| 949 | |
| 950 | /// Comptime evaluation began somewhere in the current function for a given `ComptimeReason`. |
| 951 | reason: struct { |
| 952 | /// The source location which this reason originates from. `r` is reported here. |
| 953 | src: LazySrcLoc, |
| 954 | r: ComptimeReason, |
| 955 | }, |
| 956 | }; |
| 957 | |
| 958 | const LabeledBlock = struct { |
| 959 | block: Block, |
| 960 | label: Block.Label, |
| 961 | |
| 962 | fn destroy(lb: *LabeledBlock, gpa: Allocator) void { |
| 963 | lb.block.instructions.deinit(gpa); |
| 964 | lb.label.merges.deinit(gpa); |
| 965 | gpa.destroy(lb); |
| 966 | } |
| 967 | }; |
| 968 | |
| 969 | /// The value stored in the inferred allocation. This will go into |
| 970 | /// peer type resolution. This is stored in a separate list so that |
| 971 | /// the items are contiguous in memory and thus can be passed to |
| 972 | /// `Zcu.resolvePeerTypes`. |
| 973 | const InferredAlloc = struct { |
| 974 | /// The placeholder `store` instructions used before the result pointer type |
| 975 | /// is known. These should be rewritten to perform any required coercions |
| 976 | /// when the type is resolved. |
| 977 | /// Allocated from `sema.arena`. |
| 978 | prongs: std.ArrayList(Air.Inst.Index) = .empty, |
| 979 | }; |
| 980 | |
| 981 | pub fn deinit(sema: *Sema) void { |
| 982 | const gpa = sema.gpa; |
| 983 | sema.air_instructions.deinit(gpa); |
| 984 | sema.air_extra.deinit(gpa); |
| 985 | sema.inst_map.deinit(gpa); |
| 986 | { |
| 987 | var it = sema.post_hoc_blocks.iterator(); |
| 988 | while (it.next()) |entry| { |
| 989 | const labeled_block = entry.value_ptr.*; |
| 990 | labeled_block.destroy(gpa); |
| 991 | } |
| 992 | sema.post_hoc_blocks.deinit(gpa); |
| 993 | } |
| 994 | sema.unresolved_inferred_allocs.deinit(gpa); |
| 995 | sema.base_allocs.deinit(gpa); |
| 996 | sema.maybe_comptime_allocs.deinit(gpa); |
| 997 | sema.comptime_allocs.deinit(gpa); |
| 998 | sema.exports.deinit(gpa); |
| 999 | sema.references.deinit(gpa); |
| 1000 | sema.type_references.deinit(gpa); |
| 1001 | sema.dependencies.deinit(gpa); |
| 1002 | sema.* = undefined; |
| 1003 | } |
| 1004 | |
| 1005 | /// Performs semantic analysis of a ZIR body which is behind a runtime condition. If comptime |
| 1006 | /// control flow happens here, Sema will convert it to runtime control flow by introducing post-hoc |
| 1007 | /// blocks where necessary. |
| 1008 | /// Returns the branch hint for this branch. |
| 1009 | fn analyzeBodyRuntimeBreak(sema: *Sema, block: *Block, body: []const Zir.Inst.Index) !std.lang.BranchHint { |
| 1010 | const parent_hint = sema.branch_hint; |
| 1011 | defer sema.branch_hint = parent_hint; |
| 1012 | sema.branch_hint = null; |
| 1013 | |
| 1014 | sema.analyzeBodyInner(block, body) catch |err| switch (err) { |
| 1015 | error.ComptimeBreak => { |
| 1016 | const zir_datas = sema.code.instructions.items(.data); |
| 1017 | const break_data = zir_datas[@backingInt(sema.comptime_break_inst)].@"break"; |
| 1018 | const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; |
| 1019 | try sema.addRuntimeBreak(block, extra.block_inst, break_data.operand); |
| 1020 | }, |
| 1021 | else => |e| return e, |
| 1022 | }; |
| 1023 | |
| 1024 | return sema.branch_hint orelse .none; |
| 1025 | } |
| 1026 | |
| 1027 | /// Semantically analyze a ZIR function body. It is guaranteed by AstGen that such a body cannot |
| 1028 | /// trigger comptime control flow to move above the function body. |
| 1029 | pub fn analyzeFnBody( |
| 1030 | sema: *Sema, |
| 1031 | block: *Block, |
| 1032 | body: []const Zir.Inst.Index, |
| 1033 | ) !void { |
| 1034 | sema.analyzeBodyInner(block, body) catch |err| switch (err) { |
| 1035 | error.ComptimeBreak => unreachable, // unexpected comptime control flow |
| 1036 | else => |e| return e, |
| 1037 | }; |
| 1038 | } |
| 1039 | |
| 1040 | /// Given a ZIR body which can be exited via a `break_inline` instruction, or a non-inline body which |
| 1041 | /// we are evaluating at comptime, semantically analyze the body and return the result from it. |
| 1042 | /// Returns `null` if control flow did not break from this block, but instead terminated with some |
| 1043 | /// other runtime noreturn instruction. Compile-time breaks to blocks further up the stack still |
| 1044 | /// return `error.ComptimeBreak`. If `block.isComptime()`, this function will never return `null`. |
| 1045 | fn analyzeInlineBody( |
| 1046 | sema: *Sema, |
| 1047 | block: *Block, |
| 1048 | body: []const Zir.Inst.Index, |
| 1049 | /// The index which a break instruction can target to break from this body. |
| 1050 | break_target: Zir.Inst.Index, |
| 1051 | ) CompileError!?Air.Inst.Ref { |
| 1052 | if (sema.analyzeBodyInner(block, body)) { |
| 1053 | return null; |
| 1054 | } else |err| switch (err) { |
| 1055 | error.ComptimeBreak => {}, |
| 1056 | else => |e| return e, |
| 1057 | } |
| 1058 | const break_inst = sema.code.instructions.get(@backingInt(sema.comptime_break_inst)); |
| 1059 | switch (break_inst.tag) { |
| 1060 | .switch_continue => { |
| 1061 | // This is handled by separate logic. |
| 1062 | return error.ComptimeBreak; |
| 1063 | }, |
| 1064 | .break_inline, .@"break" => {}, |
| 1065 | else => unreachable, |
| 1066 | } |
| 1067 | const extra = sema.code.extraData(Zir.Inst.Break, break_inst.data.@"break".payload_index).data; |
| 1068 | if (extra.block_inst != break_target) { |
| 1069 | // This control flow goes further up the stack. |
| 1070 | return error.ComptimeBreak; |
| 1071 | } |
| 1072 | return sema.resolveInst(break_inst.data.@"break".operand); |
| 1073 | } |
| 1074 | |
| 1075 | /// Like `analyzeInlineBody`, but if the body does not break with a value, returns |
| 1076 | /// `.unreachable_value` instead of `null`. Notably, use this to evaluate an arbitrary |
| 1077 | /// body at comptime to a single result value. |
| 1078 | pub fn resolveInlineBody( |
| 1079 | sema: *Sema, |
| 1080 | block: *Block, |
| 1081 | body: []const Zir.Inst.Index, |
| 1082 | /// The index which a break instruction can target to break from this body. |
| 1083 | break_target: Zir.Inst.Index, |
| 1084 | ) CompileError!Air.Inst.Ref { |
| 1085 | return (try sema.analyzeInlineBody(block, body, break_target)) orelse .unreachable_value; |
| 1086 | } |
| 1087 | |
| 1088 | /// This function is the main loop of `Sema`. It analyzes a single body of ZIR instructions. |
| 1089 | /// |
| 1090 | /// If this function returns normally, the merges of `block` were populated with all possible |
| 1091 | /// (runtime) results of this block. Peer type resolution should be performed on the result, |
| 1092 | /// and relevant runtime instructions written to perform necessary coercions and breaks. See |
| 1093 | /// `resolveAnalyzedBlock`. This form of return is impossible if `block.isComptime()`. |
| 1094 | /// |
| 1095 | /// Alternatively, this function may return `error.ComptimeBreak`. This indicates that comptime |
| 1096 | /// control flow is happening, and we are breaking at comptime from a block indicated by the |
| 1097 | /// break instruction in `sema.comptime_break_inst`. This occurs for any `break_inline`, or for a |
| 1098 | /// standard `break` at comptime. This error is pushed up the stack until the target block is |
| 1099 | /// reached, at which point the break operand will be fetched. |
| 1100 | /// |
| 1101 | /// It is rare to call this function directly. Usually, you want one of the following wrappers: |
| 1102 | /// * If the body is exited via a `break_inline`, or is being evaluated at comptime, |
| 1103 | /// use `Sema.analyzeInlineBody` or `Sema.resolveInlineBody`. |
| 1104 | /// * If the body is behind a fresh runtime condition, use `Sema.analyzeBodyRuntimeBreak`. |
| 1105 | /// * If the body is an entire function body, use `Sema.analyzeFnBody`. |
| 1106 | /// * If the body is to be generated into an AIR `block`, use `Sema.resolveBlockBody`. |
| 1107 | /// * Otherwise, direct usage of `Sema.analyzeBodyInner` may be necessary. |
| 1108 | fn analyzeBodyInner( |
| 1109 | sema: *Sema, |
| 1110 | block: *Block, |
| 1111 | body: []const Zir.Inst.Index, |
| 1112 | ) CompileError!void { |
| 1113 | try sema.inst_map.ensureSpaceForInstructions(sema.gpa, body); |
| 1114 | |
| 1115 | const pt = sema.pt; |
| 1116 | const zcu = pt.zcu; |
| 1117 | const map = &sema.inst_map; |
| 1118 | const tags = sema.code.instructions.items(.tag); |
| 1119 | const datas = sema.code.instructions.items(.data); |
| 1120 | |
| 1121 | var crash_info: crash_report.AnalyzeBody = undefined; |
| 1122 | crash_info.push(sema, block, body); |
| 1123 | defer crash_info.pop(); |
| 1124 | |
| 1125 | // We use a while (true) loop here to avoid a redundant way of breaking out of |
| 1126 | // the loop. The only way to break out of the loop is with a `noreturn` |
| 1127 | // instruction. |
| 1128 | var i: u32 = 0; |
| 1129 | while (true) { |
| 1130 | crash_info.setBodyIndex(i); |
| 1131 | const inst = body[i]; |
| 1132 | |
| 1133 | // The hashmap lookup in here is a little expensive, and LLVM fails to optimize it away. |
| 1134 | if (build_options.enable_logging) { |
| 1135 | std.log.scoped(.sema_zir).debug("sema ZIR {f} %{d}", .{ path: { |
| 1136 | const file_index = block.src_base_inst.resolveFile(&zcu.intern_pool); |
| 1137 | const file = zcu.fileByIndex(file_index); |
| 1138 | break :path file.path.fmt(zcu.comp); |
| 1139 | }, inst }); |
| 1140 | } |
| 1141 | |
| 1142 | const air_ref: Air.Inst.Ref = inst: switch (tags[@backingInt(inst)]) { |
| 1143 | // zig fmt: off |
| 1144 | .alloc => try sema.zirAlloc(block, inst), |
| 1145 | .alloc_inferred => try sema.zirAllocInferred(block, true), |
| 1146 | .alloc_inferred_mut => try sema.zirAllocInferred(block, false), |
| 1147 | .alloc_inferred_comptime => try sema.zirAllocInferredComptime(true), |
| 1148 | .alloc_inferred_comptime_mut => try sema.zirAllocInferredComptime(false), |
| 1149 | .resolve_inferred_alloc => try sema.zirResolveInferredAlloc(block, inst), |
| 1150 | .alloc_mut => try sema.zirAllocMut(block, inst), |
| 1151 | .alloc_comptime_mut => try sema.zirAllocComptime(block, inst), |
| 1152 | .make_ptr_const => try sema.zirMakePtrConst(block, inst), |
| 1153 | .anyframe_type => try sema.zirAnyframeType(block, inst), |
| 1154 | .array_cat => try sema.zirArrayCat(block, inst), |
| 1155 | .array_type => try sema.zirArrayType(block, inst), |
| 1156 | .array_type_sentinel => try sema.zirArrayTypeSentinel(block, inst), |
| 1157 | .reify_int => try sema.zirReifyInt(block, inst), |
| 1158 | .vector_type => try sema.zirVectorType(block, inst), |
| 1159 | .as_node => try sema.zirAsNode(block, inst), |
| 1160 | .as_shift_operand => try sema.zirAsShiftOperand(block, inst), |
| 1161 | .bit_and => try sema.zirBitwise(block, inst, .bit_and), |
| 1162 | .bit_not => try sema.zirBitNot(block, inst), |
| 1163 | .bit_or => try sema.zirBitwise(block, inst, .bit_or), |
| 1164 | .bitcast => try sema.zirBitcast(block, inst), |
| 1165 | .suspend_block => try sema.zirSuspendBlock(block, inst), |
| 1166 | .bool_not => try sema.zirBoolNot(block, inst), |
| 1167 | .bool_br_and => try sema.zirBoolBr(block, inst, false), |
| 1168 | .bool_br_or => try sema.zirBoolBr(block, inst, true), |
| 1169 | .call => try sema.zirCall(block, inst, .direct), |
| 1170 | .field_call => try sema.zirCall(block, inst, .field), |
| 1171 | .cmp_lt => try sema.zirCmp(block, inst, .lt), |
| 1172 | .cmp_lte => try sema.zirCmp(block, inst, .lte), |
| 1173 | .cmp_eq => try sema.zirCmpEq(block, inst, .eq, Air.Inst.Tag.fromCmpOp(.eq, block.float_mode == .optimized)), |
| 1174 | .cmp_gte => try sema.zirCmp(block, inst, .gte), |
| 1175 | .cmp_gt => try sema.zirCmp(block, inst, .gt), |
| 1176 | .cmp_neq => try sema.zirCmpEq(block, inst, .neq, Air.Inst.Tag.fromCmpOp(.neq, block.float_mode == .optimized)), |
| 1177 | .decl_ref => try sema.zirDeclRef(block, inst), |
| 1178 | .decl_val => try sema.zirDeclVal(block, inst), |
| 1179 | .load => try sema.zirLoad(block, inst), |
| 1180 | .elem_ptr => try sema.zirElemPtr(block, inst), |
| 1181 | .elem_ptr_node => try sema.zirElemPtrNode(block, inst), |
| 1182 | .elem_val => try sema.zirElemVal(block, inst), |
| 1183 | .elem_ptr_load => try sema.zirElemPtrLoad(block, inst), |
| 1184 | .elem_val_imm => try sema.zirElemValImm(block, inst), |
| 1185 | .elem_type => try sema.zirElemType(block, inst), |
| 1186 | .indexable_ptr_elem_type => try sema.zirIndexablePtrElemType(block, inst), |
| 1187 | .splat_op_result_ty => try sema.zirSplatOpResultType(block, inst), |
| 1188 | .from_backing_int_arg_ty => try sema.zirFromBackingIntArgTy(block, inst), |
| 1189 | .enum_literal => try sema.zirEnumLiteral(block, inst), |
| 1190 | .decl_literal => try sema.zirDeclLiteral(block, inst, true), |
| 1191 | .decl_literal_no_coerce => try sema.zirDeclLiteral(block, inst, false), |
| 1192 | .int_from_enum => try sema.zirIntFromEnum(block, inst), |
| 1193 | .enum_from_int => try sema.zirEnumFromInt(block, inst), |
| 1194 | .backing_int => try sema.zirBackingInt(block, inst), |
| 1195 | .from_backing_int => try sema.zirFromBackingInt(block, inst), |
| 1196 | .err_union_code => try sema.zirErrUnionCode(block, inst), |
| 1197 | .err_union_code_ptr => try sema.zirErrUnionCodePtr(block, inst), |
| 1198 | .err_union_payload_unsafe => try sema.zirErrUnionPayload(block, inst), |
| 1199 | .err_union_payload_unsafe_ptr => try sema.zirErrUnionPayloadPtr(block, inst), |
| 1200 | .error_union_type => try sema.zirErrorUnionType(block, inst), |
| 1201 | .error_value => try sema.zirErrorValue(block, inst), |
| 1202 | .field_ptr => try sema.zirFieldPtr(block, inst), |
| 1203 | .field_ptr_named => try sema.zirFieldPtrNamed(block, inst), |
| 1204 | .field_ptr_load => try sema.zirFieldPtrLoad(block, inst), |
| 1205 | .field_ptr_named_load => try sema.zirFieldPtrNamedLoad(block, inst), |
| 1206 | .func => try sema.zirFunc(block, inst, false), |
| 1207 | .func_inferred => try sema.zirFunc(block, inst, true), |
| 1208 | .func_fancy => try sema.zirFuncFancy(block, inst), |
| 1209 | .import => try sema.zirImport(block, inst), |
| 1210 | .indexable_ptr_len => try sema.zirIndexablePtrLen(block, inst), |
| 1211 | .int => try sema.zirInt(block, inst), |
| 1212 | .int_big => try sema.zirIntBig(block, inst), |
| 1213 | .float => try sema.zirFloat(block, inst), |
| 1214 | .float128 => try sema.zirFloat128(block, inst), |
| 1215 | .int_type => try sema.zirIntType(inst), |
| 1216 | .is_non_err => try sema.zirIsNonErr(block, inst), |
| 1217 | .is_non_err_ptr => try sema.zirIsNonErrPtr(block, inst), |
| 1218 | .ret_is_non_err => try sema.zirRetIsNonErr(block, inst), |
| 1219 | .is_non_null => try sema.zirIsNonNull(block, inst), |
| 1220 | .is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst), |
| 1221 | .merge_error_sets => try sema.zirMergeErrorSets(block, inst), |
| 1222 | .negate => try sema.zirNegate(block, inst), |
| 1223 | .negate_wrap => try sema.zirNegateWrap(block, inst), |
| 1224 | .optional_payload_safe => try sema.zirOptionalPayload(block, inst, true), |
| 1225 | .optional_payload_safe_ptr => try sema.zirOptionalPayloadPtr(block, inst, true), |
| 1226 | .optional_payload_unsafe => try sema.zirOptionalPayload(block, inst, false), |
| 1227 | .optional_payload_unsafe_ptr => try sema.zirOptionalPayloadPtr(block, inst, false), |
| 1228 | .optional_type => try sema.zirOptionalType(block, inst), |
| 1229 | .ptr_type => try sema.zirPtrType(block, inst), |
| 1230 | .ref => try sema.zirRef(block, inst), |
| 1231 | .deref => try sema.zirDeref(block, inst), |
| 1232 | .ref_deref => try sema.zirRefDeref(block, inst), |
| 1233 | .shr => try sema.zirShr(block, inst, .shr), |
| 1234 | .shr_exact => try sema.zirShr(block, inst, .shr_exact), |
| 1235 | .slice_end => try sema.zirSliceEnd(block, inst), |
| 1236 | .slice_sentinel => try sema.zirSliceSentinel(block, inst), |
| 1237 | .slice_start => try sema.zirSliceStart(block, inst), |
| 1238 | .slice_length => try sema.zirSliceLength(block, inst), |
| 1239 | .slice_sentinel_ty => try sema.zirSliceSentinelTy(block, inst), |
| 1240 | .str => try sema.zirStr(inst), |
| 1241 | .switch_block => try sema.zirSwitchBlock(block, inst, false), |
| 1242 | .switch_block_ref => try sema.zirSwitchBlock(block, inst, true), |
| 1243 | .switch_block_err_union => try sema.zirSwitchBlockErrUnion(block, inst), |
| 1244 | .type_info => try sema.zirTypeInfo(block, inst), |
| 1245 | .size_of => try sema.zirSizeOf(block, inst), |
| 1246 | .bit_size_of => try sema.zirBitSizeOf(block, inst), |
| 1247 | .typeof => try sema.zirTypeof(block, inst), |
| 1248 | .typeof_builtin => try sema.zirTypeofBuiltin(block, inst), |
| 1249 | .typeof_log2_int_type => try sema.zirTypeofLog2IntType(block, inst), |
| 1250 | .xor => try sema.zirBitwise(block, inst, .xor), |
| 1251 | .struct_init_empty => try sema.zirStructInitEmpty(block, inst), |
| 1252 | .struct_init_empty_result => try sema.zirStructInitEmptyResult(block, inst, false), |
| 1253 | .struct_init_empty_ref_result => try sema.zirStructInitEmptyResult(block, inst, true), |
| 1254 | .struct_init_anon => try sema.zirStructInitAnon(block, inst), |
| 1255 | .struct_init => try sema.zirStructInit(block, inst, false), |
| 1256 | .struct_init_ref => try sema.zirStructInit(block, inst, true), |
| 1257 | .struct_init_field_type => try sema.zirStructInitFieldType(block, inst), |
| 1258 | .struct_init_field_ptr => try sema.zirStructInitFieldPtr(block, inst), |
| 1259 | .array_init_anon => try sema.zirArrayInitAnon(block, inst), |
| 1260 | .array_init => try sema.zirArrayInit(block, inst, false), |
| 1261 | .array_init_ref => try sema.zirArrayInit(block, inst, true), |
| 1262 | .array_init_elem_type => try sema.zirArrayInitElemType(block, inst), |
| 1263 | .array_init_elem_ptr => try sema.zirArrayInitElemPtr(block, inst), |
| 1264 | .union_init => try sema.zirUnionInit(block, inst), |
| 1265 | .field_type_ref => try sema.zirFieldTypeRef(block, inst), |
| 1266 | .int_from_ptr => try sema.zirIntFromPtr(block, inst), |
| 1267 | .align_of => try sema.zirAlignOf(block, inst), |
| 1268 | .int_from_bool => try sema.zirIntFromBool(block, inst), |
| 1269 | .embed_file => try sema.zirEmbedFile(block, inst), |
| 1270 | .error_name => try sema.zirErrorName(block, inst), |
| 1271 | .tag_name => try sema.zirTagName(block, inst), |
| 1272 | .type_name => try sema.zirTypeName(block, inst), |
| 1273 | .frame_type => try sema.zirFrameType(block, inst), |
| 1274 | .int_from_float => try sema.zirIntFromFloat(block, inst), |
| 1275 | .float_from_int => try sema.zirFloatFromInt(block, inst), |
| 1276 | .ptr_from_int => try sema.zirPtrFromInt(block, inst), |
| 1277 | .float_cast => try sema.zirFloatCast(block, inst), |
| 1278 | .int_cast => try sema.zirIntCast(block, inst), |
| 1279 | .ptr_cast => try sema.zirPtrCast(block, inst), |
| 1280 | .truncate => try sema.zirTruncate(block, inst), |
| 1281 | .has_decl => try sema.zirHasDecl(block, inst), |
| 1282 | .has_field => try sema.zirHasField(block, inst), |
| 1283 | .byte_swap => try sema.zirByteSwap(block, inst), |
| 1284 | .bit_reverse => try sema.zirBitReverse(block, inst), |
| 1285 | .bit_offset_of => try sema.zirBitOffsetOf(block, inst), |
| 1286 | .offset_of => try sema.zirOffsetOf(block, inst), |
| 1287 | .splat => try sema.zirSplat(block, inst), |
| 1288 | .reduce => try sema.zirReduce(block, inst), |
| 1289 | .shuffle => try sema.zirShuffle(block, inst), |
| 1290 | .atomic_load => try sema.zirAtomicLoad(block, inst), |
| 1291 | .atomic_rmw => try sema.zirAtomicRmw(block, inst), |
| 1292 | .mul_add => try sema.zirMulAdd(block, inst), |
| 1293 | .builtin_call => try sema.zirBuiltinCall(block, inst), |
| 1294 | .@"resume" => try sema.zirResume(block, inst), |
| 1295 | .for_len => try sema.zirForLen(block, inst), |
| 1296 | .validate_array_init_ref_ty => try sema.zirValidateArrayInitRefTy(block, inst), |
| 1297 | .opt_eu_base_ptr_init => try sema.zirOptEuBasePtrInit(block, inst), |
| 1298 | .coerce_ptr_elem_ty => try sema.zirCoercePtrElemTy(block, inst), |
| 1299 | |
| 1300 | .clz => try sema.zirBitCount(block, inst, .clz, Value.clz), |
| 1301 | .ctz => try sema.zirBitCount(block, inst, .ctz, Value.ctz), |
| 1302 | .pop_count => try sema.zirBitCount(block, inst, .popcount, Value.popCount), |
| 1303 | .abs => try sema.zirAbs(block, inst), |
| 1304 | |
| 1305 | .sqrt => try sema.zirUnaryMath(block, inst, .sqrt, Value.sqrt), |
| 1306 | .sin => try sema.zirUnaryMath(block, inst, .sin, Value.sin), |
| 1307 | .cos => try sema.zirUnaryMath(block, inst, .cos, Value.cos), |
| 1308 | .tan => try sema.zirUnaryMath(block, inst, .tan, Value.tan), |
| 1309 | .exp => try sema.zirUnaryMath(block, inst, .exp, Value.exp), |
| 1310 | .exp2 => try sema.zirUnaryMath(block, inst, .exp2, Value.exp2), |
| 1311 | .log => try sema.zirUnaryMath(block, inst, .log, Value.log), |
| 1312 | .log2 => try sema.zirUnaryMath(block, inst, .log2, Value.log2), |
| 1313 | .log10 => try sema.zirUnaryMath(block, inst, .log10, Value.log10), |
| 1314 | .floor => try sema.zirUnaryMath(block, inst, .floor, Value.floor), |
| 1315 | .ceil => try sema.zirUnaryMath(block, inst, .ceil, Value.ceil), |
| 1316 | .round => try sema.zirUnaryMath(block, inst, .round, Value.round), |
| 1317 | .trunc => try sema.zirUnaryMath(block, inst, .trunc_float, Value.trunc), |
| 1318 | |
| 1319 | .error_set_decl => try sema.zirErrorSetDecl(inst), |
| 1320 | |
| 1321 | .add => try sema.zirArithmetic(block, inst, .add, true), |
| 1322 | .addwrap => try sema.zirArithmetic(block, inst, .addwrap, true), |
| 1323 | .add_sat => try sema.zirArithmetic(block, inst, .add_sat, true), |
| 1324 | .add_unsafe => try sema.zirArithmetic(block, inst, .add_unsafe, false), |
| 1325 | .mul => try sema.zirArithmetic(block, inst, .mul, true), |
| 1326 | .mulwrap => try sema.zirArithmetic(block, inst, .mulwrap, true), |
| 1327 | .mul_sat => try sema.zirArithmetic(block, inst, .mul_sat, true), |
| 1328 | .sub => try sema.zirArithmetic(block, inst, .sub, true), |
| 1329 | .subwrap => try sema.zirArithmetic(block, inst, .subwrap, true), |
| 1330 | .sub_sat => try sema.zirArithmetic(block, inst, .sub_sat, true), |
| 1331 | |
| 1332 | .div => try sema.zirDiv(block, inst), |
| 1333 | .div_exact => try sema.zirDivExact(block, inst), |
| 1334 | .div_floor => try sema.zirDivFloor(block, inst), |
| 1335 | .div_ceil => try sema.zirDivCeil(block, inst), |
| 1336 | .div_trunc => try sema.zirDivTrunc(block, inst), |
| 1337 | |
| 1338 | .mod_rem => try sema.zirModRem(block, inst), |
| 1339 | .mod => try sema.zirMod(block, inst), |
| 1340 | .rem => try sema.zirRem(block, inst), |
| 1341 | |
| 1342 | .max => try sema.zirMinMax(block, inst, .max), |
| 1343 | .min => try sema.zirMinMax(block, inst, .min), |
| 1344 | |
| 1345 | .shl => try sema.zirShl(block, inst, .shl), |
| 1346 | .shl_exact => try sema.zirShl(block, inst, .shl_exact), |
| 1347 | .shl_sat => try sema.zirShl(block, inst, .shl_sat), |
| 1348 | |
| 1349 | .ret_ptr => try sema.zirRetPtr(block, inst), |
| 1350 | .ret_type => Air.internedToRef(sema.fn_ret_ty.toIntern()), |
| 1351 | |
| 1352 | // Instructions that we know to *always* be noreturn based solely on their tag. |
| 1353 | // These functions match the return type of analyzeBody so that we can |
| 1354 | // tail call them here. |
| 1355 | .compile_error => break try sema.zirCompileError(block, inst), |
| 1356 | .ret_implicit => break try sema.zirRetImplicit(block, inst), |
| 1357 | .ret_node => break try sema.zirRetNode(block, inst), |
| 1358 | .ret_load => break try sema.zirRetLoad(block, inst), |
| 1359 | .ret_err_value => break try sema.zirRetErrValue(block, inst), |
| 1360 | .@"unreachable" => break try sema.zirUnreachable(block, inst), |
| 1361 | .panic => break try sema.zirPanic(block, inst), |
| 1362 | .trap => break try sema.zirTrap(block, inst), |
| 1363 | // zig fmt: on |
| 1364 | |
| 1365 | // This instruction never exists in an analyzed body. It exists only in the declaration |
| 1366 | // list for a container type. |
| 1367 | .declaration => unreachable, |
| 1368 | |
| 1369 | .extended => ext: { |
| 1370 | const extended = datas[@backingInt(inst)].extended; |
| 1371 | break :ext switch (extended.opcode) { |
| 1372 | // zig fmt: off |
| 1373 | .struct_decl => try sema.zirStructDecl( block, inst), |
| 1374 | .enum_decl => try sema.zirEnumDecl( block, inst), |
| 1375 | .union_decl => try sema.zirUnionDecl( block, inst), |
| 1376 | .opaque_decl => try sema.zirOpaqueDecl( block, inst), |
| 1377 | .tuple_decl => try sema.zirTupleDecl( block, extended), |
| 1378 | .this => try sema.zirThis( block, extended), |
| 1379 | .ret_addr => try sema.zirRetAddr( block, extended), |
| 1380 | .builtin_src => try sema.zirBuiltinSrc( block, extended), |
| 1381 | .error_return_trace => try sema.zirErrorReturnTrace( block), |
| 1382 | .frame => try sema.zirFrame( block, extended), |
| 1383 | .frame_address => try sema.zirFrameAddress( block, extended), |
| 1384 | .alloc => try sema.zirAllocExtended( block, extended), |
| 1385 | .builtin_extern => try sema.zirBuiltinExtern( block, extended), |
| 1386 | .@"asm" => try sema.zirAsm( block, extended, false), |
| 1387 | .asm_expr => try sema.zirAsm( block, extended, true), |
| 1388 | .typeof_peer => try sema.zirTypeofPeer( block, extended, inst), |
| 1389 | .round_op => try sema.zirRoundCast( block, extended), |
| 1390 | .round_op_ty => try sema.zirRoundOpType( block, extended), |
| 1391 | .compile_log => try sema.zirCompileLog( block, extended), |
| 1392 | .min_multi => try sema.zirMinMaxMulti( block, extended, .min), |
| 1393 | .max_multi => try sema.zirMinMaxMulti( block, extended, .max), |
| 1394 | .add_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), |
| 1395 | .sub_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), |
| 1396 | .mul_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), |
| 1397 | .shl_with_overflow => try sema.zirOverflowArithmetic(block, extended, extended.opcode), |
| 1398 | .wasm_memory_size => try sema.zirWasmMemorySize( block, extended), |
| 1399 | .wasm_memory_grow => try sema.zirWasmMemoryGrow( block, extended), |
| 1400 | .prefetch => try sema.zirPrefetch( block, extended), |
| 1401 | .error_cast => try sema.zirErrorCast( block, extended), |
| 1402 | .select => try sema.zirSelect( block, extended), |
| 1403 | .int_from_error => try sema.zirIntFromError( block, extended), |
| 1404 | .error_from_int => try sema.zirErrorFromInt( block, extended), |
| 1405 | .cmpxchg => try sema.zirCmpxchg( block, extended), |
| 1406 | .c_va_arg => try sema.zirCVaArg( block, extended), |
| 1407 | .c_va_copy => try sema.zirCVaCopy( block, extended), |
| 1408 | .c_va_end => try sema.zirCVaEnd( block, extended), |
| 1409 | .c_va_start => try sema.zirCVaStart( block, extended), |
| 1410 | .ptr_cast_full => try sema.zirPtrCastFull( block, extended), |
| 1411 | .ptr_cast_no_dest => try sema.zirPtrCastNoDest( block, extended), |
| 1412 | .work_item_id => try sema.zirWorkItem( block, extended, extended.opcode), |
| 1413 | .work_group_size => try sema.zirWorkItem( block, extended, extended.opcode), |
| 1414 | .work_group_id => try sema.zirWorkItem( block, extended, extended.opcode), |
| 1415 | .in_comptime => try sema.zirInComptime( block), |
| 1416 | .closure_get => try sema.zirClosureGet( block, extended), |
| 1417 | |
| 1418 | .reify_slice_arg_ty => try sema.zirReifySliceArgTy( block, extended), |
| 1419 | .reify_enum_value_slice_ty => try sema.zirReifyEnumValueSliceTy(block, extended), |
| 1420 | .reify_pointer_sentinel_ty => try sema.zirReifyPointerSentinelTy(block, extended), |
| 1421 | .reify_tuple => try sema.zirReifyTuple( block, extended), |
| 1422 | .reify_pointer => try sema.zirReifyPointer( block, extended), |
| 1423 | .reify_fn => try sema.zirReifyFn( block, extended), |
| 1424 | .reify_struct => try sema.zirReifyStruct( block, extended, inst), |
| 1425 | .reify_union => try sema.zirReifyUnion( block, extended, inst), |
| 1426 | .reify_enum => try sema.zirReifyEnum( block, extended, inst), |
| 1427 | .reify_spirv_type => try sema.zirReifySpirvType( block, extended, inst), |
| 1428 | // zig fmt: on |
| 1429 | |
| 1430 | .set_float_mode => { |
| 1431 | try sema.zirSetFloatMode(block, extended); |
| 1432 | i += 1; |
| 1433 | continue; |
| 1434 | }, |
| 1435 | .breakpoint => { |
| 1436 | try sema.zirBreakpoint(block, extended); |
| 1437 | i += 1; |
| 1438 | continue; |
| 1439 | }, |
| 1440 | .disable_instrumentation => { |
| 1441 | try sema.zirDisableInstrumentation(); |
| 1442 | i += 1; |
| 1443 | continue; |
| 1444 | }, |
| 1445 | .disable_intrinsics => { |
| 1446 | try sema.zirDisableIntrinsics(); |
| 1447 | i += 1; |
| 1448 | continue; |
| 1449 | }, |
| 1450 | .restore_err_ret_index => { |
| 1451 | try sema.zirRestoreErrRetIndex(block, extended); |
| 1452 | i += 1; |
| 1453 | continue; |
| 1454 | }, |
| 1455 | .branch_hint => { |
| 1456 | try sema.zirBranchHint(block, extended); |
| 1457 | i += 1; |
| 1458 | continue; |
| 1459 | }, |
| 1460 | .value_placeholder => unreachable, // never appears in a body |
| 1461 | .field_parent_ptr => try sema.zirFieldParentPtr(block, extended), |
| 1462 | .std_lang_value => try sema.zirStdLangValue(block, extended), |
| 1463 | .inplace_arith_result_ty => try sema.zirInplaceArithResultTy(extended), |
| 1464 | .dbg_empty_stmt => { |
| 1465 | try sema.zirDbgEmptyStmt(block, inst); |
| 1466 | i += 1; |
| 1467 | continue; |
| 1468 | }, |
| 1469 | .astgen_error => return sema.failTransitive(.astgen_error), |
| 1470 | .float_op_result_ty => try sema.zirFloatOpResultType(block, extended), |
| 1471 | }; |
| 1472 | }, |
| 1473 | |
| 1474 | // Instructions that we know can *never* be noreturn based solely on |
| 1475 | // their tag. We avoid needlessly checking if they are noreturn and |
| 1476 | // continue the loop. |
| 1477 | // We also know that they cannot be referenced later, so we avoid |
| 1478 | // putting them into the map. |
| 1479 | .dbg_stmt => { |
| 1480 | try sema.zirDbgStmt(block, inst); |
| 1481 | i += 1; |
| 1482 | continue; |
| 1483 | }, |
| 1484 | .dbg_var_ptr => { |
| 1485 | try sema.zirDbgVar(block, inst, .dbg_var_ptr); |
| 1486 | i += 1; |
| 1487 | continue; |
| 1488 | }, |
| 1489 | .dbg_var_val => { |
| 1490 | try sema.zirDbgVar(block, inst, .dbg_var_val); |
| 1491 | i += 1; |
| 1492 | continue; |
| 1493 | }, |
| 1494 | .ensure_err_union_payload_void => { |
| 1495 | try sema.zirEnsureErrUnionPayloadVoid(block, inst); |
| 1496 | i += 1; |
| 1497 | continue; |
| 1498 | }, |
| 1499 | .ensure_result_non_error => { |
| 1500 | try sema.zirEnsureResultNonError(block, inst); |
| 1501 | i += 1; |
| 1502 | continue; |
| 1503 | }, |
| 1504 | .ensure_result_used => { |
| 1505 | try sema.zirEnsureResultUsed(block, inst); |
| 1506 | i += 1; |
| 1507 | continue; |
| 1508 | }, |
| 1509 | .set_eval_branch_quota => { |
| 1510 | try sema.zirSetEvalBranchQuota(block, inst); |
| 1511 | i += 1; |
| 1512 | continue; |
| 1513 | }, |
| 1514 | .atomic_store => { |
| 1515 | try sema.zirAtomicStore(block, inst); |
| 1516 | i += 1; |
| 1517 | continue; |
| 1518 | }, |
| 1519 | .store_node => { |
| 1520 | try sema.zirStoreNode(block, inst); |
| 1521 | i += 1; |
| 1522 | continue; |
| 1523 | }, |
| 1524 | .store_to_inferred_ptr => { |
| 1525 | try sema.zirStoreToInferredPtr(block, inst); |
| 1526 | i += 1; |
| 1527 | continue; |
| 1528 | }, |
| 1529 | .validate_struct_init_ty => { |
| 1530 | try sema.zirValidateStructInitTy(block, inst, false); |
| 1531 | i += 1; |
| 1532 | continue; |
| 1533 | }, |
| 1534 | .validate_struct_init_result_ty => { |
| 1535 | try sema.zirValidateStructInitTy(block, inst, true); |
| 1536 | i += 1; |
| 1537 | continue; |
| 1538 | }, |
| 1539 | .validate_array_init_ty => { |
| 1540 | try sema.zirValidateArrayInitTy(block, inst, false); |
| 1541 | i += 1; |
| 1542 | continue; |
| 1543 | }, |
| 1544 | .validate_array_init_result_ty => { |
| 1545 | try sema.zirValidateArrayInitTy(block, inst, true); |
| 1546 | i += 1; |
| 1547 | continue; |
| 1548 | }, |
| 1549 | .validate_ptr_struct_init => { |
| 1550 | try sema.zirValidatePtrStructInit(block, inst); |
| 1551 | i += 1; |
| 1552 | continue; |
| 1553 | }, |
| 1554 | .validate_ptr_array_init => { |
| 1555 | try sema.zirValidatePtrArrayInit(block, inst); |
| 1556 | i += 1; |
| 1557 | continue; |
| 1558 | }, |
| 1559 | .validate_destructure => { |
| 1560 | try sema.zirValidateDestructure(block, inst); |
| 1561 | i += 1; |
| 1562 | continue; |
| 1563 | }, |
| 1564 | .validate_ref_ty => { |
| 1565 | try sema.zirValidateRefTy(block, inst); |
| 1566 | i += 1; |
| 1567 | continue; |
| 1568 | }, |
| 1569 | .validate_const => { |
| 1570 | try sema.zirValidateConst(block, inst); |
| 1571 | i += 1; |
| 1572 | continue; |
| 1573 | }, |
| 1574 | .@"export" => { |
| 1575 | try sema.zirExport(block, inst); |
| 1576 | i += 1; |
| 1577 | continue; |
| 1578 | }, |
| 1579 | .set_runtime_safety => { |
| 1580 | try sema.zirSetRuntimeSafety(block, inst); |
| 1581 | i += 1; |
| 1582 | continue; |
| 1583 | }, |
| 1584 | .param => { |
| 1585 | try sema.zirParam(block, inst, false); |
| 1586 | i += 1; |
| 1587 | continue; |
| 1588 | }, |
| 1589 | .param_comptime => { |
| 1590 | try sema.zirParam(block, inst, true); |
| 1591 | i += 1; |
| 1592 | continue; |
| 1593 | }, |
| 1594 | .param_anytype => { |
| 1595 | try sema.zirParamAnytype(block, inst, false); |
| 1596 | i += 1; |
| 1597 | continue; |
| 1598 | }, |
| 1599 | .param_anytype_comptime => { |
| 1600 | try sema.zirParamAnytype(block, inst, true); |
| 1601 | i += 1; |
| 1602 | continue; |
| 1603 | }, |
| 1604 | .memcpy => { |
| 1605 | try sema.zirMemcpy(block, inst, .memcpy, true); |
| 1606 | i += 1; |
| 1607 | continue; |
| 1608 | }, |
| 1609 | .memmove => { |
| 1610 | try sema.zirMemcpy(block, inst, .memmove, false); |
| 1611 | i += 1; |
| 1612 | continue; |
| 1613 | }, |
| 1614 | .memset => { |
| 1615 | try sema.zirMemset(block, inst); |
| 1616 | i += 1; |
| 1617 | continue; |
| 1618 | }, |
| 1619 | .check_comptime_control_flow => { |
| 1620 | if (!block.isComptime()) { |
| 1621 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 1622 | const src = block.nodeOffset(inst_data.src_node); |
| 1623 | const inline_block = inst_data.operand.toIndex().?; |
| 1624 | |
| 1625 | var check_block = block; |
| 1626 | const target_runtime_index = while (true) { |
| 1627 | if (check_block.inline_block == inline_block.toOptional()) { |
| 1628 | break check_block.runtime_index; |
| 1629 | } |
| 1630 | check_block = check_block.parent.?; |
| 1631 | }; |
| 1632 | |
| 1633 | if (@backingInt(target_runtime_index) < @backingInt(block.runtime_index)) { |
| 1634 | const runtime_src = block.runtime_cond orelse block.runtime_loop.?; |
| 1635 | const msg = msg: { |
| 1636 | const msg = try sema.errMsg(src, "comptime control flow inside runtime block", .{}); |
| 1637 | errdefer msg.destroy(sema.gpa); |
| 1638 | try sema.errNote(runtime_src, msg, "runtime control flow here", .{}); |
| 1639 | break :msg msg; |
| 1640 | }; |
| 1641 | return sema.failWithOwnedErrorMsg(block, msg); |
| 1642 | } |
| 1643 | } |
| 1644 | i += 1; |
| 1645 | continue; |
| 1646 | }, |
| 1647 | .save_err_ret_index => { |
| 1648 | try sema.zirSaveErrRetIndex(block, inst); |
| 1649 | i += 1; |
| 1650 | continue; |
| 1651 | }, |
| 1652 | .restore_err_ret_index_unconditional => { |
| 1653 | const un_node = datas[@backingInt(inst)].un_node; |
| 1654 | try sema.restoreErrRetIndex(block, block.nodeOffset(un_node.src_node), un_node.operand, .none); |
| 1655 | i += 1; |
| 1656 | continue; |
| 1657 | }, |
| 1658 | .restore_err_ret_index_fn_entry => { |
| 1659 | const un_node = datas[@backingInt(inst)].un_node; |
| 1660 | try sema.restoreErrRetIndex(block, block.nodeOffset(un_node.src_node), .none, un_node.operand); |
| 1661 | i += 1; |
| 1662 | continue; |
| 1663 | }, |
| 1664 | |
| 1665 | // Special case instructions to handle comptime control flow. |
| 1666 | .@"break" => { |
| 1667 | if (block.isComptime()) { |
| 1668 | sema.comptime_break_inst = inst; |
| 1669 | return error.ComptimeBreak; |
| 1670 | } else { |
| 1671 | try sema.zirBreak(block, inst); |
| 1672 | break; |
| 1673 | } |
| 1674 | }, |
| 1675 | .break_inline => { |
| 1676 | sema.comptime_break_inst = inst; |
| 1677 | return error.ComptimeBreak; |
| 1678 | }, |
| 1679 | .repeat => { |
| 1680 | if (block.isComptime()) { |
| 1681 | // Send comptime control flow back to the beginning of this block. |
| 1682 | const src = block.nodeOffset(datas[@backingInt(inst)].node); |
| 1683 | try sema.emitBackwardBranch(block, src); |
| 1684 | i = 0; |
| 1685 | continue; |
| 1686 | } else { |
| 1687 | // We are definitely called by `zirLoop`, which will treat the |
| 1688 | // fact that this body does not terminate `noreturn` as an |
| 1689 | // implicit repeat. |
| 1690 | // TODO: since AIR has `repeat` now, we could change ZIR to generate |
| 1691 | // more optimal code utilizing `repeat` instructions across blocks! |
| 1692 | break; |
| 1693 | } |
| 1694 | }, |
| 1695 | .repeat_inline => { |
| 1696 | // Send comptime control flow back to the beginning of this block. |
| 1697 | const src = block.nodeOffset(datas[@backingInt(inst)].node); |
| 1698 | try sema.emitBackwardBranch(block, src); |
| 1699 | i = 0; |
| 1700 | continue; |
| 1701 | }, |
| 1702 | .switch_continue => if (block.isComptime()) { |
| 1703 | sema.comptime_break_inst = inst; |
| 1704 | return error.ComptimeBreak; |
| 1705 | } else { |
| 1706 | try sema.zirSwitchContinue(block, inst); |
| 1707 | break; |
| 1708 | }, |
| 1709 | |
| 1710 | .loop => if (block.isComptime()) { |
| 1711 | continue :inst .block_inline; |
| 1712 | } else try sema.zirLoop(block, inst), |
| 1713 | |
| 1714 | .block => if (block.isComptime()) { |
| 1715 | continue :inst .block_inline; |
| 1716 | } else try sema.zirBlock(block, inst), |
| 1717 | |
| 1718 | .block_comptime => { |
| 1719 | const pl_node = datas[@backingInt(inst)].pl_node; |
| 1720 | const src = block.nodeOffset(pl_node.src_node); |
| 1721 | const extra = sema.code.extraData(Zir.Inst.BlockComptime, pl_node.payload_index); |
| 1722 | const block_body = sema.code.bodySlice(extra.end, extra.data.body_len); |
| 1723 | |
| 1724 | var child_block = block.makeSubBlock(); |
| 1725 | defer child_block.instructions.deinit(sema.gpa); |
| 1726 | |
| 1727 | // We won't have any merges, but we must ensure this block is properly labeled for |
| 1728 | // any `.restore_err_ret_index_*` instructions. |
| 1729 | var label: Block.Label = .{ |
| 1730 | .zir_block = inst, |
| 1731 | .merges = undefined, |
| 1732 | }; |
| 1733 | child_block.label = &label; |
| 1734 | |
| 1735 | child_block.comptime_reason = .{ .reason = .{ |
| 1736 | .src = src, |
| 1737 | .r = .{ .simple = extra.data.reason }, |
| 1738 | } }; |
| 1739 | |
| 1740 | const result = try sema.resolveInlineBody(&child_block, block_body, inst); |
| 1741 | |
| 1742 | // Only check for the result being comptime-known in the outermost `block_comptime`. |
| 1743 | // That way, AstGen can safely elide redundant `block_comptime` without affecting semantics. |
| 1744 | if (!block.isComptime() and !try sema.isComptimeKnown(result)) { |
| 1745 | return sema.failWithNeededComptime(&child_block, src, null); |
| 1746 | } |
| 1747 | |
| 1748 | break :inst result; |
| 1749 | }, |
| 1750 | |
| 1751 | .block_inline => blk: { |
| 1752 | // Directly analyze the block body without introducing a new block. |
| 1753 | // However, in the case of a corresponding break_inline which reaches |
| 1754 | // through a runtime conditional branch, we must retroactively emit |
| 1755 | // a block, so we remember the block index here just in case. |
| 1756 | const block_index = block.instructions.items.len; |
| 1757 | const inst_data = datas[@backingInt(inst)].pl_node; |
| 1758 | const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); |
| 1759 | const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len); |
| 1760 | const gpa = sema.gpa; |
| 1761 | |
| 1762 | const BreakResult = struct { |
| 1763 | block_inst: Zir.Inst.Index, |
| 1764 | operand: Zir.Inst.Ref, |
| 1765 | }; |
| 1766 | |
| 1767 | const opt_break_data: ?BreakResult, const need_debug_scope = b: { |
| 1768 | // Create a temporary child block so that this inline block is properly |
| 1769 | // labeled for any .restore_err_ret_index instructions |
| 1770 | var child_block = block.makeSubBlock(); |
| 1771 | var need_debug_scope = false; |
| 1772 | child_block.need_debug_scope = &need_debug_scope; |
| 1773 | |
| 1774 | // If this block contains a function prototype, we need to reset the |
| 1775 | // current list of parameters and restore it later. |
| 1776 | // Note: this probably needs to be resolved in a more general manner. |
| 1777 | const tag_index = @backingInt(inline_body[inline_body.len - 1]); |
| 1778 | child_block.inline_block = (if (tags[tag_index] == .repeat_inline) |
| 1779 | inline_body[0] |
| 1780 | else |
| 1781 | inst).toOptional(); |
| 1782 | |
| 1783 | var label: Block.Label = .{ |
| 1784 | .zir_block = inst, |
| 1785 | .merges = undefined, |
| 1786 | }; |
| 1787 | child_block.label = &label; |
| 1788 | |
| 1789 | // Write these instructions directly into the parent block |
| 1790 | child_block.instructions = block.instructions; |
| 1791 | defer block.instructions = child_block.instructions; |
| 1792 | |
| 1793 | const break_result: ?BreakResult = if (sema.analyzeBodyInner(&child_block, inline_body)) r: { |
| 1794 | break :r null; |
| 1795 | } else |err| switch (err) { |
| 1796 | error.ComptimeBreak => brk_res: { |
| 1797 | const break_inst = sema.comptime_break_inst; |
| 1798 | const break_data = sema.code.instructions.items(.data)[@backingInt(break_inst)].@"break"; |
| 1799 | const break_extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; |
| 1800 | break :brk_res .{ |
| 1801 | .block_inst = break_extra.block_inst, |
| 1802 | .operand = break_data.operand, |
| 1803 | }; |
| 1804 | }, |
| 1805 | else => |e| return e, |
| 1806 | }; |
| 1807 | |
| 1808 | if (need_debug_scope) { |
| 1809 | _ = try sema.ensurePostHoc(block, inst); |
| 1810 | } |
| 1811 | |
| 1812 | break :b .{ break_result, need_debug_scope }; |
| 1813 | }; |
| 1814 | |
| 1815 | // A runtime conditional branch that needs a post-hoc block to be |
| 1816 | // emitted communicates this by mapping the block index into the inst map. |
| 1817 | if (map.get(inst)) |new_block_ref| ph: { |
| 1818 | // Comptime control flow populates the map, so we don't actually know |
| 1819 | // if this is a post-hoc runtime block until we check the |
| 1820 | // post_hoc_block map. |
| 1821 | const new_block_inst = new_block_ref.toIndex() orelse break :ph; |
| 1822 | const labeled_block = sema.post_hoc_blocks.get(new_block_inst) orelse |
| 1823 | break :ph; |
| 1824 | |
| 1825 | // In this case we need to move all the instructions starting at |
| 1826 | // block_index from the current block into this new one. |
| 1827 | |
| 1828 | if (opt_break_data) |break_data| { |
| 1829 | // This is a comptime break which we now change to a runtime break |
| 1830 | // since it crosses a runtime branch. |
| 1831 | // It may pass through our currently being analyzed block_inline or it |
| 1832 | // may point directly to it. In the latter case, this modifies the |
| 1833 | // block that we looked up in the post_hoc_blocks map above. |
| 1834 | try sema.addRuntimeBreak(block, break_data.block_inst, break_data.operand); |
| 1835 | } |
| 1836 | |
| 1837 | try labeled_block.block.instructions.appendSlice(gpa, block.instructions.items[block_index..]); |
| 1838 | block.instructions.items.len = block_index; |
| 1839 | |
| 1840 | const block_result = try sema.resolveAnalyzedBlock(block, block.nodeOffset(inst_data.src_node), &labeled_block.block, &labeled_block.label.merges, need_debug_scope); |
| 1841 | { |
| 1842 | // Destroy the ad-hoc block entry so that it does not interfere with |
| 1843 | // the next iteration of comptime control flow, if any. |
| 1844 | labeled_block.destroy(gpa); |
| 1845 | assert(sema.post_hoc_blocks.remove(new_block_inst)); |
| 1846 | } |
| 1847 | |
| 1848 | break :blk block_result; |
| 1849 | } |
| 1850 | |
| 1851 | const break_data = opt_break_data orelse break; |
| 1852 | if (inst == break_data.block_inst) { |
| 1853 | break :blk sema.resolveInst(break_data.operand); |
| 1854 | } else { |
| 1855 | // `comptime_break_inst` preserved from `analyzeBodyInner` above. |
| 1856 | return error.ComptimeBreak; |
| 1857 | } |
| 1858 | }, |
| 1859 | .condbr => if (block.isComptime()) { |
| 1860 | continue :inst .condbr_inline; |
| 1861 | } else { |
| 1862 | try sema.zirCondbr(block, inst); |
| 1863 | break; |
| 1864 | }, |
| 1865 | .condbr_inline => { |
| 1866 | const inst_data = datas[@backingInt(inst)].pl_node; |
| 1867 | const cond_src = block.src(.{ .node_offset_if_cond = inst_data.src_node }); |
| 1868 | const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); |
| 1869 | const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len); |
| 1870 | const else_body = sema.code.bodySlice( |
| 1871 | extra.end + then_body.len, |
| 1872 | extra.data.else_body_len, |
| 1873 | ); |
| 1874 | const uncasted_cond = sema.resolveInst(extra.data.condition); |
| 1875 | const cond = try sema.coerce(block, .bool, uncasted_cond, cond_src); |
| 1876 | const cond_val = try sema.resolveConstDefinedValue( |
| 1877 | block, |
| 1878 | cond_src, |
| 1879 | cond, |
| 1880 | // If this block is comptime, it's more helpful to just give the outer message. |
| 1881 | // This is particularly true if this came from a comptime `condbr` above. |
| 1882 | if (block.isComptime()) null else .{ .simple = .inline_loop_operand }, |
| 1883 | ); |
| 1884 | const inline_body = if (cond_val.toBool()) then_body else else_body; |
| 1885 | |
| 1886 | try sema.maybeErrorUnwrapCondbr(block, inline_body, extra.data.condition, cond_src); |
| 1887 | const old_runtime_index = block.runtime_index; |
| 1888 | defer block.runtime_index = old_runtime_index; |
| 1889 | |
| 1890 | const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break; |
| 1891 | break :inst result; |
| 1892 | }, |
| 1893 | .@"try" => blk: { |
| 1894 | if (!block.isComptime()) break :blk try sema.zirTry(block, inst); |
| 1895 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 1896 | const src = block.nodeOffset(inst_data.src_node); |
| 1897 | const operand_src = block.src(.{ .node_offset_try_operand = inst_data.src_node }); |
| 1898 | const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); |
| 1899 | const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len); |
| 1900 | const err_union = sema.resolveInst(extra.data.operand); |
| 1901 | const err_union_ty = sema.typeOf(err_union); |
| 1902 | if (err_union_ty.zigTypeTag(zcu) != .error_union) { |
| 1903 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 1904 | const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)}); |
| 1905 | errdefer msg.destroy(sema.gpa); |
| 1906 | try sema.addDeclaredHereNote(msg, err_union_ty); |
| 1907 | try sema.errNote(operand_src, msg, "consider omitting 'try'", .{}); |
| 1908 | break :msg msg; |
| 1909 | }); |
| 1910 | } |
| 1911 | const is_non_err_val = (try sema.resolveIsNonErrVal(block, operand_src, err_union)).?; |
| 1912 | if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, operand_src, null); |
| 1913 | if (is_non_err_val.toBool()) { |
| 1914 | break :blk try sema.analyzeErrUnionPayload(block, src, err_union_ty, err_union, operand_src, false); |
| 1915 | } |
| 1916 | const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break; |
| 1917 | break :blk result; |
| 1918 | }, |
| 1919 | .try_ptr => blk: { |
| 1920 | if (!block.isComptime()) break :blk try sema.zirTryPtr(block, inst); |
| 1921 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 1922 | const src = block.nodeOffset(inst_data.src_node); |
| 1923 | const operand_src = block.src(.{ .node_offset_try_operand = inst_data.src_node }); |
| 1924 | const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); |
| 1925 | const inline_body = sema.code.bodySlice(extra.end, extra.data.body_len); |
| 1926 | const operand = sema.resolveInst(extra.data.operand); |
| 1927 | const err_union = try sema.analyzeLoad(block, src, operand, operand_src); |
| 1928 | const is_non_err_val = (try sema.resolveIsNonErrVal(block, operand_src, err_union)).?; |
| 1929 | if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, operand_src, null); |
| 1930 | if (is_non_err_val.toBool()) { |
| 1931 | break :blk try sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); |
| 1932 | } |
| 1933 | const result = try sema.analyzeInlineBody(block, inline_body, inst) orelse break; |
| 1934 | break :blk result; |
| 1935 | }, |
| 1936 | .@"defer" => blk: { |
| 1937 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].@"defer"; |
| 1938 | const defer_body = sema.code.bodySlice(inst_data.index, inst_data.len); |
| 1939 | if (sema.analyzeBodyInner(block, defer_body)) { |
| 1940 | // The defer terminated noreturn - no more analysis needed. |
| 1941 | break; |
| 1942 | } else |err| switch (err) { |
| 1943 | error.ComptimeBreak => {}, |
| 1944 | else => |e| return e, |
| 1945 | } |
| 1946 | if (sema.comptime_break_inst != defer_body[defer_body.len - 1]) { |
| 1947 | return error.ComptimeBreak; |
| 1948 | } |
| 1949 | break :blk .void_value; |
| 1950 | }, |
| 1951 | }; |
| 1952 | |
| 1953 | const is_inferred_alloc = if (air_ref.toIndex()) |air_inst| switch (sema.air_instructions.items(.tag)[@backingInt(air_inst)]) { |
| 1954 | .inferred_alloc, .inferred_alloc_comptime => true, |
| 1955 | else => false, |
| 1956 | } else false; |
| 1957 | // We must resolve the layout of a type before creating a value of that type. Therefore, |
| 1958 | // the layout of the type of `air_ref` must already be resolved. The call to `classify` |
| 1959 | // doubles as an assertion of this. |
| 1960 | if (!is_inferred_alloc) switch (sema.typeOf(air_ref).classify(zcu)) { |
| 1961 | .no_possible_value => { |
| 1962 | // The instruction result was noreturn, which should mean that the body itself now |
| 1963 | // ends with a noreturn instruction. Let's confirm that. |
| 1964 | const last_inst = block.instructions.items[block.instructions.items.len - 1]; |
| 1965 | const last_inst_ty = sema.typeOf(last_inst.toRef()); |
| 1966 | assert(last_inst_ty.classify(zcu) == .no_possible_value); |
| 1967 | break; |
| 1968 | }, |
| 1969 | .one_possible_value => assert(air_ref.toInterned() != null), // the value should be comptime-known |
| 1970 | .partially_comptime => assert(air_ref.toInterned() != null), // the value should be comptime-known |
| 1971 | .fully_comptime => assert(air_ref.toInterned() != null), // the value should be comptime-known |
| 1972 | .runtime => {}, |
| 1973 | }; |
| 1974 | |
| 1975 | map.putAssumeCapacity(inst, air_ref); |
| 1976 | i += 1; |
| 1977 | } |
| 1978 | } |
| 1979 | |
| 1980 | fn resolveInstAllowNone(sema: *Sema, zir_ref: Zir.Inst.Ref) Air.Inst.Ref { |
| 1981 | if (zir_ref == .none) { |
| 1982 | return .none; |
| 1983 | } else { |
| 1984 | return resolveInst(sema, zir_ref); |
| 1985 | } |
| 1986 | } |
| 1987 | |
| 1988 | fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) Air.Inst.Ref { |
| 1989 | assert(zir_ref != .none); |
| 1990 | if (zir_ref.toIndex()) |i| { |
| 1991 | return sema.inst_map.get(i).?; |
| 1992 | } |
| 1993 | // First section of indexes correspond to a set number of constant values. |
| 1994 | // We intentionally map the same indexes to the same values between ZIR and AIR. |
| 1995 | return @fromBackingInt(@intCast(@backingInt(zir_ref))); |
| 1996 | } |
| 1997 | |
| 1998 | fn resolveConstBool( |
| 1999 | sema: *Sema, |
| 2000 | block: *Block, |
| 2001 | src: LazySrcLoc, |
| 2002 | zir_ref: Zir.Inst.Ref, |
| 2003 | reason: ComptimeReason, |
| 2004 | ) !bool { |
| 2005 | const air_inst = sema.resolveInst(zir_ref); |
| 2006 | const wanted_type: Type = .bool; |
| 2007 | const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); |
| 2008 | const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason); |
| 2009 | return val.toBool(); |
| 2010 | } |
| 2011 | |
| 2012 | fn resolveConstString( |
| 2013 | sema: *Sema, |
| 2014 | block: *Block, |
| 2015 | src: LazySrcLoc, |
| 2016 | zir_ref: Zir.Inst.Ref, |
| 2017 | /// `null` may be passed only if `block.isComptime()`. It indicates that the reason for the value |
| 2018 | /// being comptime-resolved is that the block is being comptime-evaluated. |
| 2019 | reason: ?ComptimeReason, |
| 2020 | ) ![]u8 { |
| 2021 | const air_inst = sema.resolveInst(zir_ref); |
| 2022 | return sema.toConstString(block, src, air_inst, reason); |
| 2023 | } |
| 2024 | |
| 2025 | pub fn toConstString( |
| 2026 | sema: *Sema, |
| 2027 | block: *Block, |
| 2028 | src: LazySrcLoc, |
| 2029 | air_inst: Air.Inst.Ref, |
| 2030 | /// `null` may be passed only if `block.isComptime()`. It indicates that the reason for the value |
| 2031 | /// being comptime-resolved is that the block is being comptime-evaluated. |
| 2032 | reason: ?ComptimeReason, |
| 2033 | ) ![]u8 { |
| 2034 | const pt = sema.pt; |
| 2035 | const coerced_inst = try sema.coerce(block, .slice_const_u8, air_inst, src); |
| 2036 | const slice_val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason); |
| 2037 | const arr_val = try sema.derefSliceAsArray(block, src, slice_val, reason); |
| 2038 | return arr_val.toAllocatedBytes(arr_val.typeOf(pt.zcu), sema.arena, pt); |
| 2039 | } |
| 2040 | |
| 2041 | pub fn resolveConstStringIntern( |
| 2042 | sema: *Sema, |
| 2043 | block: *Block, |
| 2044 | src: LazySrcLoc, |
| 2045 | zir_ref: Zir.Inst.Ref, |
| 2046 | reason: ComptimeReason, |
| 2047 | ) !InternPool.NullTerminatedString { |
| 2048 | const air_inst = sema.resolveInst(zir_ref); |
| 2049 | const wanted_type: Type = .slice_const_u8; |
| 2050 | const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); |
| 2051 | const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, reason); |
| 2052 | return sema.sliceToIpString(block, src, val, reason); |
| 2053 | } |
| 2054 | |
| 2055 | fn resolveTypeOrPoison(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !?Type { |
| 2056 | const air_inst = sema.resolveInst(zir_ref); |
| 2057 | const ty = try sema.analyzeAsType(block, src, .type, air_inst); |
| 2058 | if (ty.isGenericPoison()) return null; |
| 2059 | return ty; |
| 2060 | } |
| 2061 | |
| 2062 | pub fn resolveType(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) !Type { |
| 2063 | return (try sema.resolveTypeOrPoison(block, src, zir_ref)).?; |
| 2064 | } |
| 2065 | |
| 2066 | fn resolveDestType( |
| 2067 | sema: *Sema, |
| 2068 | block: *Block, |
| 2069 | src: LazySrcLoc, |
| 2070 | zir_ref: Zir.Inst.Ref, |
| 2071 | strat: enum { remove_eu_opt, remove_eu, remove_opt }, |
| 2072 | builtin_name: []const u8, |
| 2073 | ) !Type { |
| 2074 | const pt = sema.pt; |
| 2075 | const zcu = pt.zcu; |
| 2076 | const remove_eu = switch (strat) { |
| 2077 | .remove_eu_opt, .remove_eu => true, |
| 2078 | .remove_opt => false, |
| 2079 | }; |
| 2080 | const remove_opt = switch (strat) { |
| 2081 | .remove_eu_opt, .remove_opt => true, |
| 2082 | .remove_eu => false, |
| 2083 | }; |
| 2084 | |
| 2085 | const raw_ty = try sema.resolveTypeOrPoison(block, src, zir_ref) orelse { |
| 2086 | // Cast builtins use their result type as the destination type, but |
| 2087 | // it could be an anytype argument, which we can't catch in AstGen. |
| 2088 | const msg = msg: { |
| 2089 | const msg = try sema.errMsg(src, "{s} must have a known result type", .{builtin_name}); |
| 2090 | errdefer msg.destroy(sema.gpa); |
| 2091 | switch (sema.genericPoisonReason(block, zir_ref)) { |
| 2092 | .anytype_param => |call_src| try sema.errNote(call_src, msg, "result type is unknown due to anytype parameter", .{}), |
| 2093 | .anyopaque_ptr => |ptr_src| try sema.errNote(ptr_src, msg, "result type is unknown due to opaque pointer type", .{}), |
| 2094 | .unknown => {}, |
| 2095 | } |
| 2096 | try sema.errNote(src, msg, "use @as to provide explicit result type", .{}); |
| 2097 | break :msg msg; |
| 2098 | }; |
| 2099 | return sema.failWithOwnedErrorMsg(block, msg); |
| 2100 | }; |
| 2101 | |
| 2102 | if (remove_eu and raw_ty.zigTypeTag(zcu) == .error_union) { |
| 2103 | const eu_child = raw_ty.errorUnionPayload(zcu); |
| 2104 | if (remove_opt and eu_child.zigTypeTag(zcu) == .optional) { |
| 2105 | return eu_child.childType(zcu); |
| 2106 | } |
| 2107 | return eu_child; |
| 2108 | } |
| 2109 | if (remove_opt and raw_ty.zigTypeTag(zcu) == .optional) { |
| 2110 | return raw_ty.childType(zcu); |
| 2111 | } |
| 2112 | return raw_ty; |
| 2113 | } |
| 2114 | |
| 2115 | const GenericPoisonReason = union(enum) { |
| 2116 | anytype_param: LazySrcLoc, |
| 2117 | anyopaque_ptr: LazySrcLoc, |
| 2118 | unknown, |
| 2119 | }; |
| 2120 | |
| 2121 | /// Backtracks through ZIR instructions to determine the reason a generic poison |
| 2122 | /// type was created. Used for error reporting. |
| 2123 | fn genericPoisonReason(sema: *Sema, block: *Block, ref: Zir.Inst.Ref) GenericPoisonReason { |
| 2124 | var cur = ref; |
| 2125 | while (true) { |
| 2126 | const inst = cur.toIndex() orelse return .unknown; |
| 2127 | switch (sema.code.instructions.items(.tag)[@backingInt(inst)]) { |
| 2128 | .validate_array_init_ref_ty => { |
| 2129 | const pl_node = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 2130 | const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data; |
| 2131 | cur = extra.ptr_ty; |
| 2132 | }, |
| 2133 | .array_init_elem_type => { |
| 2134 | const bin = sema.code.instructions.items(.data)[@backingInt(inst)].bin; |
| 2135 | cur = bin.lhs; |
| 2136 | }, |
| 2137 | .indexable_ptr_elem_type, .splat_op_result_ty => { |
| 2138 | const un_node = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 2139 | cur = un_node.operand; |
| 2140 | }, |
| 2141 | .struct_init_field_type => { |
| 2142 | const pl_node = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 2143 | const extra = sema.code.extraData(Zir.Inst.FieldType, pl_node.payload_index).data; |
| 2144 | cur = extra.container_type; |
| 2145 | }, |
| 2146 | .elem_type => { |
| 2147 | // There are two cases here: the pointer type may already have been |
| 2148 | // generic poison, or it may have been an anyopaque pointer. |
| 2149 | const un_node = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 2150 | const operand_ref = sema.resolveInst(un_node.operand); |
| 2151 | const operand_val = operand_ref.toInterned() orelse return .unknown; |
| 2152 | if (operand_val == .generic_poison_type) { |
| 2153 | // The pointer was generic poison - keep looking. |
| 2154 | cur = un_node.operand; |
| 2155 | } else { |
| 2156 | // This must be an anyopaque pointer! |
| 2157 | return .{ .anyopaque_ptr = block.nodeOffset(un_node.src_node) }; |
| 2158 | } |
| 2159 | }, |
| 2160 | .call, .field_call => { |
| 2161 | // A function call can never return generic poison, so we must be |
| 2162 | // evaluating an `anytype` function parameter. |
| 2163 | // TODO: better source location - function decl rather than call |
| 2164 | const pl_node = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 2165 | return .{ .anytype_param = block.nodeOffset(pl_node.src_node) }; |
| 2166 | }, |
| 2167 | else => return .unknown, |
| 2168 | } |
| 2169 | } |
| 2170 | } |
| 2171 | |
| 2172 | pub fn analyzeAsType( |
| 2173 | sema: *Sema, |
| 2174 | block: *Block, |
| 2175 | src: LazySrcLoc, |
| 2176 | reason: std.zig.SimpleComptimeReason, |
| 2177 | air_inst: Air.Inst.Ref, |
| 2178 | ) !Type { |
| 2179 | const wanted_type: Type = .type; |
| 2180 | const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); |
| 2181 | const val = try sema.resolveConstDefinedValue(block, src, coerced_inst, .{ .simple = reason }); |
| 2182 | return val.toType(); |
| 2183 | } |
| 2184 | |
| 2185 | pub fn setupErrorReturnTrace(sema: *Sema, block: *Block, last_arg_index: usize) !void { |
| 2186 | const pt = sema.pt; |
| 2187 | const comp = pt.zcu.comp; |
| 2188 | const gpa = comp.gpa; |
| 2189 | const io = comp.io; |
| 2190 | const ip = &pt.zcu.intern_pool; |
| 2191 | |
| 2192 | if (!comp.config.any_error_tracing) return; |
| 2193 | |
| 2194 | assert(!block.isComptime()); |
| 2195 | var err_trace_block = block.makeSubBlock(); |
| 2196 | defer err_trace_block.instructions.deinit(gpa); |
| 2197 | |
| 2198 | const src: LazySrcLoc = LazySrcLoc.unneeded; |
| 2199 | |
| 2200 | // var addrs: [err_return_trace_addr_count]usize = undefined; |
| 2201 | const err_return_trace_addr_count = 32; |
| 2202 | const addr_arr_ty = try pt.arrayType(.{ |
| 2203 | .len = err_return_trace_addr_count, |
| 2204 | .child = .usize_type, |
| 2205 | }); |
| 2206 | const addrs_ptr = try err_trace_block.addTy(.alloc, try pt.singleMutPtrType(addr_arr_ty)); |
| 2207 | |
| 2208 | // var st: StackTrace = undefined; |
| 2209 | const stack_trace_ty = try sema.getStdLangType(block.nodeOffset(.zero), .StackTrace); |
| 2210 | const st_ptr = try err_trace_block.addTy(.alloc, try pt.singleMutPtrType(stack_trace_ty)); |
| 2211 | |
| 2212 | // st.instruction_addresses = &addrs; |
| 2213 | const instruction_addresses_field_name = try ip.getOrPutString(gpa, io, pt.tid, "instruction_addresses", .no_embedded_nulls); |
| 2214 | const addr_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, instruction_addresses_field_name, src, true); |
| 2215 | try sema.storePtr2(&err_trace_block, src, addr_field_ptr, src, addrs_ptr, src, .store); |
| 2216 | |
| 2217 | // st.index = 0; |
| 2218 | const index_field_name = try ip.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls); |
| 2219 | const index_field_ptr = try sema.fieldPtr(&err_trace_block, src, st_ptr, index_field_name, src, true); |
| 2220 | try sema.storePtr2(&err_trace_block, src, index_field_ptr, src, .zero_usize, src, .store); |
| 2221 | |
| 2222 | // @errorReturnTrace() = &st; |
| 2223 | _ = try err_trace_block.addUnOp(.set_err_return_trace, st_ptr); |
| 2224 | |
| 2225 | try block.instructions.insertSlice(gpa, last_arg_index, err_trace_block.instructions.items); |
| 2226 | } |
| 2227 | |
| 2228 | /// Return the Value corresponding to a given AIR ref, or `null` if it refers to a runtime value. |
| 2229 | fn resolveValue(sema: *Sema, inst: Air.Inst.Ref) ?Value { |
| 2230 | const zcu = sema.pt.zcu; |
| 2231 | assert(inst != .none); |
| 2232 | |
| 2233 | if (inst.toInterned()) |ip_index| { |
| 2234 | return .fromInterned(ip_index); |
| 2235 | } |
| 2236 | |
| 2237 | // Runtime-known value. We'll be returning `null`, but first, some assertions. |
| 2238 | const air_tags = sema.air_instructions.items(.tag); |
| 2239 | switch (air_tags[@backingInt(inst.toIndex().?)]) { |
| 2240 | .inferred_alloc => unreachable, // assertion failure |
| 2241 | .inferred_alloc_comptime => unreachable, // assertion failure |
| 2242 | else => {}, |
| 2243 | } |
| 2244 | // LLVM fails to eliminate this `classify` call in -Ofast, which hurts performance, so |
| 2245 | // we must explicitly check for `std.debug.runtime_safety`. |
| 2246 | if (std.debug.runtime_safety) switch (sema.typeOf(inst).classify(zcu)) { |
| 2247 | .no_possible_value => unreachable, // values of this type do not exist |
| 2248 | .one_possible_value => unreachable, // the value should be comptime-known |
| 2249 | .partially_comptime => unreachable, // the value should be comptime-known |
| 2250 | .fully_comptime => unreachable, // the value should be comptime-known |
| 2251 | .runtime => {}, |
| 2252 | }; |
| 2253 | return null; |
| 2254 | } |
| 2255 | |
| 2256 | /// Like `resolveValue`, but emits an error if the value is not comptime-known. |
| 2257 | pub fn resolveConstValue( |
| 2258 | sema: *Sema, |
| 2259 | block: *Block, |
| 2260 | src: LazySrcLoc, |
| 2261 | inst: Air.Inst.Ref, |
| 2262 | /// `null` may be passed only if `block.isComptime()`. It indicates that the reason for the value |
| 2263 | /// being comptime-resolved is that the block is being comptime-evaluated. |
| 2264 | reason: ?ComptimeReason, |
| 2265 | ) CompileError!Value { |
| 2266 | assert(reason != null or block.isComptime()); |
| 2267 | return sema.resolveValue(inst) orelse { |
| 2268 | return sema.failWithNeededComptime(block, src, reason); |
| 2269 | }; |
| 2270 | } |
| 2271 | |
| 2272 | /// Like `resolveValue`, but emits an error if the value is comptime-known to be undefined. |
| 2273 | fn resolveDefinedValue( |
| 2274 | sema: *Sema, |
| 2275 | block: *Block, |
| 2276 | src: LazySrcLoc, |
| 2277 | air_ref: Air.Inst.Ref, |
| 2278 | ) CompileError!?Value { |
| 2279 | const pt = sema.pt; |
| 2280 | const zcu = pt.zcu; |
| 2281 | const val = sema.resolveValue(air_ref) orelse return null; |
| 2282 | if (val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, null); |
| 2283 | return val; |
| 2284 | } |
| 2285 | |
| 2286 | /// Like `resolveValue`, but emits an error if the value is not comptime-known or is undefined. |
| 2287 | pub fn resolveConstDefinedValue( |
| 2288 | sema: *Sema, |
| 2289 | block: *Block, |
| 2290 | src: LazySrcLoc, |
| 2291 | air_ref: Air.Inst.Ref, |
| 2292 | /// `null` may be passed only if `block.isComptime()`. It indicates that the reason for the value |
| 2293 | /// being comptime-resolved is that the block is being comptime-evaluated. |
| 2294 | reason: ?ComptimeReason, |
| 2295 | ) CompileError!Value { |
| 2296 | const val = try sema.resolveConstValue(block, src, air_ref, reason); |
| 2297 | if (val.isUndef(sema.pt.zcu)) return sema.failWithUseOfUndef(block, src, null); |
| 2298 | return val; |
| 2299 | } |
| 2300 | |
| 2301 | /// Value Tag may be `undef` or `variable`. |
| 2302 | pub fn resolveFinalDeclValue( |
| 2303 | sema: *Sema, |
| 2304 | block: *Block, |
| 2305 | src: LazySrcLoc, |
| 2306 | air_ref: Air.Inst.Ref, |
| 2307 | ) CompileError!Value { |
| 2308 | const zcu = sema.pt.zcu; |
| 2309 | const val = try sema.resolveConstValue(block, src, air_ref, .{ .simple = .container_var_init }); |
| 2310 | if (val.canMutateComptimeVarState(zcu)) { |
| 2311 | const ip = &zcu.intern_pool; |
| 2312 | const nav = ip.getNav(sema.owner.unwrap().nav_val); |
| 2313 | return sema.failWithContainsReferenceToComptimeVar(block, src, nav.name, "global variable", val); |
| 2314 | } |
| 2315 | return val; |
| 2316 | } |
| 2317 | |
| 2318 | fn failWithNeededComptime( |
| 2319 | sema: *Sema, |
| 2320 | block: *Block, |
| 2321 | src: LazySrcLoc, |
| 2322 | /// `null` may be passed only if `block.isComptime()`. It indicates that the reason for the value |
| 2323 | /// being comptime-resolved is that the block is being comptime-evaluated. |
| 2324 | reason: ?ComptimeReason, |
| 2325 | ) CompileError { |
| 2326 | const msg, const fail_block = msg: { |
| 2327 | const msg = try sema.errMsg(src, "unable to resolve comptime value", .{}); |
| 2328 | errdefer msg.destroy(sema.gpa); |
| 2329 | const fail_block = if (reason) |r| b: { |
| 2330 | try r.explain(sema, src, msg); |
| 2331 | break :b block; |
| 2332 | } else b: { |
| 2333 | break :b try block.explainWhyBlockIsComptime(msg); |
| 2334 | }; |
| 2335 | break :msg .{ msg, fail_block }; |
| 2336 | }; |
| 2337 | return sema.failWithOwnedErrorMsg(fail_block, msg); |
| 2338 | } |
| 2339 | |
| 2340 | pub fn failWithUseOfUndef(sema: *Sema, block: *Block, src: LazySrcLoc, vector_index: ?usize) CompileError { |
| 2341 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 2342 | const msg = try sema.errMsg(src, "use of undefined value here causes illegal behavior", .{}); |
| 2343 | errdefer msg.destroy(sema.gpa); |
| 2344 | if (vector_index) |i| try sema.errNote(src, msg, "when computing vector element at index '{d}'", .{i}); |
| 2345 | break :msg msg; |
| 2346 | }); |
| 2347 | } |
| 2348 | |
| 2349 | pub fn failWithUndefSliceLen(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError { |
| 2350 | return sema.fail(block, src, "use of slice with undefined length here causes illegal behavior", .{}); |
| 2351 | } |
| 2352 | |
| 2353 | pub fn failWithDivideByZero(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError { |
| 2354 | return sema.fail(block, src, "division by zero here causes illegal behavior", .{}); |
| 2355 | } |
| 2356 | |
| 2357 | pub fn failWithTooLargeShiftAmount( |
| 2358 | sema: *Sema, |
| 2359 | block: *Block, |
| 2360 | operand_ty: Type, |
| 2361 | shift_amt: Value, |
| 2362 | shift_src: LazySrcLoc, |
| 2363 | vector_index: ?usize, |
| 2364 | ) CompileError { |
| 2365 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 2366 | const msg = try sema.errMsg( |
| 2367 | shift_src, |
| 2368 | "shift amount '{f}' is too large for operand type '{f}'", |
| 2369 | .{ shift_amt.fmtValueSema(sema.pt, sema), operand_ty.fmt(sema.pt) }, |
| 2370 | ); |
| 2371 | errdefer msg.destroy(sema.gpa); |
| 2372 | if (vector_index) |i| try sema.errNote(shift_src, msg, "when computing vector element at index '{d}'", .{i}); |
| 2373 | break :msg msg; |
| 2374 | }); |
| 2375 | } |
| 2376 | |
| 2377 | pub fn failWithNegativeShiftAmount(sema: *Sema, block: *Block, src: LazySrcLoc, shift_amt: Value, vector_index: ?usize) CompileError { |
| 2378 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 2379 | const msg = try sema.errMsg(src, "shift by negative amount '{f}'", .{shift_amt.fmtValueSema(sema.pt, sema)}); |
| 2380 | errdefer msg.destroy(sema.gpa); |
| 2381 | if (vector_index) |i| try sema.errNote(src, msg, "when computing vector element at index '{d}'", .{i}); |
| 2382 | break :msg msg; |
| 2383 | }); |
| 2384 | } |
| 2385 | |
| 2386 | pub fn failWithUnsupportedComptimeShiftAmount(sema: *Sema, block: *Block, src: LazySrcLoc, vector_index: ?usize) CompileError { |
| 2387 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 2388 | const msg = try sema.errMsg( |
| 2389 | src, |
| 2390 | "this implementation only supports comptime shift amounts of up to 2^{d} - 1 bits", |
| 2391 | .{@min(@bitSizeOf(usize), 64)}, |
| 2392 | ); |
| 2393 | errdefer msg.destroy(sema.gpa); |
| 2394 | if (vector_index) |i| try sema.errNote(src, msg, "when computing vector element at index '{d}'", .{i}); |
| 2395 | break :msg msg; |
| 2396 | }); |
| 2397 | } |
| 2398 | |
| 2399 | fn failWithModRemNegative(sema: *Sema, block: *Block, src: LazySrcLoc, lhs_ty: Type, rhs_ty: Type) CompileError { |
| 2400 | const pt = sema.pt; |
| 2401 | return sema.fail(block, src, "remainder division with '{f}' and '{f}': signed integers and floats must use @rem or @mod", .{ |
| 2402 | lhs_ty.fmt(pt), rhs_ty.fmt(pt), |
| 2403 | }); |
| 2404 | } |
| 2405 | |
| 2406 | fn failWithInvalidSwitchTagCapture(sema: *Sema, block: *Block, tag_capture_src: LazySrcLoc, operand_ty: Type) CompileError { |
| 2407 | const pt = sema.pt; |
| 2408 | const zcu = pt.zcu; |
| 2409 | |
| 2410 | if (operand_ty.zigTypeTag(zcu) == .@"union") { |
| 2411 | assert(operand_ty.containerLayout(zcu) == .@"packed"); |
| 2412 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 2413 | const msg = try sema.errMsg(tag_capture_src, "cannot capture tag of packed union", .{}); |
| 2414 | errdefer msg.destroy(sema.gpa); |
| 2415 | try sema.addDeclaredHereNote(msg, operand_ty); |
| 2416 | if (operand_ty.srcLocOrNull(zcu)) |ty_src| { |
| 2417 | try sema.errNote(ty_src, msg, "consider using a tagged union", .{}); |
| 2418 | } |
| 2419 | break :msg msg; |
| 2420 | }); |
| 2421 | } |
| 2422 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 2423 | const msg = try sema.errMsg(tag_capture_src, "cannot capture tag of non-union type '{f}'", .{ |
| 2424 | operand_ty.fmt(pt), |
| 2425 | }); |
| 2426 | errdefer msg.destroy(sema.gpa); |
| 2427 | try sema.addDeclaredHereNote(msg, operand_ty); |
| 2428 | break :msg msg; |
| 2429 | }); |
| 2430 | } |
| 2431 | |
| 2432 | fn failWithAmbiguousBackingIntType( |
| 2433 | sema: *Sema, |
| 2434 | block: *Block, |
| 2435 | src: LazySrcLoc, |
| 2436 | int_backed_ty: Type, |
| 2437 | builtin_name: []const u8, |
| 2438 | ) CompileError { |
| 2439 | const pt = sema.pt; |
| 2440 | const zcu = pt.zcu; |
| 2441 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 2442 | const msg = try sema.errMsg(src, "{s} is ambiguous for type '{f}'", .{ |
| 2443 | builtin_name, int_backed_ty.fmt(pt), |
| 2444 | }); |
| 2445 | errdefer msg.destroy(sema.gpa); |
| 2446 | try sema.errNote(int_backed_ty.srcLoc(zcu), msg, "backing integer type of {t} is inferred", .{ |
| 2447 | int_backed_ty.zigTypeTag(zcu), |
| 2448 | }); |
| 2449 | try sema.errNote(int_backed_ty.srcLoc(zcu), msg, "consider explicitly specifying the backing integer type", .{}); |
| 2450 | break :msg msg; |
| 2451 | }); |
| 2452 | } |
| 2453 | |
| 2454 | fn failWithExpectedOptionalType(sema: *Sema, block: *Block, src: LazySrcLoc, non_optional_ty: Type) CompileError { |
| 2455 | const pt = sema.pt; |
| 2456 | const msg = msg: { |
| 2457 | const msg = try sema.errMsg(src, "expected optional type, found '{f}'", .{ |
| 2458 | non_optional_ty.fmt(pt), |
| 2459 | }); |
| 2460 | errdefer msg.destroy(sema.gpa); |
| 2461 | if (non_optional_ty.zigTypeTag(pt.zcu) == .error_union) { |
| 2462 | try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); |
| 2463 | } |
| 2464 | try addDeclaredHereNote(sema, msg, non_optional_ty); |
| 2465 | break :msg msg; |
| 2466 | }; |
| 2467 | return sema.failWithOwnedErrorMsg(block, msg); |
| 2468 | } |
| 2469 | |
| 2470 | fn failWithArrayInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError { |
| 2471 | const pt = sema.pt; |
| 2472 | const zcu = pt.zcu; |
| 2473 | const msg = msg: { |
| 2474 | const msg = try sema.errMsg(src, "type '{f}' does not support array initialization syntax", .{ |
| 2475 | ty.fmt(pt), |
| 2476 | }); |
| 2477 | errdefer msg.destroy(sema.gpa); |
| 2478 | if (ty.isSlice(zcu)) { |
| 2479 | try sema.errNote(src, msg, "inferred array length is specified with an underscore: '[_]{f}'", .{ty.childType(zcu).fmt(pt)}); |
| 2480 | } |
| 2481 | break :msg msg; |
| 2482 | }; |
| 2483 | return sema.failWithOwnedErrorMsg(block, msg); |
| 2484 | } |
| 2485 | |
| 2486 | fn failWithStructInitNotSupported(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError { |
| 2487 | const pt = sema.pt; |
| 2488 | return sema.fail(block, src, "type '{f}' does not support struct initialization syntax", .{ |
| 2489 | ty.fmt(pt), |
| 2490 | }); |
| 2491 | } |
| 2492 | |
| 2493 | pub fn failWithIntegerOverflow(sema: *Sema, block: *Block, src: LazySrcLoc, int_ty: Type, val: Value, vector_index: ?usize) CompileError { |
| 2494 | const pt = sema.pt; |
| 2495 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 2496 | const msg = try sema.errMsg(src, "overflow of integer type '{f}' with value '{f}'", .{ |
| 2497 | int_ty.fmt(pt), val.fmtValueSema(pt, sema), |
| 2498 | }); |
| 2499 | errdefer msg.destroy(sema.gpa); |
| 2500 | if (vector_index) |i| try sema.errNote(src, msg, "when computing vector element at index '{d}'", .{i}); |
| 2501 | break :msg msg; |
| 2502 | }); |
| 2503 | } |
| 2504 | |
| 2505 | fn failWithInvalidComptimeFieldStore(sema: *Sema, block: *Block, init_src: LazySrcLoc, container_ty: Type, field_index: usize) CompileError { |
| 2506 | const pt = sema.pt; |
| 2507 | const zcu = pt.zcu; |
| 2508 | const msg = msg: { |
| 2509 | const msg = try sema.errMsg(init_src, "value stored in comptime field does not match the default value of the field", .{}); |
| 2510 | errdefer msg.destroy(sema.gpa); |
| 2511 | |
| 2512 | const struct_type = zcu.typeToStruct(container_ty) orelse break :msg msg; |
| 2513 | try sema.errNote(.{ |
| 2514 | .base_node_inst = struct_type.zir_index, |
| 2515 | .offset = .{ .container_field_value = @intCast(field_index) }, |
| 2516 | }, msg, "default value set here", .{}); |
| 2517 | break :msg msg; |
| 2518 | }; |
| 2519 | return sema.failWithOwnedErrorMsg(block, msg); |
| 2520 | } |
| 2521 | |
| 2522 | fn failWithUseOfAsync(sema: *Sema, block: *Block, src: LazySrcLoc) CompileError { |
| 2523 | const msg = msg: { |
| 2524 | const msg = try sema.errMsg(src, "async has not been implemented in the self-hosted compiler yet", .{}); |
| 2525 | errdefer msg.destroy(sema.gpa); |
| 2526 | break :msg msg; |
| 2527 | }; |
| 2528 | return sema.failWithOwnedErrorMsg(block, msg); |
| 2529 | } |
| 2530 | |
| 2531 | fn failWithInvalidFieldAccess( |
| 2532 | sema: *Sema, |
| 2533 | block: *Block, |
| 2534 | src: LazySrcLoc, |
| 2535 | object_ty: Type, |
| 2536 | field_name: InternPool.NullTerminatedString, |
| 2537 | ) CompileError { |
| 2538 | const pt = sema.pt; |
| 2539 | const zcu = pt.zcu; |
| 2540 | const inner_ty = if (object_ty.isSinglePointer(zcu)) object_ty.childType(zcu) else object_ty; |
| 2541 | |
| 2542 | if (inner_ty.zigTypeTag(zcu) == .optional) opt: { |
| 2543 | const child_ty = inner_ty.optionalChild(zcu); |
| 2544 | if (!typeSupportsFieldAccess(zcu, child_ty, field_name)) break :opt; |
| 2545 | const msg = msg: { |
| 2546 | const msg = try sema.errMsg(src, "optional type '{f}' does not support field access", .{object_ty.fmt(pt)}); |
| 2547 | errdefer msg.destroy(sema.gpa); |
| 2548 | try sema.errNote(src, msg, "consider using '.?', 'orelse', or 'if'", .{}); |
| 2549 | break :msg msg; |
| 2550 | }; |
| 2551 | return sema.failWithOwnedErrorMsg(block, msg); |
| 2552 | } else if (inner_ty.zigTypeTag(zcu) == .error_union) err: { |
| 2553 | const child_ty = inner_ty.errorUnionPayload(zcu); |
| 2554 | if (!typeSupportsFieldAccess(zcu, child_ty, field_name)) break :err; |
| 2555 | const msg = msg: { |
| 2556 | const msg = try sema.errMsg(src, "error union type '{f}' does not support field access", .{object_ty.fmt(pt)}); |
| 2557 | errdefer msg.destroy(sema.gpa); |
| 2558 | try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); |
| 2559 | break :msg msg; |
| 2560 | }; |
| 2561 | return sema.failWithOwnedErrorMsg(block, msg); |
| 2562 | } |
| 2563 | return sema.fail(block, src, "type '{f}' does not support field access", .{object_ty.fmt(pt)}); |
| 2564 | } |
| 2565 | |
| 2566 | fn typeSupportsFieldAccess(zcu: *const Zcu, ty: Type, field_name: InternPool.NullTerminatedString) bool { |
| 2567 | const ip = &zcu.intern_pool; |
| 2568 | switch (ty.zigTypeTag(zcu)) { |
| 2569 | .array => return field_name.eqlSlice("len", ip), |
| 2570 | .pointer => { |
| 2571 | const ptr_info = ty.ptrInfo(zcu); |
| 2572 | if (ptr_info.flags.size == .slice) { |
| 2573 | return field_name.eqlSlice("ptr", ip) or field_name.eqlSlice("len", ip); |
| 2574 | } else if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .array) { |
| 2575 | return field_name.eqlSlice("len", ip); |
| 2576 | } else return false; |
| 2577 | }, |
| 2578 | .type, .@"struct", .@"union" => return true, |
| 2579 | else => return false, |
| 2580 | } |
| 2581 | } |
| 2582 | |
| 2583 | fn failWithComptimeErrorRetTrace( |
| 2584 | sema: *Sema, |
| 2585 | block: *Block, |
| 2586 | src: LazySrcLoc, |
| 2587 | name: InternPool.NullTerminatedString, |
| 2588 | ) CompileError { |
| 2589 | const pt = sema.pt; |
| 2590 | const zcu = pt.zcu; |
| 2591 | const msg = msg: { |
| 2592 | const msg = try sema.errMsg(src, "caught unexpected error '{f}'", .{name.fmt(&zcu.intern_pool)}); |
| 2593 | errdefer msg.destroy(sema.gpa); |
| 2594 | |
| 2595 | for (sema.comptime_err_ret_trace.items) |src_loc| { |
| 2596 | try sema.errNote(src_loc, msg, "error returned here", .{}); |
| 2597 | } |
| 2598 | break :msg msg; |
| 2599 | }; |
| 2600 | return sema.failWithOwnedErrorMsg(block, msg); |
| 2601 | } |
| 2602 | |
| 2603 | fn failWithInvalidPtrArithmetic(sema: *Sema, block: *Block, src: LazySrcLoc, arithmetic: []const u8, supports: []const u8) CompileError { |
| 2604 | const msg = msg: { |
| 2605 | const msg = try sema.errMsg(src, "invalid {s} arithmetic operator", .{arithmetic}); |
| 2606 | errdefer msg.destroy(sema.gpa); |
| 2607 | try sema.errNote(src, msg, "{s} arithmetic only supports {s}", .{ arithmetic, supports }); |
| 2608 | break :msg msg; |
| 2609 | }; |
| 2610 | return sema.failWithOwnedErrorMsg(block, msg); |
| 2611 | } |
| 2612 | |
| 2613 | /// We don't return a pointer to the new error note because the pointer |
| 2614 | /// becomes invalid when you add another one. |
| 2615 | pub fn errNote( |
| 2616 | sema: *Sema, |
| 2617 | src: LazySrcLoc, |
| 2618 | parent: *Zcu.ErrorMsg, |
| 2619 | comptime format: []const u8, |
| 2620 | args: anytype, |
| 2621 | ) error{OutOfMemory}!void { |
| 2622 | return sema.pt.zcu.errNote(src, parent, format, args); |
| 2623 | } |
| 2624 | |
| 2625 | fn addFieldErrNote( |
| 2626 | sema: *Sema, |
| 2627 | container_ty: Type, |
| 2628 | field_index: usize, |
| 2629 | parent: *Zcu.ErrorMsg, |
| 2630 | comptime format: []const u8, |
| 2631 | args: anytype, |
| 2632 | ) !void { |
| 2633 | @branchHint(.cold); |
| 2634 | const type_src = container_ty.srcLocOrNull(sema.pt.zcu) orelse return; |
| 2635 | const field_src: LazySrcLoc = .{ |
| 2636 | .base_node_inst = type_src.base_node_inst, |
| 2637 | .offset = .{ .container_field_name = @intCast(field_index) }, |
| 2638 | }; |
| 2639 | try sema.errNote(field_src, parent, format, args); |
| 2640 | } |
| 2641 | |
| 2642 | pub fn errMsg( |
| 2643 | sema: *Sema, |
| 2644 | src: LazySrcLoc, |
| 2645 | comptime format: []const u8, |
| 2646 | args: anytype, |
| 2647 | ) Allocator.Error!*Zcu.ErrorMsg { |
| 2648 | assert(src.offset != .unneeded); |
| 2649 | return Zcu.ErrorMsg.create(sema.gpa, src, format, args); |
| 2650 | } |
| 2651 | |
| 2652 | fn typeMismatchErrMsg(sema: *Sema, src: LazySrcLoc, expected: Type, found: Type) Allocator.Error!*Zcu.ErrorMsg { |
| 2653 | const pt = sema.pt; |
| 2654 | var cmp: Type.Comparison = try .init(&.{ expected, found }, pt); |
| 2655 | defer cmp.deinit(pt); |
| 2656 | |
| 2657 | const msg = try sema.errMsg(src, "expected type '{f}', found '{f}'", .{ |
| 2658 | cmp.fmtType(expected, pt), |
| 2659 | cmp.fmtType(found, pt), |
| 2660 | }); |
| 2661 | errdefer msg.destroy(sema.gpa); |
| 2662 | |
| 2663 | for (cmp.type_dedupe_cache.keys(), cmp.type_dedupe_cache.values()) |ty, value| { |
| 2664 | if (value == .dont_dedupe) continue; |
| 2665 | const placeholder = value.dedupe; |
| 2666 | try sema.errNote(src, msg, "{f} = {f}", .{ placeholder, ty.fmt(pt) }); |
| 2667 | } |
| 2668 | |
| 2669 | return msg; |
| 2670 | } |
| 2671 | |
| 2672 | pub fn fail( |
| 2673 | sema: *Sema, |
| 2674 | block: *Block, |
| 2675 | src: LazySrcLoc, |
| 2676 | comptime format: []const u8, |
| 2677 | args: anytype, |
| 2678 | ) SemaError { |
| 2679 | const err_msg = try sema.errMsg(src, format, args); |
| 2680 | inline for (args) |arg| { |
| 2681 | if (@TypeOf(arg) == Type.Formatter) { |
| 2682 | try addDeclaredHereNote(sema, err_msg, arg.data.ty); |
| 2683 | } |
| 2684 | } |
| 2685 | return sema.failWithOwnedErrorMsg(block, err_msg); |
| 2686 | } |
| 2687 | |
| 2688 | fn failWithTypeMismatch(sema: *Sema, block: *Block, src: LazySrcLoc, expected: Type, found: Type) CompileError { |
| 2689 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 2690 | const msg = try sema.typeMismatchErrMsg(src, expected, found); |
| 2691 | errdefer msg.destroy(sema.gpa); |
| 2692 | try addDeclaredHereNote(sema, msg, expected); |
| 2693 | try addDeclaredHereNote(sema, msg, found); |
| 2694 | break :msg msg; |
| 2695 | }); |
| 2696 | } |
| 2697 | |
| 2698 | pub fn failWithOwnedErrorMsg(sema: *Sema, block: ?*Block, err_msg: *Zcu.ErrorMsg) SemaError { |
| 2699 | @branchHint(.cold); |
| 2700 | const zcu = sema.pt.zcu; |
| 2701 | const comp = zcu.comp; |
| 2702 | const gpa = comp.gpa; |
| 2703 | const io = comp.io; |
| 2704 | |
| 2705 | assert(sema.err == null); |
| 2706 | |
| 2707 | if (build_options.enable_debug_extensions and comp.debug_compile_errors) { |
| 2708 | var wip_errors: std.zig.ErrorBundle.Wip = undefined; |
| 2709 | wip_errors.init(gpa) catch @panic("out of memory"); |
| 2710 | Compilation.addModuleErrorMsg(zcu, &wip_errors, err_msg.*, false) catch @panic("out of memory"); |
| 2711 | std.debug.print("compile error during Sema:\n", .{}); |
| 2712 | var error_bundle = wip_errors.toOwnedBundle("") catch @panic("out of memory"); |
| 2713 | error_bundle.renderToStderr(io, .{}, .auto) catch @panic("failed to print to stderr"); |
| 2714 | std.debug.panicExtra(@returnAddress(), "unexpected compile error occurred", .{}); |
| 2715 | } |
| 2716 | |
| 2717 | if (block) |start_block| { |
| 2718 | var block_it = start_block; |
| 2719 | while (block_it.inlining) |inlining| { |
| 2720 | const note_str = note: { |
| 2721 | if (inlining.is_generic_instantiation) break :note "generic function instantiated here"; |
| 2722 | if (inlining.call_block.isComptime()) break :note "called at comptime here"; |
| 2723 | break :note "called inline here"; |
| 2724 | }; |
| 2725 | try sema.errNote(inlining.call_src, err_msg, "{s}", .{note_str}); |
| 2726 | block_it = inlining.call_block; |
| 2727 | } |
| 2728 | } |
| 2729 | |
| 2730 | err_msg.reference_trace_root = sema.owner.toOptional(); |
| 2731 | |
| 2732 | try zcu.failed_analysis.putNoClobber(gpa, sema.owner, err_msg); |
| 2733 | assert(!zcu.transitive_failed_analysis.contains(sema.owner)); |
| 2734 | |
| 2735 | sema.err = err_msg; |
| 2736 | return error.AlreadyReported; |
| 2737 | } |
| 2738 | |
| 2739 | /// Given an ErrorMsg, modify its message and source location to the given values, turning the |
| 2740 | /// original message into a note. Notes on the original message are preserved as further notes. |
| 2741 | /// Reference trace is preserved. |
| 2742 | fn reparentOwnedErrorMsg( |
| 2743 | sema: *Sema, |
| 2744 | src: LazySrcLoc, |
| 2745 | msg: *Zcu.ErrorMsg, |
| 2746 | comptime format: []const u8, |
| 2747 | args: anytype, |
| 2748 | ) !void { |
| 2749 | const msg_str = try std.fmt.allocPrint(sema.gpa, format, args); |
| 2750 | |
| 2751 | const orig_notes = msg.notes.len; |
| 2752 | msg.notes = try sema.gpa.realloc(msg.notes, orig_notes + 1); |
| 2753 | @memmove(msg.notes[1..][0..orig_notes], msg.notes[0..orig_notes]); |
| 2754 | msg.notes[0] = .{ |
| 2755 | .src_loc = msg.src_loc, |
| 2756 | .msg = msg.msg, |
| 2757 | }; |
| 2758 | |
| 2759 | msg.src_loc = src; |
| 2760 | msg.msg = msg_str; |
| 2761 | } |
| 2762 | |
| 2763 | const align_ty: Type = .u29; |
| 2764 | |
| 2765 | pub fn analyzeAsAlign( |
| 2766 | sema: *Sema, |
| 2767 | block: *Block, |
| 2768 | src: LazySrcLoc, |
| 2769 | air_ref: Air.Inst.Ref, |
| 2770 | ) !Alignment { |
| 2771 | const alignment_big = try sema.analyzeAsInt( |
| 2772 | block, |
| 2773 | src, |
| 2774 | air_ref, |
| 2775 | align_ty, |
| 2776 | .{ .simple = .@"align" }, |
| 2777 | ); |
| 2778 | return sema.validateAlign(block, src, alignment_big); |
| 2779 | } |
| 2780 | |
| 2781 | fn validateAlign( |
| 2782 | sema: *Sema, |
| 2783 | block: *Block, |
| 2784 | src: LazySrcLoc, |
| 2785 | alignment: u64, |
| 2786 | ) !Alignment { |
| 2787 | if (alignment == 0) return sema.fail(block, src, "alignment must be >= 1", .{}); |
| 2788 | if (!std.math.isPowerOfTwo(alignment)) { |
| 2789 | return sema.fail(block, src, "alignment value '{d}' is not a power of two", .{ |
| 2790 | alignment, |
| 2791 | }); |
| 2792 | } |
| 2793 | return Alignment.fromNonzeroByteUnits(alignment); |
| 2794 | } |
| 2795 | |
| 2796 | fn resolveAlign( |
| 2797 | sema: *Sema, |
| 2798 | block: *Block, |
| 2799 | src: LazySrcLoc, |
| 2800 | zir_ref: Zir.Inst.Ref, |
| 2801 | ) !Alignment { |
| 2802 | const air_ref = sema.resolveInst(zir_ref); |
| 2803 | return sema.analyzeAsAlign(block, src, air_ref); |
| 2804 | } |
| 2805 | |
| 2806 | fn resolveInt( |
| 2807 | sema: *Sema, |
| 2808 | block: *Block, |
| 2809 | src: LazySrcLoc, |
| 2810 | zir_ref: Zir.Inst.Ref, |
| 2811 | dest_ty: Type, |
| 2812 | reason: ComptimeReason, |
| 2813 | ) !u64 { |
| 2814 | const air_ref = sema.resolveInst(zir_ref); |
| 2815 | return sema.analyzeAsInt(block, src, air_ref, dest_ty, reason); |
| 2816 | } |
| 2817 | |
| 2818 | fn analyzeAsInt( |
| 2819 | sema: *Sema, |
| 2820 | block: *Block, |
| 2821 | src: LazySrcLoc, |
| 2822 | air_ref: Air.Inst.Ref, |
| 2823 | dest_ty: Type, |
| 2824 | reason: ComptimeReason, |
| 2825 | ) !u64 { |
| 2826 | const coerced = try sema.coerce(block, dest_ty, air_ref, src); |
| 2827 | const val = try sema.resolveConstDefinedValue(block, src, coerced, reason); |
| 2828 | return val.toUnsignedInt(sema.pt.zcu); |
| 2829 | } |
| 2830 | |
| 2831 | fn analyzeValueAsCallconv( |
| 2832 | sema: *Sema, |
| 2833 | block: *Block, |
| 2834 | src: LazySrcLoc, |
| 2835 | val: Value, |
| 2836 | ) !std.lang.CallingConvention { |
| 2837 | return interpretStdLangType(sema, block, src, val, std.lang.CallingConvention); |
| 2838 | } |
| 2839 | |
| 2840 | fn interpretStdLangType( |
| 2841 | sema: *Sema, |
| 2842 | block: *Block, |
| 2843 | src: LazySrcLoc, |
| 2844 | val: Value, |
| 2845 | comptime T: type, |
| 2846 | ) !T { |
| 2847 | return val.interpret(T, sema.pt) catch |err| switch (err) { |
| 2848 | error.OutOfMemory => |e| return e, |
| 2849 | error.UndefinedValue => return sema.failWithUseOfUndef(block, src, null), |
| 2850 | error.TypeMismatch => @panic("std.lang is corrupt"), |
| 2851 | }; |
| 2852 | } |
| 2853 | |
| 2854 | fn uninterpretStdLangType( |
| 2855 | sema: *Sema, |
| 2856 | val: anytype, |
| 2857 | ty: Type, |
| 2858 | ) !Value { |
| 2859 | return Value.uninterpret(val, ty, sema.pt) catch |err| switch (err) { |
| 2860 | error.OutOfMemory => |e| return e, |
| 2861 | error.TypeMismatch => @panic("std.lang is corrupt"), |
| 2862 | }; |
| 2863 | } |
| 2864 | |
| 2865 | fn zirTupleDecl( |
| 2866 | sema: *Sema, |
| 2867 | block: *Block, |
| 2868 | extended: Zir.Inst.Extended.InstData, |
| 2869 | ) CompileError!Air.Inst.Ref { |
| 2870 | const pt = sema.pt; |
| 2871 | const zcu = pt.zcu; |
| 2872 | const comp = zcu.comp; |
| 2873 | const gpa = comp.gpa; |
| 2874 | const io = comp.io; |
| 2875 | |
| 2876 | const fields_len = extended.small; |
| 2877 | const extra = sema.code.extraData(Zir.Inst.TupleDecl, extended.operand); |
| 2878 | var extra_index = extra.end; |
| 2879 | |
| 2880 | const types = try sema.arena.alloc(InternPool.Index, fields_len); |
| 2881 | const inits = try sema.arena.alloc(InternPool.Index, fields_len); |
| 2882 | |
| 2883 | const extra_as_refs: []const Zir.Inst.Ref = @ptrCast(sema.code.extra); |
| 2884 | |
| 2885 | for (types, inits, 0..) |*field_ty, *field_init, field_index| { |
| 2886 | const zir_field_ty, const zir_field_init = extra_as_refs[extra_index..][0..2].*; |
| 2887 | extra_index += 2; |
| 2888 | |
| 2889 | const type_src = block.src(.{ .tuple_field_type = .{ |
| 2890 | .tuple_decl_node_offset = extra.data.src_node, |
| 2891 | .elem_index = @intCast(field_index), |
| 2892 | } }); |
| 2893 | const init_src = block.src(.{ .tuple_field_init = .{ |
| 2894 | .tuple_decl_node_offset = extra.data.src_node, |
| 2895 | .elem_index = @intCast(field_index), |
| 2896 | } }); |
| 2897 | |
| 2898 | const field_type = try sema.resolveType(block, type_src, zir_field_ty); |
| 2899 | try sema.validateTupleFieldType(block, field_type, type_src); |
| 2900 | |
| 2901 | field_ty.* = field_type.toIntern(); |
| 2902 | field_init.* = init: { |
| 2903 | if (zir_field_init != .none) { |
| 2904 | const uncoerced_field_init = sema.resolveInst(zir_field_init); |
| 2905 | const coerced_field_init = try sema.coerce(block, field_type, uncoerced_field_init, init_src); |
| 2906 | const field_init_val = try sema.resolveConstDefinedValue(block, init_src, coerced_field_init, .{ .simple = .tuple_field_default_value }); |
| 2907 | if (field_init_val.canMutateComptimeVarState(zcu)) { |
| 2908 | const field_name = try zcu.intern_pool.getOrPutStringFmt(gpa, io, pt.tid, "{d}", .{field_index}, .no_embedded_nulls); |
| 2909 | return sema.failWithContainsReferenceToComptimeVar(block, init_src, field_name, "field default value", field_init_val); |
| 2910 | } |
| 2911 | break :init field_init_val.toIntern(); |
| 2912 | } |
| 2913 | break :init .none; |
| 2914 | }; |
| 2915 | } |
| 2916 | |
| 2917 | return Air.internedToRef(try zcu.intern_pool.getTupleType(gpa, io, pt.tid, .{ |
| 2918 | .types = types, |
| 2919 | .values = inits, |
| 2920 | })); |
| 2921 | } |
| 2922 | |
| 2923 | fn validateTupleFieldType( |
| 2924 | sema: *Sema, |
| 2925 | block: *Block, |
| 2926 | field_ty: Type, |
| 2927 | field_ty_src: LazySrcLoc, |
| 2928 | ) CompileError!void { |
| 2929 | const gpa = sema.gpa; |
| 2930 | const zcu = sema.pt.zcu; |
| 2931 | if (field_ty.zigTypeTag(zcu) == .@"opaque") { |
| 2932 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 2933 | const msg = try sema.errMsg(field_ty_src, "opaque types have unknown size and therefore cannot be directly embedded in tuples", .{}); |
| 2934 | errdefer msg.destroy(gpa); |
| 2935 | |
| 2936 | try sema.addDeclaredHereNote(msg, field_ty); |
| 2937 | break :msg msg; |
| 2938 | }); |
| 2939 | } |
| 2940 | if (field_ty.zigTypeTag(zcu) == .noreturn) { |
| 2941 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 2942 | const msg = try sema.errMsg(field_ty_src, "tuple fields cannot be 'noreturn'", .{}); |
| 2943 | errdefer msg.destroy(gpa); |
| 2944 | |
| 2945 | try sema.addDeclaredHereNote(msg, field_ty); |
| 2946 | break :msg msg; |
| 2947 | }); |
| 2948 | } |
| 2949 | } |
| 2950 | |
| 2951 | /// Given a ZIR extra index which points to a list of `Zir.Inst.Capture`, |
| 2952 | /// resolves this into a list of `InternPool.CaptureValue` allocated by `arena`. |
| 2953 | fn getCaptures( |
| 2954 | sema: *Sema, |
| 2955 | block: *Block, |
| 2956 | type_src: LazySrcLoc, |
| 2957 | zir_captures: []const Zir.Inst.Capture, |
| 2958 | zir_capture_names: []const Zir.NullTerminatedString, |
| 2959 | ) ![]InternPool.CaptureValue { |
| 2960 | const pt = sema.pt; |
| 2961 | const zcu = pt.zcu; |
| 2962 | const comp = zcu.comp; |
| 2963 | const gpa = comp.gpa; |
| 2964 | const io = comp.io; |
| 2965 | const ip = &zcu.intern_pool; |
| 2966 | |
| 2967 | const parent_ty: Type = .fromInterned(zcu.namespacePtr(block.namespace).owner_type); |
| 2968 | const parent_captures: InternPool.CaptureValue.Slice = parent_ty.getCaptures(zcu); |
| 2969 | |
| 2970 | const captures = try sema.arena.alloc(InternPool.CaptureValue, zir_captures.len); |
| 2971 | |
| 2972 | for (zir_captures, zir_capture_names, captures) |zir_capture, zir_name, *capture| { |
| 2973 | const zir_name_slice = sema.code.nullTerminatedString(zir_name); |
| 2974 | capture.* = switch (zir_capture.unwrap()) { |
| 2975 | .nested => |parent_idx| parent_captures.get(ip)[parent_idx], |
| 2976 | .instruction_load => |ptr_inst| capture: { |
| 2977 | const ptr_ref = sema.resolveInst(ptr_inst.toRef()); |
| 2978 | const ptr_val = sema.resolveValue(ptr_ref) orelse { |
| 2979 | break :capture .wrap(.{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() }); |
| 2980 | }; |
| 2981 | // TODO: better source location |
| 2982 | const loaded_val = try sema.pointerDeref(block, type_src, ptr_val, sema.typeOf(ptr_ref)) orelse { |
| 2983 | break :capture .wrap(.{ .runtime = sema.typeOf(ptr_ref).childType(zcu).toIntern() }); |
| 2984 | }; |
| 2985 | if (loaded_val.canMutateComptimeVarState(zcu)) { |
| 2986 | const field_name = try ip.getOrPutString(gpa, io, pt.tid, zir_name_slice, .no_embedded_nulls); |
| 2987 | return sema.failWithContainsReferenceToComptimeVar(block, type_src, field_name, "captured value", loaded_val); |
| 2988 | } |
| 2989 | break :capture .wrap(.{ .@"comptime" = loaded_val.toIntern() }); |
| 2990 | }, |
| 2991 | .instruction => |inst| capture: { |
| 2992 | const air_ref = sema.resolveInst(inst.toRef()); |
| 2993 | if (sema.resolveValue(air_ref)) |val| { |
| 2994 | if (val.canMutateComptimeVarState(zcu)) { |
| 2995 | const field_name = try ip.getOrPutString(gpa, io, pt.tid, zir_name_slice, .no_embedded_nulls); |
| 2996 | return sema.failWithContainsReferenceToComptimeVar(block, type_src, field_name, "captured value", val); |
| 2997 | } |
| 2998 | break :capture .wrap(.{ .@"comptime" = val.toIntern() }); |
| 2999 | } |
| 3000 | break :capture .wrap(.{ .runtime = sema.typeOf(air_ref).toIntern() }); |
| 3001 | }, |
| 3002 | .decl_val => |str| capture: { |
| 3003 | const decl_name = try ip.getOrPutString( |
| 3004 | gpa, |
| 3005 | io, |
| 3006 | pt.tid, |
| 3007 | sema.code.nullTerminatedString(str), |
| 3008 | .no_embedded_nulls, |
| 3009 | ); |
| 3010 | const nav = try sema.lookupIdentifier(block, decl_name); |
| 3011 | break :capture .wrap(.{ .nav_val = nav }); |
| 3012 | }, |
| 3013 | .decl_ref => |str| capture: { |
| 3014 | const decl_name = try ip.getOrPutString( |
| 3015 | gpa, |
| 3016 | io, |
| 3017 | pt.tid, |
| 3018 | sema.code.nullTerminatedString(str), |
| 3019 | .no_embedded_nulls, |
| 3020 | ); |
| 3021 | const nav = try sema.lookupIdentifier(block, decl_name); |
| 3022 | break :capture InternPool.CaptureValue.wrap(.{ .nav_ref = nav }); |
| 3023 | }, |
| 3024 | }; |
| 3025 | } |
| 3026 | |
| 3027 | return captures; |
| 3028 | } |
| 3029 | |
| 3030 | fn zirErrorSetDecl( |
| 3031 | sema: *Sema, |
| 3032 | inst: Zir.Inst.Index, |
| 3033 | ) CompileError!Air.Inst.Ref { |
| 3034 | const pt = sema.pt; |
| 3035 | const zcu = pt.zcu; |
| 3036 | const comp = zcu.comp; |
| 3037 | const gpa = comp.gpa; |
| 3038 | const io = comp.io; |
| 3039 | |
| 3040 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 3041 | const extra = sema.code.extraData(Zir.Inst.ErrorSetDecl, inst_data.payload_index); |
| 3042 | |
| 3043 | var names: InferredErrorSet.NameMap = .{}; |
| 3044 | try names.ensureUnusedCapacity(sema.arena, extra.data.fields_len); |
| 3045 | |
| 3046 | var extra_index: u32 = @intCast(extra.end); |
| 3047 | const extra_index_end = extra_index + extra.data.fields_len; |
| 3048 | while (extra_index < extra_index_end) : (extra_index += 1) { |
| 3049 | const name_index: Zir.NullTerminatedString = @fromBackingInt(@intCast(sema.code.extra[extra_index])); |
| 3050 | const name = sema.code.nullTerminatedString(name_index); |
| 3051 | const name_ip = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls); |
| 3052 | _ = try pt.getErrorValue(name_ip); |
| 3053 | const result = names.getOrPutAssumeCapacity(name_ip); |
| 3054 | assert(!result.found_existing); // verified in AstGen |
| 3055 | } |
| 3056 | |
| 3057 | return Air.internedToRef((try pt.errorSetFromUnsortedNames(names.keys())).toIntern()); |
| 3058 | } |
| 3059 | |
| 3060 | fn zirRetPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 3061 | const pt = sema.pt; |
| 3062 | const zcu = pt.zcu; |
| 3063 | |
| 3064 | const src = block.nodeOffset(sema.code.instructions.items(.data)[@backingInt(inst)].node); |
| 3065 | |
| 3066 | if (block.isComptime() or sema.fn_ret_ty.comptimeOnly(zcu)) { |
| 3067 | return sema.analyzeComptimeAlloc(block, src, sema.fn_ret_ty, .none); |
| 3068 | } |
| 3069 | |
| 3070 | const target = zcu.getTarget(); |
| 3071 | const ptr_type = try pt.ptrType(.{ |
| 3072 | .child = sema.fn_ret_ty.toIntern(), |
| 3073 | .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, |
| 3074 | }); |
| 3075 | |
| 3076 | if (block.inlining != null) { |
| 3077 | // We are inlining a function call; this should be emitted as an alloc, not a ret_ptr. |
| 3078 | // TODO when functions gain result location support, the inlining struct in |
| 3079 | // Block should contain the return pointer, and we would pass that through here. |
| 3080 | return block.addTy(.alloc, ptr_type); |
| 3081 | } |
| 3082 | |
| 3083 | return block.addTy(.ret_ptr, ptr_type); |
| 3084 | } |
| 3085 | |
| 3086 | fn zirRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 3087 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_tok; |
| 3088 | const operand = sema.resolveInst(inst_data.operand); |
| 3089 | return sema.analyzeRef(block, block.tokenOffset(inst_data.src_tok), operand, .none); |
| 3090 | } |
| 3091 | |
| 3092 | fn zirDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 3093 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 3094 | const src = block.nodeOffset(inst_data.src_node); |
| 3095 | const ptr_src = block.src(.{ .node_offset_deref_ptr = inst_data.src_node }); |
| 3096 | const operand = sema.resolveInst(inst_data.operand); |
| 3097 | |
| 3098 | try sema.validateDeref(block, src, operand, sema.typeOf(operand)); |
| 3099 | |
| 3100 | return sema.analyzeLoad(block, src, operand, ptr_src); |
| 3101 | } |
| 3102 | |
| 3103 | fn zirRefDeref(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 3104 | const pt = sema.pt; |
| 3105 | const zcu = pt.zcu; |
| 3106 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 3107 | const src = block.nodeOffset(inst_data.src_node); |
| 3108 | const operand = sema.resolveInst(inst_data.operand); |
| 3109 | const operand_ty = sema.typeOf(operand); |
| 3110 | |
| 3111 | try sema.validateDeref(block, src, operand, operand_ty); |
| 3112 | |
| 3113 | const ptr_info = operand_ty.ptrInfo(zcu); |
| 3114 | return single_ptr: switch (ptr_info.flags.size) { |
| 3115 | .many => unreachable, // cannot be dereferenced directly |
| 3116 | .slice => { |
| 3117 | const slice_val = sema.resolveValue(operand).?; |
| 3118 | const slice = zcu.intern_pool.indexToKey(slice_val.toIntern()).slice; |
| 3119 | break :single_ptr .fromValue(try pt.sliceToArrayPtr(slice)); |
| 3120 | }, |
| 3121 | .c => { |
| 3122 | const single_ptr_ty = try pt.ptrType(p: { |
| 3123 | var p = ptr_info; |
| 3124 | p.flags.size = .one; |
| 3125 | p.flags.is_allowzero = false; |
| 3126 | break :p p; |
| 3127 | }); |
| 3128 | // https://github.com/ziglang/zig/issues/6597 |
| 3129 | if (sema.resolveValue(operand)) |operand_val| { |
| 3130 | if (!operand_val.isNull(zcu)) { |
| 3131 | break :single_ptr .fromValue(try pt.getCoerced(operand_val, single_ptr_ty)); |
| 3132 | } |
| 3133 | } |
| 3134 | if (block.wantSafety()) { |
| 3135 | const is_non_null = try block.addUnOp(.is_non_null, operand); |
| 3136 | try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); |
| 3137 | } |
| 3138 | const single_ptr = try block.addTyOp(.ptr_cast, single_ptr_ty, operand); |
| 3139 | try sema.checkKnownAllocPtr(block, operand, single_ptr); |
| 3140 | break :single_ptr single_ptr; |
| 3141 | }, |
| 3142 | .one => operand, |
| 3143 | }; |
| 3144 | } |
| 3145 | |
| 3146 | fn validateDeref( |
| 3147 | sema: *Sema, |
| 3148 | block: *Block, |
| 3149 | src: LazySrcLoc, |
| 3150 | ref: Air.Inst.Ref, |
| 3151 | ty: Type, |
| 3152 | ) CompileError!void { |
| 3153 | const pt = sema.pt; |
| 3154 | const zcu = pt.zcu; |
| 3155 | const ip = &zcu.intern_pool; |
| 3156 | if (ty.zigTypeTag(zcu) != .pointer) { |
| 3157 | return sema.fail(block, src, "cannot dereference non-pointer type '{f}'", .{ty.fmt(pt)}); |
| 3158 | } |
| 3159 | const size = ty.ptrSize(zcu); |
| 3160 | switch (size) { |
| 3161 | .many => return sema.fail(block, src, "index syntax required for unknown-length pointer type '{f}'", .{ty.fmt(pt)}), |
| 3162 | .one, .c, .slice => {}, |
| 3163 | } |
| 3164 | if (sema.resolveValue(ref)) |val| { |
| 3165 | // Error for deref of undef pointer, unless the pointee is OPV in which case it's legal. |
| 3166 | if (val.isUndef(zcu) and ty.childType(zcu).classify(zcu) != .one_possible_value) { |
| 3167 | return sema.fail(block, src, "cannot dereference undefined value", .{}); |
| 3168 | } |
| 3169 | // We need a defined slice length for the array type the slice should be dereferenced to. |
| 3170 | if (size == .slice and ip.indexToKey(val.toIntern()).slice.len == .undef_usize) { |
| 3171 | return sema.fail(block, src, "cannot dereference slice with undefined length", .{}); |
| 3172 | } |
| 3173 | } else if (size == .slice) { |
| 3174 | return sema.fail(block, src, "index syntax required to access runtime-known slice", .{}); |
| 3175 | } |
| 3176 | } |
| 3177 | |
| 3178 | fn zirEnsureResultUsed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 3179 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 3180 | const operand = sema.resolveInst(inst_data.operand); |
| 3181 | const src = block.nodeOffset(inst_data.src_node); |
| 3182 | |
| 3183 | return sema.ensureResultUsed(block, sema.typeOf(operand), src); |
| 3184 | } |
| 3185 | |
| 3186 | fn ensureResultUsed( |
| 3187 | sema: *Sema, |
| 3188 | block: *Block, |
| 3189 | ty: Type, |
| 3190 | src: LazySrcLoc, |
| 3191 | ) CompileError!void { |
| 3192 | const pt = sema.pt; |
| 3193 | const zcu = pt.zcu; |
| 3194 | switch (ty.zigTypeTag(zcu)) { |
| 3195 | .void, .noreturn => return, |
| 3196 | .error_set => { |
| 3197 | return sema.fail(block, src, "error set of type '{f}' is ignored", .{ty.fmt(pt)}); |
| 3198 | }, |
| 3199 | .error_union => { |
| 3200 | const msg = msg: { |
| 3201 | const msg = try sema.errMsg(src, "error union of type '{f}' is ignored", .{ty.fmt(pt)}); |
| 3202 | errdefer msg.destroy(sema.gpa); |
| 3203 | try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); |
| 3204 | break :msg msg; |
| 3205 | }; |
| 3206 | return sema.failWithOwnedErrorMsg(block, msg); |
| 3207 | }, |
| 3208 | else => { |
| 3209 | const msg = msg: { |
| 3210 | const msg = try sema.errMsg(src, "value of type '{f}' ignored", .{ty.fmt(pt)}); |
| 3211 | errdefer msg.destroy(sema.gpa); |
| 3212 | try sema.errNote(src, msg, "all non-void values must be used", .{}); |
| 3213 | try sema.errNote(src, msg, "to discard the value, assign it to '_'", .{}); |
| 3214 | break :msg msg; |
| 3215 | }; |
| 3216 | return sema.failWithOwnedErrorMsg(block, msg); |
| 3217 | }, |
| 3218 | } |
| 3219 | } |
| 3220 | |
| 3221 | fn zirEnsureResultNonError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 3222 | const pt = sema.pt; |
| 3223 | const zcu = pt.zcu; |
| 3224 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 3225 | const operand = sema.resolveInst(inst_data.operand); |
| 3226 | const src = block.nodeOffset(inst_data.src_node); |
| 3227 | const operand_ty = sema.typeOf(operand); |
| 3228 | switch (operand_ty.zigTypeTag(zcu)) { |
| 3229 | .error_set => return sema.fail(block, src, "error set is discarded", .{}), |
| 3230 | .error_union => { |
| 3231 | const msg = msg: { |
| 3232 | const msg = try sema.errMsg(src, "error union is discarded", .{}); |
| 3233 | errdefer msg.destroy(sema.gpa); |
| 3234 | try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); |
| 3235 | break :msg msg; |
| 3236 | }; |
| 3237 | return sema.failWithOwnedErrorMsg(block, msg); |
| 3238 | }, |
| 3239 | else => return, |
| 3240 | } |
| 3241 | } |
| 3242 | |
| 3243 | fn zirEnsureErrUnionPayloadVoid(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 3244 | const pt = sema.pt; |
| 3245 | const zcu = pt.zcu; |
| 3246 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 3247 | const src = block.nodeOffset(inst_data.src_node); |
| 3248 | const operand = sema.resolveInst(inst_data.operand); |
| 3249 | const operand_ty = sema.typeOf(operand); |
| 3250 | const err_union_ty = if (operand_ty.zigTypeTag(zcu) == .pointer) |
| 3251 | operand_ty.childType(zcu) |
| 3252 | else |
| 3253 | operand_ty; |
| 3254 | if (err_union_ty.zigTypeTag(zcu) != .error_union) return; |
| 3255 | const payload_ty = err_union_ty.errorUnionPayload(zcu).zigTypeTag(zcu); |
| 3256 | if (payload_ty != .void and payload_ty != .noreturn) { |
| 3257 | const msg = msg: { |
| 3258 | const msg = try sema.errMsg(src, "error union payload is ignored", .{}); |
| 3259 | errdefer msg.destroy(sema.gpa); |
| 3260 | try sema.errNote(src, msg, "payload value can be explicitly ignored with '|_|'", .{}); |
| 3261 | break :msg msg; |
| 3262 | }; |
| 3263 | return sema.failWithOwnedErrorMsg(block, msg); |
| 3264 | } |
| 3265 | } |
| 3266 | |
| 3267 | fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 3268 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 3269 | const src = block.nodeOffset(inst_data.src_node); |
| 3270 | const object = sema.resolveInst(inst_data.operand); |
| 3271 | |
| 3272 | return indexablePtrLen(sema, block, src, object); |
| 3273 | } |
| 3274 | |
| 3275 | fn indexablePtrLen( |
| 3276 | sema: *Sema, |
| 3277 | block: *Block, |
| 3278 | src: LazySrcLoc, |
| 3279 | object: Air.Inst.Ref, |
| 3280 | ) CompileError!Air.Inst.Ref { |
| 3281 | const pt = sema.pt; |
| 3282 | const zcu = pt.zcu; |
| 3283 | const comp = zcu.comp; |
| 3284 | const gpa = comp.gpa; |
| 3285 | const io = comp.io; |
| 3286 | const object_ty = sema.typeOf(object); |
| 3287 | const is_pointer_to = object_ty.isSinglePointer(zcu); |
| 3288 | const indexable_ty = if (is_pointer_to) object_ty.childType(zcu) else object_ty; |
| 3289 | try sema.checkIndexable(block, src, indexable_ty); |
| 3290 | const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls); |
| 3291 | return sema.fieldVal(block, src, object, field_name, src); |
| 3292 | } |
| 3293 | |
| 3294 | fn indexablePtrLenOrNone( |
| 3295 | sema: *Sema, |
| 3296 | block: *Block, |
| 3297 | src: LazySrcLoc, |
| 3298 | operand: Air.Inst.Ref, |
| 3299 | ) CompileError!Air.Inst.Ref { |
| 3300 | const pt = sema.pt; |
| 3301 | const zcu = pt.zcu; |
| 3302 | const comp = zcu.comp; |
| 3303 | const gpa = comp.gpa; |
| 3304 | const io = comp.io; |
| 3305 | const operand_ty = sema.typeOf(operand); |
| 3306 | try checkMemOperand(sema, block, src, operand_ty); |
| 3307 | switch (operand_ty.ptrSize(zcu)) { |
| 3308 | .many, .c => return .none, |
| 3309 | .one, .slice => {}, |
| 3310 | } |
| 3311 | const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls); |
| 3312 | return sema.fieldVal(block, src, operand, field_name, src); |
| 3313 | } |
| 3314 | |
| 3315 | fn zirAllocExtended( |
| 3316 | sema: *Sema, |
| 3317 | block: *Block, |
| 3318 | extended: Zir.Inst.Extended.InstData, |
| 3319 | ) CompileError!Air.Inst.Ref { |
| 3320 | const pt = sema.pt; |
| 3321 | const zcu = pt.zcu; |
| 3322 | const gpa = sema.gpa; |
| 3323 | const extra = sema.code.extraData(Zir.Inst.AllocExtended, extended.operand); |
| 3324 | const var_src = block.nodeOffset(extra.data.src_node); |
| 3325 | const ty_src = block.src(.{ .node_offset_var_decl_ty = extra.data.src_node }); |
| 3326 | const align_src = block.src(.{ .node_offset_var_decl_align = extra.data.src_node }); |
| 3327 | const small: Zir.Inst.AllocExtended.Small = @bitCast(extended.small); |
| 3328 | |
| 3329 | var extra_index: usize = extra.end; |
| 3330 | |
| 3331 | const var_ty: Type = if (small.has_type) blk: { |
| 3332 | const type_ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_index])); |
| 3333 | extra_index += 1; |
| 3334 | break :blk try sema.resolveType(block, ty_src, type_ref); |
| 3335 | } else undefined; |
| 3336 | |
| 3337 | const alignment = if (small.has_align) blk: { |
| 3338 | const align_ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_index])); |
| 3339 | extra_index += 1; |
| 3340 | break :blk try sema.resolveAlign(block, align_src, align_ref); |
| 3341 | } else .none; |
| 3342 | |
| 3343 | if (small.has_type) { |
| 3344 | try sema.ensureLayoutResolved(var_ty, var_src, if (small.is_const) .constant else .variable); |
| 3345 | if (block.isComptime() or small.is_comptime or var_ty.comptimeOnly(zcu)) { |
| 3346 | return sema.analyzeComptimeAlloc(block, var_src, var_ty, alignment); |
| 3347 | } |
| 3348 | if (!small.is_const) { |
| 3349 | try sema.validateVarType(block, ty_src, var_ty, false); |
| 3350 | } |
| 3351 | const target = pt.zcu.getTarget(); |
| 3352 | if (sema.func_is_naked and var_ty.hasRuntimeBits(zcu)) { |
| 3353 | const store_src = block.src(.{ .node_offset_store_ptr = extra.data.src_node }); |
| 3354 | return sema.fail(block, store_src, "local variable in naked function", .{}); |
| 3355 | } |
| 3356 | const ptr_type = try pt.ptrType(.{ |
| 3357 | .child = var_ty.toIntern(), |
| 3358 | .flags = .{ |
| 3359 | .alignment = alignment, |
| 3360 | .address_space = target_util.defaultAddressSpace(target, .local), |
| 3361 | }, |
| 3362 | }); |
| 3363 | const ptr = try block.addTy(.alloc, ptr_type); |
| 3364 | if (small.is_const) { |
| 3365 | const ptr_inst = ptr.toIndex().?; |
| 3366 | try sema.maybe_comptime_allocs.put(gpa, ptr_inst, .{ .runtime_index = block.runtime_index }); |
| 3367 | try sema.base_allocs.put(gpa, ptr_inst, ptr_inst); |
| 3368 | } |
| 3369 | return ptr; |
| 3370 | } |
| 3371 | |
| 3372 | if (block.isComptime() or small.is_comptime) { |
| 3373 | const iac_index: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len)); |
| 3374 | try sema.air_instructions.append(gpa, .{ |
| 3375 | .tag = .inferred_alloc_comptime, |
| 3376 | .data = .{ .inferred_alloc_comptime = .{ |
| 3377 | .alignment = alignment, |
| 3378 | .is_const = small.is_const, |
| 3379 | .ptr = undefined, |
| 3380 | } }, |
| 3381 | }); |
| 3382 | return iac_index.toRef(); |
| 3383 | } |
| 3384 | |
| 3385 | const result_index = try block.addInstAsIndex(.{ |
| 3386 | .tag = .inferred_alloc, |
| 3387 | .data = .{ .inferred_alloc = .{ |
| 3388 | .alignment = alignment, |
| 3389 | .is_const = small.is_const, |
| 3390 | } }, |
| 3391 | }); |
| 3392 | try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{}); |
| 3393 | if (small.is_const) { |
| 3394 | try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index }); |
| 3395 | try sema.base_allocs.put(gpa, result_index, result_index); |
| 3396 | } |
| 3397 | return result_index.toRef(); |
| 3398 | } |
| 3399 | |
| 3400 | fn zirAllocComptime(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 3401 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 3402 | const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); |
| 3403 | const var_src = block.nodeOffset(inst_data.src_node); |
| 3404 | const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); |
| 3405 | try sema.ensureLayoutResolved(var_ty, var_src, .variable); |
| 3406 | return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none); |
| 3407 | } |
| 3408 | |
| 3409 | fn zirMakePtrConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 3410 | const pt = sema.pt; |
| 3411 | const zcu = pt.zcu; |
| 3412 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 3413 | const alloc = sema.resolveInst(inst_data.operand); |
| 3414 | const alloc_ty = sema.typeOf(alloc); |
| 3415 | const ptr_info = alloc_ty.ptrInfo(zcu); |
| 3416 | const elem_ty: Type = .fromInterned(ptr_info.child); |
| 3417 | |
| 3418 | // If the alloc was created in a comptime scope, we already created a comptime alloc for it. |
| 3419 | // However, if the final constructed value does not reference comptime-mutable memory, we wish |
| 3420 | // to promote it to an anon decl. |
| 3421 | already_ct: { |
| 3422 | const ptr_val = sema.resolveValue(alloc) orelse break :already_ct; |
| 3423 | |
| 3424 | // If this was a comptime inferred alloc, then `storeToInferredAllocComptime` |
| 3425 | // might have already done our job and created an anon decl ref. |
| 3426 | switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) { |
| 3427 | .ptr => |ptr| switch (ptr.base_addr) { |
| 3428 | .uav => { |
| 3429 | // The comptime-ification was already done for us. |
| 3430 | // Just make sure the pointer is const. |
| 3431 | return sema.makePtrConst(block, alloc); |
| 3432 | }, |
| 3433 | else => {}, |
| 3434 | }, |
| 3435 | else => {}, |
| 3436 | } |
| 3437 | |
| 3438 | if (!sema.isComptimeMutablePtr(ptr_val)) break :already_ct; |
| 3439 | const ptr = zcu.intern_pool.indexToKey(ptr_val.toIntern()).ptr; |
| 3440 | assert(ptr.byte_offset == 0); |
| 3441 | const alloc_index = ptr.base_addr.comptime_alloc; |
| 3442 | const ct_alloc = sema.getComptimeAlloc(alloc_index); |
| 3443 | const interned = try ct_alloc.val.intern(pt, sema.arena); |
| 3444 | if (interned.canMutateComptimeVarState(zcu)) { |
| 3445 | // Preserve the comptime alloc, just make the pointer const. |
| 3446 | ct_alloc.val = .{ .interned = interned.toIntern() }; |
| 3447 | ct_alloc.is_const = true; |
| 3448 | return sema.makePtrConst(block, alloc); |
| 3449 | } else { |
| 3450 | // Promote the constant to an anon decl. |
| 3451 | const new_mut_ptr = Air.internedToRef(try pt.intern(.{ .ptr = .{ |
| 3452 | .ty = alloc_ty.toIntern(), |
| 3453 | .base_addr = .{ .uav = .{ |
| 3454 | .val = interned.toIntern(), |
| 3455 | .orig_ty = alloc_ty.toIntern(), |
| 3456 | } }, |
| 3457 | .byte_offset = 0, |
| 3458 | } })); |
| 3459 | return sema.makePtrConst(block, new_mut_ptr); |
| 3460 | } |
| 3461 | } |
| 3462 | |
| 3463 | // Otherwise, check if the alloc is comptime-known despite being in a runtime scope. |
| 3464 | if (try sema.resolveComptimeKnownAllocPtr(block, alloc, null)) |ptr_val| { |
| 3465 | return sema.makePtrConst(block, Air.internedToRef(ptr_val)); |
| 3466 | } |
| 3467 | |
| 3468 | if (elem_ty.comptimeOnly(zcu)) { |
| 3469 | // The value was initialized through RLS, so we didn't detect the runtime condition earlier. |
| 3470 | // TODO: source location of runtime control flow |
| 3471 | const init_src = block.src(.{ .node_offset_var_decl_init = inst_data.src_node }); |
| 3472 | return sema.fail(block, init_src, "value with comptime-only type '{f}' depends on runtime control flow", .{elem_ty.fmt(pt)}); |
| 3473 | } |
| 3474 | |
| 3475 | // This is a runtime value. |
| 3476 | return sema.makePtrConst(block, alloc); |
| 3477 | } |
| 3478 | |
| 3479 | /// If `alloc` is an inferred allocation, `resolved_inferred_ty` is taken to be its resolved |
| 3480 | /// type. Otherwise, it may be `null`, and the type will be inferred from `alloc`. |
| 3481 | fn resolveComptimeKnownAllocPtr(sema: *Sema, block: *Block, alloc: Air.Inst.Ref, resolved_alloc_ty: ?Type) CompileError!?InternPool.Index { |
| 3482 | const pt = sema.pt; |
| 3483 | const zcu = pt.zcu; |
| 3484 | const comp = zcu.comp; |
| 3485 | const gpa = comp.gpa; |
| 3486 | const io = comp.io; |
| 3487 | |
| 3488 | const alloc_ty = resolved_alloc_ty orelse sema.typeOf(alloc); |
| 3489 | const ptr_info = alloc_ty.ptrInfo(zcu); |
| 3490 | const elem_ty: Type = .fromInterned(ptr_info.child); |
| 3491 | elem_ty.assertHasLayout(zcu); |
| 3492 | |
| 3493 | const alloc_inst = alloc.toIndex() orelse return null; |
| 3494 | const comptime_info = sema.maybe_comptime_allocs.fetchRemove(alloc_inst) orelse return null; |
| 3495 | const stores = comptime_info.value.stores.items(.inst); |
| 3496 | |
| 3497 | // If the elem type is OPV, no need to faff about with `stores`; just use the OPV. |
| 3498 | if (try elem_ty.onePossibleValue(pt)) |opv| { |
| 3499 | return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, opv.toIntern(), null, alloc_inst, comptime_info.value); |
| 3500 | } |
| 3501 | |
| 3502 | // Since the elem type isn't OPV, there should have been at least one store. |
| 3503 | assert(stores.len > 0); |
| 3504 | |
| 3505 | // Since the entry existed in `maybe_comptime_allocs`, the allocation is comptime-known. |
| 3506 | // We will resolve and return its value. |
| 3507 | |
| 3508 | // In general, we want to create a comptime alloc of the correct type and |
| 3509 | // apply the stores to that alloc in order. However, before going to all |
| 3510 | // that effort, let's optimize for the common case of a single store. |
| 3511 | |
| 3512 | simple: { |
| 3513 | if (stores.len != 1) break :simple; |
| 3514 | const store_inst = sema.air_instructions.get(@backingInt(stores[0])); |
| 3515 | switch (store_inst.tag) { |
| 3516 | .store, .store_safe => {}, |
| 3517 | .set_union_tag, .optional_payload_ptr_set, .errunion_payload_ptr_set => break :simple, // there's OPV stuff going on! |
| 3518 | else => unreachable, |
| 3519 | } |
| 3520 | if (store_inst.data.bin_op.lhs != alloc) break :simple; |
| 3521 | |
| 3522 | const val = store_inst.data.bin_op.rhs.toInterned().?; |
| 3523 | assert(zcu.intern_pool.typeOf(val) == elem_ty.toIntern()); |
| 3524 | return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, val, null, alloc_inst, comptime_info.value); |
| 3525 | } |
| 3526 | |
| 3527 | // The simple strategy failed: we must create a mutable comptime alloc and |
| 3528 | // perform all of the runtime store operations at comptime. |
| 3529 | |
| 3530 | const ct_alloc = try sema.newComptimeAlloc(block, .unneeded, elem_ty, ptr_info.flags.alignment); |
| 3531 | |
| 3532 | const alloc_ptr = try pt.intern(.{ .ptr = .{ |
| 3533 | .ty = alloc_ty.toIntern(), |
| 3534 | .base_addr = .{ .comptime_alloc = ct_alloc }, |
| 3535 | .byte_offset = 0, |
| 3536 | } }); |
| 3537 | |
| 3538 | // Maps from pointers into the runtime allocs, to comptime-mutable pointers into the comptime alloc |
| 3539 | var ptr_mapping = std.AutoHashMap(Air.Inst.Index, InternPool.Index).init(sema.arena); |
| 3540 | try ptr_mapping.ensureTotalCapacity(@intCast(stores.len)); |
| 3541 | ptr_mapping.putAssumeCapacity(alloc_inst, alloc_ptr); |
| 3542 | |
| 3543 | // Whilst constructing our mapping, we will also initialize optional and error union payloads when |
| 3544 | // we encounter the corresponding pointers. For this reason, the ordering of `to_map` matters. |
| 3545 | var to_map = try std.array_list.Managed(Air.Inst.Index).initCapacity(sema.arena, stores.len); |
| 3546 | |
| 3547 | for (stores) |store_inst_idx| { |
| 3548 | const store_inst = sema.air_instructions.get(@backingInt(store_inst_idx)); |
| 3549 | const ptr_to_map = switch (store_inst.tag) { |
| 3550 | .store, .store_safe => store_inst.data.bin_op.lhs.toIndex().?, // Map the pointer being stored to. |
| 3551 | .set_union_tag => store_inst.data.bin_op.lhs.toIndex().?, // Map the union pointer. |
| 3552 | .optional_payload_ptr_set, .errunion_payload_ptr_set => store_inst_idx, // Map the generated pointer itself. |
| 3553 | else => unreachable, |
| 3554 | }; |
| 3555 | to_map.appendAssumeCapacity(ptr_to_map); |
| 3556 | } |
| 3557 | |
| 3558 | const tmp_air = sema.getTmpAir(); |
| 3559 | |
| 3560 | while (to_map.pop()) |air_ptr| { |
| 3561 | if (ptr_mapping.contains(air_ptr)) continue; |
| 3562 | const PointerMethod = union(enum) { |
| 3563 | same_addr, |
| 3564 | opt_payload, |
| 3565 | eu_payload, |
| 3566 | field: u32, |
| 3567 | elem: u64, |
| 3568 | }; |
| 3569 | const inst_tag = tmp_air.instructions.items(.tag)[@backingInt(air_ptr)]; |
| 3570 | const air_parent_ptr: Air.Inst.Ref, const method: PointerMethod = switch (inst_tag) { |
| 3571 | .struct_field_ptr => blk: { |
| 3572 | const data = tmp_air.extraData( |
| 3573 | Air.StructField, |
| 3574 | tmp_air.instructions.items(.data)[@backingInt(air_ptr)].ty_pl.payload, |
| 3575 | ).data; |
| 3576 | break :blk .{ |
| 3577 | data.struct_operand, |
| 3578 | .{ .field = data.field_index }, |
| 3579 | }; |
| 3580 | }, |
| 3581 | .struct_field_ptr_index_0, |
| 3582 | .struct_field_ptr_index_1, |
| 3583 | .struct_field_ptr_index_2, |
| 3584 | .struct_field_ptr_index_3, |
| 3585 | => .{ |
| 3586 | tmp_air.instructions.items(.data)[@backingInt(air_ptr)].ty_op.operand, |
| 3587 | .{ .field = switch (inst_tag) { |
| 3588 | .struct_field_ptr_index_0 => 0, |
| 3589 | .struct_field_ptr_index_1 => 1, |
| 3590 | .struct_field_ptr_index_2 => 2, |
| 3591 | .struct_field_ptr_index_3 => 3, |
| 3592 | else => unreachable, |
| 3593 | } }, |
| 3594 | }, |
| 3595 | .ptr_slice_ptr_ptr => .{ |
| 3596 | tmp_air.instructions.items(.data)[@backingInt(air_ptr)].ty_op.operand, |
| 3597 | .{ .field = Value.slice_ptr_index }, |
| 3598 | }, |
| 3599 | .ptr_slice_len_ptr => .{ |
| 3600 | tmp_air.instructions.items(.data)[@backingInt(air_ptr)].ty_op.operand, |
| 3601 | .{ .field = Value.slice_len_index }, |
| 3602 | }, |
| 3603 | .ptr_elem_ptr => blk: { |
| 3604 | const data = tmp_air.extraData( |
| 3605 | Air.Bin, |
| 3606 | tmp_air.instructions.items(.data)[@backingInt(air_ptr)].ty_pl.payload, |
| 3607 | ).data; |
| 3608 | const idx_val = sema.resolveValue(data.rhs).?; |
| 3609 | break :blk .{ |
| 3610 | data.lhs, |
| 3611 | .{ .elem = idx_val.toUnsignedInt(zcu) }, |
| 3612 | }; |
| 3613 | }, |
| 3614 | .ptr_cast => .{ |
| 3615 | tmp_air.instructions.items(.data)[@backingInt(air_ptr)].ty_op.operand, |
| 3616 | .same_addr, |
| 3617 | }, |
| 3618 | .optional_payload_ptr_set => .{ |
| 3619 | tmp_air.instructions.items(.data)[@backingInt(air_ptr)].ty_op.operand, |
| 3620 | .opt_payload, |
| 3621 | }, |
| 3622 | .errunion_payload_ptr_set => .{ |
| 3623 | tmp_air.instructions.items(.data)[@backingInt(air_ptr)].ty_op.operand, |
| 3624 | .eu_payload, |
| 3625 | }, |
| 3626 | else => unreachable, |
| 3627 | }; |
| 3628 | |
| 3629 | const decl_parent_ptr = ptr_mapping.get(air_parent_ptr.toIndex().?) orelse { |
| 3630 | // Resolve the parent pointer first. |
| 3631 | // Note that we add in what seems like the wrong order, because we're popping from the end of this array. |
| 3632 | try to_map.appendSlice(&.{ air_ptr, air_parent_ptr.toIndex().? }); |
| 3633 | continue; |
| 3634 | }; |
| 3635 | const new_ptr_ty = tmp_air.typeOfIndex(air_ptr, &zcu.intern_pool).toIntern(); |
| 3636 | const new_ptr = switch (method) { |
| 3637 | .same_addr => try zcu.intern_pool.getCoerced(gpa, io, pt.tid, decl_parent_ptr, new_ptr_ty), |
| 3638 | .opt_payload => ptr: { |
| 3639 | // Set the optional to non-null at comptime. |
| 3640 | // If the payload is OPV, we must use that value instead of undef. |
| 3641 | const opt_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu); |
| 3642 | const payload_ty = opt_ty.optionalChild(zcu); |
| 3643 | const payload_val = try payload_ty.onePossibleValue(pt) orelse try pt.undefValue(payload_ty); |
| 3644 | const opt_val = try pt.intern(.{ .opt = .{ |
| 3645 | .ty = opt_ty.toIntern(), |
| 3646 | .val = payload_val.toIntern(), |
| 3647 | } }); |
| 3648 | try sema.storePtrVal(block, LazySrcLoc.unneeded, Value.fromInterned(decl_parent_ptr), Value.fromInterned(opt_val), opt_ty); |
| 3649 | break :ptr (try Value.fromInterned(decl_parent_ptr).ptrOptPayload(pt)).toIntern(); |
| 3650 | }, |
| 3651 | .eu_payload => ptr: { |
| 3652 | // Set the error union to non-error at comptime. |
| 3653 | // If the payload is OPV, we must use that value instead of undef. |
| 3654 | const eu_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu); |
| 3655 | const payload_ty = eu_ty.errorUnionPayload(zcu); |
| 3656 | const payload_val = try payload_ty.onePossibleValue(pt) orelse try pt.undefValue(payload_ty); |
| 3657 | const eu_val = try pt.intern(.{ .error_union = .{ |
| 3658 | .ty = eu_ty.toIntern(), |
| 3659 | .val = .{ .payload = payload_val.toIntern() }, |
| 3660 | } }); |
| 3661 | try sema.storePtrVal(block, LazySrcLoc.unneeded, Value.fromInterned(decl_parent_ptr), Value.fromInterned(eu_val), eu_ty); |
| 3662 | break :ptr (try Value.fromInterned(decl_parent_ptr).ptrEuPayload(pt)).toIntern(); |
| 3663 | }, |
| 3664 | .field => |idx| ptr: { |
| 3665 | const maybe_union_ty = Value.fromInterned(decl_parent_ptr).typeOf(zcu).childType(zcu); |
| 3666 | if (zcu.typeToUnion(maybe_union_ty)) |union_obj| if (union_obj.layout == .auto) { |
| 3667 | // As this is a union field, we must store to the pointer now to set the tag. |
| 3668 | // The payload value will be stored later, so undef is a sufficent payload for now. |
| 3669 | const payload_ty: Type = .fromInterned(union_obj.field_types.get(&zcu.intern_pool)[idx]); |
| 3670 | const payload_val = try pt.undefValue(payload_ty); |
| 3671 | const tag_val = try pt.enumValueFieldIndex(.fromInterned(union_obj.enum_tag_type), idx); |
| 3672 | const store_val = try pt.unionValue(maybe_union_ty, tag_val, payload_val); |
| 3673 | try sema.storePtrVal(block, .unneeded, .fromInterned(decl_parent_ptr), store_val, maybe_union_ty); |
| 3674 | }; |
| 3675 | break :ptr (try Value.fromInterned(decl_parent_ptr).ptrField(idx, pt)).toIntern(); |
| 3676 | }, |
| 3677 | .elem => |idx| ptr: { |
| 3678 | const parent_ptr_val: Value = .fromInterned(decl_parent_ptr); |
| 3679 | if (parent_ptr_val.typeOf(zcu).childType(zcu).zigTypeTag(zcu) == .vector) { |
| 3680 | const elem_ptr_ty: Type = .fromInterned(new_ptr_ty); |
| 3681 | // Vectors are a bit weird; see logic in `elemPtrVector`. |
| 3682 | if (elem_ptr_ty.ptrInfo(zcu).flags.vector_index != .none) { |
| 3683 | break :ptr (try pt.getCoerced(parent_ptr_val, elem_ptr_ty)).toIntern(); |
| 3684 | } else { |
| 3685 | const bit_offset = idx * @divExact(elem_ptr_ty.childType(zcu).bitSize(zcu), 8); |
| 3686 | break :ptr (try parent_ptr_val.getOffsetPtr(bit_offset, elem_ptr_ty, pt)).toIntern(); |
| 3687 | } |
| 3688 | } |
| 3689 | break :ptr (try parent_ptr_val.ptrElem(idx, pt)).toIntern(); |
| 3690 | }, |
| 3691 | }; |
| 3692 | try ptr_mapping.put(air_ptr, new_ptr); |
| 3693 | } |
| 3694 | |
| 3695 | // We have a correlation between AIR pointers and decl pointers. Perform all stores at comptime. |
| 3696 | // Any implicit stores performed by `optional_payload_ptr_set` or `errunion_payload_ptr_set` |
| 3697 | // instructions were already done above. |
| 3698 | |
| 3699 | for (stores) |store_inst_idx| { |
| 3700 | const store_inst = sema.air_instructions.get(@backingInt(store_inst_idx)); |
| 3701 | switch (store_inst.tag) { |
| 3702 | .optional_payload_ptr_set, .errunion_payload_ptr_set => {}, // Handled explicitly above |
| 3703 | .set_union_tag => { |
| 3704 | // Usually, we can ignore these, because the creation of the field pointer above |
| 3705 | // already did it for us. However, if the field is OPV, this is relevant, because |
| 3706 | // there is not going to be a store to the field. So we must initialize the union |
| 3707 | // tag if the field is OPV. |
| 3708 | const union_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?; |
| 3709 | const union_ptr_val: Value = .fromInterned(ptr_mapping.get(union_ptr_inst).?); |
| 3710 | const tag_val: Value = .fromInterned(store_inst.data.bin_op.rhs.toInterned().?); |
| 3711 | const union_ty = union_ptr_val.typeOf(zcu).childType(zcu); |
| 3712 | const field_ty = union_ty.unionFieldType(tag_val, zcu).?; |
| 3713 | if (try field_ty.onePossibleValue(pt)) |payload_val| { |
| 3714 | const new_union_val = try pt.unionValue(union_ty, tag_val, payload_val); |
| 3715 | try sema.storePtrVal(block, .unneeded, union_ptr_val, new_union_val, union_ty); |
| 3716 | } |
| 3717 | }, |
| 3718 | .store, .store_safe => { |
| 3719 | const air_ptr_inst = store_inst.data.bin_op.lhs.toIndex().?; |
| 3720 | const store_val = sema.resolveValue(store_inst.data.bin_op.rhs).?; |
| 3721 | const new_ptr = ptr_mapping.get(air_ptr_inst).?; |
| 3722 | try sema.storePtrVal(block, .unneeded, .fromInterned(new_ptr), store_val, store_val.typeOf(zcu)); |
| 3723 | }, |
| 3724 | else => unreachable, |
| 3725 | } |
| 3726 | } |
| 3727 | |
| 3728 | // The value is finalized - load it! |
| 3729 | const val = (try sema.pointerDeref(block, LazySrcLoc.unneeded, Value.fromInterned(alloc_ptr), alloc_ty)).?.toIntern(); |
| 3730 | return sema.finishResolveComptimeKnownAllocPtr(block, alloc_ty, val, ct_alloc, alloc_inst, comptime_info.value); |
| 3731 | } |
| 3732 | |
| 3733 | /// Given the resolved comptime-known value, rewrites the dead AIR to not |
| 3734 | /// create a runtime stack allocation. Also places the resulting value into |
| 3735 | /// either an anon decl ref or a comptime alloc depending on whether it |
| 3736 | /// references comptime-mutable memory. If `existing_comptime_alloc` is |
| 3737 | /// passed, it is a scratch allocation which already contains `result_val`. |
| 3738 | /// Same return type as `resolveComptimeKnownAllocPtr` so we can tail call. |
| 3739 | fn finishResolveComptimeKnownAllocPtr( |
| 3740 | sema: *Sema, |
| 3741 | block: *Block, |
| 3742 | alloc_ty: Type, |
| 3743 | result_val: InternPool.Index, |
| 3744 | existing_comptime_alloc: ?ComptimeAllocIndex, |
| 3745 | alloc_inst: Air.Inst.Index, |
| 3746 | comptime_info: MaybeComptimeAlloc, |
| 3747 | ) CompileError!?InternPool.Index { |
| 3748 | const pt = sema.pt; |
| 3749 | const zcu = pt.zcu; |
| 3750 | |
| 3751 | // We're almost done - we have the resolved comptime value. We just need to |
| 3752 | // eliminate the now-dead runtime instructions. |
| 3753 | |
| 3754 | // This instruction has type `alloc_ty`, meaning we can rewrite the `alloc` AIR instruction to |
| 3755 | // this one to drop the side effect. We also need to rewrite the stores; we'll turn them to this |
| 3756 | // too because it doesn't really matter what they become. |
| 3757 | const nop_inst: Air.Inst = .{ .tag = .ptr_from_int, .data = .{ .ty_op = .{ |
| 3758 | .ty = alloc_ty, |
| 3759 | .operand = .zero_usize, |
| 3760 | } } }; |
| 3761 | |
| 3762 | sema.air_instructions.set(@backingInt(alloc_inst), nop_inst); |
| 3763 | for (comptime_info.stores.items(.inst)) |store_inst| { |
| 3764 | sema.air_instructions.set(@backingInt(store_inst), nop_inst); |
| 3765 | } |
| 3766 | |
| 3767 | if (Value.fromInterned(result_val).canMutateComptimeVarState(zcu)) { |
| 3768 | const alloc_index = existing_comptime_alloc orelse a: { |
| 3769 | const idx = try sema.newComptimeAlloc(block, .unneeded, alloc_ty.childType(zcu), alloc_ty.ptrAlignment(zcu)); |
| 3770 | const alloc = sema.getComptimeAlloc(idx); |
| 3771 | alloc.val = .{ .interned = result_val }; |
| 3772 | break :a idx; |
| 3773 | }; |
| 3774 | sema.getComptimeAlloc(alloc_index).is_const = true; |
| 3775 | return try pt.intern(.{ .ptr = .{ |
| 3776 | .ty = alloc_ty.toIntern(), |
| 3777 | .base_addr = .{ .comptime_alloc = alloc_index }, |
| 3778 | .byte_offset = 0, |
| 3779 | } }); |
| 3780 | } else { |
| 3781 | return try pt.intern(.{ .ptr = .{ |
| 3782 | .ty = alloc_ty.toIntern(), |
| 3783 | .base_addr = .{ .uav = .{ |
| 3784 | .orig_ty = alloc_ty.toIntern(), |
| 3785 | .val = result_val, |
| 3786 | } }, |
| 3787 | .byte_offset = 0, |
| 3788 | } }); |
| 3789 | } |
| 3790 | } |
| 3791 | |
| 3792 | fn makePtrTyConst(sema: *Sema, ptr_ty: Type) CompileError!Type { |
| 3793 | var ptr_info = ptr_ty.ptrInfo(sema.pt.zcu); |
| 3794 | ptr_info.flags.is_const = true; |
| 3795 | return sema.pt.ptrType(ptr_info); |
| 3796 | } |
| 3797 | |
| 3798 | fn makePtrConst(sema: *Sema, block: *Block, alloc: Air.Inst.Ref) CompileError!Air.Inst.Ref { |
| 3799 | const alloc_ty = sema.typeOf(alloc); |
| 3800 | const const_ptr_ty = try sema.makePtrTyConst(alloc_ty); |
| 3801 | |
| 3802 | // Detect if a comptime value simply needs to have its type changed. |
| 3803 | if (sema.resolveValue(alloc)) |val| { |
| 3804 | return Air.internedToRef((try sema.pt.getCoerced(val, const_ptr_ty)).toIntern()); |
| 3805 | } |
| 3806 | |
| 3807 | return block.addTyOp(.ptr_cast, const_ptr_ty, alloc); |
| 3808 | } |
| 3809 | |
| 3810 | fn zirAllocInferredComptime( |
| 3811 | sema: *Sema, |
| 3812 | is_const: bool, |
| 3813 | ) CompileError!Air.Inst.Ref { |
| 3814 | const gpa = sema.gpa; |
| 3815 | |
| 3816 | try sema.air_instructions.append(gpa, .{ |
| 3817 | .tag = .inferred_alloc_comptime, |
| 3818 | .data = .{ .inferred_alloc_comptime = .{ |
| 3819 | .alignment = .none, |
| 3820 | .is_const = is_const, |
| 3821 | .ptr = undefined, |
| 3822 | } }, |
| 3823 | }); |
| 3824 | return @as(Air.Inst.Index, @fromBackingInt(@intCast(sema.air_instructions.len - 1))).toRef(); |
| 3825 | } |
| 3826 | |
| 3827 | fn zirAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 3828 | const pt = sema.pt; |
| 3829 | const zcu = pt.zcu; |
| 3830 | |
| 3831 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 3832 | const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); |
| 3833 | const var_src = block.nodeOffset(inst_data.src_node); |
| 3834 | |
| 3835 | const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); |
| 3836 | try sema.ensureLayoutResolved(var_ty, var_src, .constant); |
| 3837 | if (block.isComptime() or var_ty.comptimeOnly(zcu)) { |
| 3838 | return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none); |
| 3839 | } |
| 3840 | if (sema.func_is_naked and var_ty.hasRuntimeBits(zcu)) { |
| 3841 | const mut_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node }); |
| 3842 | return sema.fail(block, mut_src, "local variable in naked function", .{}); |
| 3843 | } |
| 3844 | const target = zcu.getTarget(); |
| 3845 | const ptr_type = try pt.ptrType(.{ |
| 3846 | .child = var_ty.toIntern(), |
| 3847 | .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, |
| 3848 | }); |
| 3849 | const ptr = try block.addTy(.alloc, ptr_type); |
| 3850 | const ptr_inst = ptr.toIndex().?; |
| 3851 | try sema.maybe_comptime_allocs.put(sema.gpa, ptr_inst, .{ .runtime_index = block.runtime_index }); |
| 3852 | try sema.base_allocs.put(sema.gpa, ptr_inst, ptr_inst); |
| 3853 | return ptr; |
| 3854 | } |
| 3855 | |
| 3856 | fn zirAllocMut(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 3857 | const pt = sema.pt; |
| 3858 | const zcu = pt.zcu; |
| 3859 | |
| 3860 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 3861 | const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); |
| 3862 | const var_src = block.nodeOffset(inst_data.src_node); |
| 3863 | |
| 3864 | const var_ty = try sema.resolveType(block, ty_src, inst_data.operand); |
| 3865 | try sema.ensureLayoutResolved(var_ty, var_src, .variable); |
| 3866 | if (block.isComptime()) { |
| 3867 | return sema.analyzeComptimeAlloc(block, var_src, var_ty, .none); |
| 3868 | } |
| 3869 | if (sema.func_is_naked and var_ty.hasRuntimeBits(zcu)) { |
| 3870 | const store_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node }); |
| 3871 | return sema.fail(block, store_src, "local variable in naked function", .{}); |
| 3872 | } |
| 3873 | try sema.validateVarType(block, ty_src, var_ty, false); |
| 3874 | const target = zcu.getTarget(); |
| 3875 | const ptr_type = try pt.ptrType(.{ |
| 3876 | .child = var_ty.toIntern(), |
| 3877 | .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, |
| 3878 | }); |
| 3879 | return block.addTy(.alloc, ptr_type); |
| 3880 | } |
| 3881 | |
| 3882 | fn zirAllocInferred( |
| 3883 | sema: *Sema, |
| 3884 | block: *Block, |
| 3885 | is_const: bool, |
| 3886 | ) CompileError!Air.Inst.Ref { |
| 3887 | const gpa = sema.gpa; |
| 3888 | |
| 3889 | if (block.isComptime()) { |
| 3890 | try sema.air_instructions.append(gpa, .{ |
| 3891 | .tag = .inferred_alloc_comptime, |
| 3892 | .data = .{ .inferred_alloc_comptime = .{ |
| 3893 | .alignment = .none, |
| 3894 | .is_const = is_const, |
| 3895 | .ptr = undefined, |
| 3896 | } }, |
| 3897 | }); |
| 3898 | return @as(Air.Inst.Index, @fromBackingInt(@intCast(sema.air_instructions.len - 1))).toRef(); |
| 3899 | } |
| 3900 | |
| 3901 | const result_index = try block.addInstAsIndex(.{ |
| 3902 | .tag = .inferred_alloc, |
| 3903 | .data = .{ .inferred_alloc = .{ |
| 3904 | .alignment = .none, |
| 3905 | .is_const = is_const, |
| 3906 | } }, |
| 3907 | }); |
| 3908 | try sema.unresolved_inferred_allocs.putNoClobber(gpa, result_index, .{}); |
| 3909 | if (is_const) { |
| 3910 | try sema.maybe_comptime_allocs.put(gpa, result_index, .{ .runtime_index = block.runtime_index }); |
| 3911 | try sema.base_allocs.put(sema.gpa, result_index, result_index); |
| 3912 | } |
| 3913 | return result_index.toRef(); |
| 3914 | } |
| 3915 | |
| 3916 | fn zirResolveInferredAlloc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 3917 | const pt = sema.pt; |
| 3918 | const zcu = pt.zcu; |
| 3919 | const gpa = sema.gpa; |
| 3920 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 3921 | const src = block.nodeOffset(inst_data.src_node); |
| 3922 | const ty_src = block.src(.{ .node_offset_var_decl_ty = inst_data.src_node }); |
| 3923 | const ptr = sema.resolveInst(inst_data.operand); |
| 3924 | const ptr_inst = ptr.toIndex().?; |
| 3925 | const target = zcu.getTarget(); |
| 3926 | |
| 3927 | switch (sema.air_instructions.items(.tag)[@backingInt(ptr_inst)]) { |
| 3928 | .inferred_alloc_comptime => { |
| 3929 | // The work was already done for us by `Sema.storeToInferredAllocComptime`. Also, since |
| 3930 | // we had a value of the exact correct type to store, the result type's layout must be |
| 3931 | // already resolved. So all we need to do here is return the pointer. |
| 3932 | const iac = sema.air_instructions.items(.data)[@backingInt(ptr_inst)].inferred_alloc_comptime; |
| 3933 | const resolved_ptr = iac.ptr; |
| 3934 | |
| 3935 | if (std.debug.runtime_safety) { |
| 3936 | // The inferred_alloc_comptime should never be referenced again |
| 3937 | sema.air_instructions.set(@backingInt(ptr_inst), .{ .tag = undefined, .data = undefined }); |
| 3938 | } |
| 3939 | |
| 3940 | const val = switch (zcu.intern_pool.indexToKey(resolved_ptr).ptr.base_addr) { |
| 3941 | .uav => |a| a.val, |
| 3942 | .comptime_alloc => |i| val: { |
| 3943 | const alloc = sema.getComptimeAlloc(i); |
| 3944 | break :val (try alloc.val.intern(pt, sema.arena)).toIntern(); |
| 3945 | }, |
| 3946 | else => unreachable, |
| 3947 | }; |
| 3948 | if (zcu.intern_pool.isFuncBody(val)) { |
| 3949 | const ty: Type = .fromInterned(zcu.intern_pool.typeOf(val)); |
| 3950 | if (ty.fnHasRuntimeBits(zcu)) { |
| 3951 | const orig_fn_index = zcu.intern_pool.unwrapCoercedFunc(val); |
| 3952 | try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_fn_index })); |
| 3953 | try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index); |
| 3954 | } |
| 3955 | } |
| 3956 | |
| 3957 | return Air.internedToRef(resolved_ptr); |
| 3958 | }, |
| 3959 | .inferred_alloc => { |
| 3960 | const ia1 = sema.air_instructions.items(.data)[@backingInt(ptr_inst)].inferred_alloc; |
| 3961 | const ia2 = sema.unresolved_inferred_allocs.fetchSwapRemove(ptr_inst).?.value; |
| 3962 | const peer_vals = try sema.arena.alloc(Air.Inst.Ref, ia2.prongs.items.len); |
| 3963 | for (peer_vals, ia2.prongs.items) |*peer_val, store_inst| { |
| 3964 | assert(sema.air_instructions.items(.tag)[@backingInt(store_inst)] == .store); |
| 3965 | const bin_op = sema.air_instructions.items(.data)[@backingInt(store_inst)].bin_op; |
| 3966 | peer_val.* = bin_op.rhs; |
| 3967 | } |
| 3968 | const final_elem_ty = try sema.resolvePeerTypes(block, ty_src, peer_vals, .none); |
| 3969 | // The layout of the peers is already resolved, so the layout of `final_elem_ty` is too. |
| 3970 | final_elem_ty.assertHasLayout(zcu); |
| 3971 | |
| 3972 | const final_ptr_ty = try pt.ptrType(.{ |
| 3973 | .child = final_elem_ty.toIntern(), |
| 3974 | .flags = .{ |
| 3975 | .alignment = ia1.alignment, |
| 3976 | .address_space = target_util.defaultAddressSpace(target, .local), |
| 3977 | }, |
| 3978 | }); |
| 3979 | |
| 3980 | if (!ia1.is_const) { |
| 3981 | try sema.validateVarType(block, ty_src, final_elem_ty, false); |
| 3982 | } else if (try sema.resolveComptimeKnownAllocPtr(block, ptr, final_ptr_ty)) |ptr_val| { |
| 3983 | const const_ptr_ty = try sema.makePtrTyConst(final_ptr_ty); |
| 3984 | const new_const_ptr = try pt.getCoerced(Value.fromInterned(ptr_val), const_ptr_ty); |
| 3985 | |
| 3986 | return Air.internedToRef(new_const_ptr.toIntern()); |
| 3987 | } |
| 3988 | |
| 3989 | if (final_elem_ty.comptimeOnly(zcu)) { |
| 3990 | // The alloc wasn't comptime-known per the above logic, so the |
| 3991 | // type cannot be comptime-only. |
| 3992 | // TODO: source location of runtime control flow |
| 3993 | return sema.fail(block, src, "value with comptime-only type '{f}' depends on runtime control flow", .{final_elem_ty.fmt(pt)}); |
| 3994 | } |
| 3995 | if (sema.func_is_naked and final_elem_ty.hasRuntimeBits(zcu)) { |
| 3996 | const mut_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node }); |
| 3997 | return sema.fail(block, mut_src, "local variable in naked function", .{}); |
| 3998 | } |
| 3999 | // Change it to a normal alloc. |
| 4000 | sema.air_instructions.set(@backingInt(ptr_inst), .{ |
| 4001 | .tag = .alloc, |
| 4002 | .data = .{ .ty = final_ptr_ty }, |
| 4003 | }); |
| 4004 | |
| 4005 | // Now we need to go back over all the store instructions, and do the logic as if |
| 4006 | // the new result ptr type was available. |
| 4007 | |
| 4008 | for (ia2.prongs.items) |placeholder_inst| { |
| 4009 | var replacement_block = block.makeSubBlock(); |
| 4010 | defer replacement_block.instructions.deinit(gpa); |
| 4011 | |
| 4012 | assert(sema.air_instructions.items(.tag)[@backingInt(placeholder_inst)] == .store); |
| 4013 | const bin_op = sema.air_instructions.items(.data)[@backingInt(placeholder_inst)].bin_op; |
| 4014 | try sema.storePtr2(&replacement_block, src, bin_op.lhs, src, bin_op.rhs, src, .store); |
| 4015 | |
| 4016 | // If only one instruction is produced then we can replace the store |
| 4017 | // placeholder instruction with this instruction; no need for an entire block. |
| 4018 | if (replacement_block.instructions.items.len == 1) { |
| 4019 | const only_inst = replacement_block.instructions.items[0]; |
| 4020 | sema.air_instructions.set(@backingInt(placeholder_inst), sema.air_instructions.get(@backingInt(only_inst))); |
| 4021 | continue; |
| 4022 | } |
| 4023 | |
| 4024 | // Here we replace the placeholder store instruction with a block |
| 4025 | // that does the actual store logic. |
| 4026 | _ = try replacement_block.addBr(placeholder_inst, .void_value); |
| 4027 | try sema.air_extra.ensureUnusedCapacity( |
| 4028 | gpa, |
| 4029 | @typeInfo(Air.Block).@"struct".field_names.len + replacement_block.instructions.items.len, |
| 4030 | ); |
| 4031 | sema.air_instructions.set(@backingInt(placeholder_inst), .{ |
| 4032 | .tag = .block, |
| 4033 | .data = .{ .ty_pl = .{ |
| 4034 | .ty = .void, |
| 4035 | .payload = sema.addExtraAssumeCapacity(Air.Block{ |
| 4036 | .body_len = @intCast(replacement_block.instructions.items.len), |
| 4037 | }), |
| 4038 | } }, |
| 4039 | }); |
| 4040 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items)); |
| 4041 | } |
| 4042 | |
| 4043 | if (ia1.is_const) { |
| 4044 | return sema.makePtrConst(block, ptr); |
| 4045 | } else { |
| 4046 | return ptr; |
| 4047 | } |
| 4048 | }, |
| 4049 | else => unreachable, |
| 4050 | } |
| 4051 | } |
| 4052 | |
| 4053 | fn zirForLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 4054 | const pt = sema.pt; |
| 4055 | const zcu = pt.zcu; |
| 4056 | const comp = zcu.comp; |
| 4057 | const gpa = comp.gpa; |
| 4058 | const io = comp.io; |
| 4059 | const ip = &zcu.intern_pool; |
| 4060 | |
| 4061 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 4062 | const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); |
| 4063 | const all_args = sema.code.refSlice(extra.end, extra.data.operands_len); |
| 4064 | const arg_pairs: []const [2]Zir.Inst.Ref = @as([*]const [2]Zir.Inst.Ref, @ptrCast(all_args))[0..@divExact(all_args.len, 2)]; |
| 4065 | const src = block.nodeOffset(inst_data.src_node); |
| 4066 | |
| 4067 | var len: Air.Inst.Ref = .none; |
| 4068 | var len_val: ?Value = null; |
| 4069 | var len_idx: u32 = undefined; |
| 4070 | var any_runtime = false; |
| 4071 | |
| 4072 | const runtime_arg_lens = try gpa.alloc(Air.Inst.Ref, arg_pairs.len); |
| 4073 | defer gpa.free(runtime_arg_lens); |
| 4074 | |
| 4075 | // First pass to look for comptime values. |
| 4076 | for (arg_pairs, 0..) |zir_arg_pair, i_usize| { |
| 4077 | const i: u32 = @intCast(i_usize); |
| 4078 | runtime_arg_lens[i] = .none; |
| 4079 | if (zir_arg_pair[0] == .none) continue; |
| 4080 | |
| 4081 | const arg_src = block.src(.{ .for_input = .{ |
| 4082 | .for_node_offset = inst_data.src_node, |
| 4083 | .input_index = i, |
| 4084 | } }); |
| 4085 | |
| 4086 | const arg_len_uncoerced = if (zir_arg_pair[1] == .none) l: { |
| 4087 | // This argument is an indexable. |
| 4088 | const object = sema.resolveInst(zir_arg_pair[0]); |
| 4089 | const object_ty = sema.typeOf(object); |
| 4090 | if (!object_ty.isIndexable(zcu)) { |
| 4091 | // Instead of using checkIndexable we customize this error. |
| 4092 | const msg = msg: { |
| 4093 | const msg = try sema.errMsg(arg_src, "type '{f}' is not indexable and not a range", .{object_ty.fmt(pt)}); |
| 4094 | errdefer msg.destroy(sema.gpa); |
| 4095 | try sema.errNote(arg_src, msg, "for loop operand must be a range, array, slice, tuple, or vector", .{}); |
| 4096 | |
| 4097 | if (object_ty.zigTypeTag(zcu) == .error_union) { |
| 4098 | try sema.errNote(arg_src, msg, "consider using 'try', 'catch', or 'if'", .{}); |
| 4099 | } |
| 4100 | |
| 4101 | break :msg msg; |
| 4102 | }; |
| 4103 | return sema.failWithOwnedErrorMsg(block, msg); |
| 4104 | } |
| 4105 | if (!object_ty.indexableHasLen(zcu)) continue; |
| 4106 | break :l try sema.fieldVal(block, arg_src, object, try ip.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls), arg_src); |
| 4107 | } else l: { |
| 4108 | // This argument is a range. |
| 4109 | const range_start = sema.resolveInst(zir_arg_pair[0]); |
| 4110 | const range_end = sema.resolveInst(zir_arg_pair[1]); |
| 4111 | if (try sema.resolveDefinedValue(block, arg_src, range_start)) |start| { |
| 4112 | if (try sema.valuesEqual(start, .zero_usize, .usize)) break :l range_end; |
| 4113 | } |
| 4114 | break :l try sema.analyzeArithmetic(block, .sub, range_end, range_start, arg_src, arg_src, arg_src, true); |
| 4115 | }; |
| 4116 | const arg_len = try sema.coerce(block, .usize, arg_len_uncoerced, arg_src); |
| 4117 | if (len == .none) { |
| 4118 | len = arg_len; |
| 4119 | len_idx = i; |
| 4120 | } |
| 4121 | if (try sema.resolveDefinedValue(block, src, arg_len)) |arg_val| { |
| 4122 | if (len_val) |v| { |
| 4123 | if (!(try sema.valuesEqual(arg_val, v, .usize))) { |
| 4124 | const msg = msg: { |
| 4125 | const msg = try sema.errMsg(src, "non-matching for loop lengths", .{}); |
| 4126 | errdefer msg.destroy(gpa); |
| 4127 | const a_src = block.src(.{ .for_input = .{ |
| 4128 | .for_node_offset = inst_data.src_node, |
| 4129 | .input_index = len_idx, |
| 4130 | } }); |
| 4131 | try sema.errNote(a_src, msg, "length {f} here", .{ |
| 4132 | v.fmtValueSema(pt, sema), |
| 4133 | }); |
| 4134 | try sema.errNote(arg_src, msg, "length {f} here", .{ |
| 4135 | arg_val.fmtValueSema(pt, sema), |
| 4136 | }); |
| 4137 | break :msg msg; |
| 4138 | }; |
| 4139 | return sema.failWithOwnedErrorMsg(block, msg); |
| 4140 | } |
| 4141 | } else { |
| 4142 | len = arg_len; |
| 4143 | len_val = arg_val; |
| 4144 | len_idx = i; |
| 4145 | } |
| 4146 | continue; |
| 4147 | } |
| 4148 | runtime_arg_lens[i] = arg_len; |
| 4149 | any_runtime = true; |
| 4150 | } |
| 4151 | |
| 4152 | if (len == .none) { |
| 4153 | const msg = msg: { |
| 4154 | const msg = try sema.errMsg(src, "unbounded for loop", .{}); |
| 4155 | errdefer msg.destroy(gpa); |
| 4156 | for (arg_pairs, 0..) |zir_arg_pair, i_usize| { |
| 4157 | const i: u32 = @intCast(i_usize); |
| 4158 | if (zir_arg_pair[0] == .none) continue; |
| 4159 | if (zir_arg_pair[1] != .none) continue; |
| 4160 | const object = sema.resolveInst(zir_arg_pair[0]); |
| 4161 | const object_ty = sema.typeOf(object); |
| 4162 | const arg_src = block.src(.{ .for_input = .{ |
| 4163 | .for_node_offset = inst_data.src_node, |
| 4164 | .input_index = i, |
| 4165 | } }); |
| 4166 | try sema.errNote(arg_src, msg, "type '{f}' has no upper bound", .{ |
| 4167 | object_ty.fmt(pt), |
| 4168 | }); |
| 4169 | } |
| 4170 | break :msg msg; |
| 4171 | }; |
| 4172 | return sema.failWithOwnedErrorMsg(block, msg); |
| 4173 | } |
| 4174 | |
| 4175 | // Now for the runtime checks. |
| 4176 | if (any_runtime and block.wantSafety()) { |
| 4177 | var ok: Air.Inst.Ref = .none; |
| 4178 | for (runtime_arg_lens, 0..) |arg_len, i| { |
| 4179 | if (arg_len == .none) continue; |
| 4180 | if (i == len_idx) continue; |
| 4181 | const eq = try block.addBinOp(.cmp_eq, len, arg_len); |
| 4182 | ok = if (ok != .none) |
| 4183 | try block.addBinOp(.bit_and, ok, eq) |
| 4184 | else |
| 4185 | eq; |
| 4186 | } |
| 4187 | if (ok != .none) |
| 4188 | try sema.addSafetyCheck(block, src, ok, .for_len_mismatch); |
| 4189 | } |
| 4190 | |
| 4191 | return len; |
| 4192 | } |
| 4193 | |
| 4194 | /// Given any single pointer, retrieve a pointer to the payload of any optional |
| 4195 | /// or error union pointed to, initializing these pointers along the way. |
| 4196 | /// Given a `*E!?T`, returns a (valid) `*T`. |
| 4197 | /// May invalidate already-stored payload data. |
| 4198 | /// Asserts that the layout of the pointer child type is already resolved. |
| 4199 | fn optEuBasePtrInit(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, src: LazySrcLoc) CompileError!Air.Inst.Ref { |
| 4200 | const pt = sema.pt; |
| 4201 | const zcu = pt.zcu; |
| 4202 | sema.typeOf(ptr).childType(zcu).assertHasLayout(zcu); |
| 4203 | var base_ptr = ptr; |
| 4204 | while (true) switch (sema.typeOf(base_ptr).childType(zcu).zigTypeTag(zcu)) { |
| 4205 | .error_union => base_ptr = try sema.analyzeErrUnionPayloadPtr(block, src, base_ptr, false, true), |
| 4206 | .optional => base_ptr = try sema.analyzeOptionalPayloadPtr(block, src, base_ptr, false, true), |
| 4207 | else => break, |
| 4208 | }; |
| 4209 | try sema.checkKnownAllocPtr(block, ptr, base_ptr); |
| 4210 | return base_ptr; |
| 4211 | } |
| 4212 | |
| 4213 | fn zirOptEuBasePtrInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 4214 | const un_node = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 4215 | const ptr = sema.resolveInst(un_node.operand); |
| 4216 | const src = block.nodeOffset(un_node.src_node); |
| 4217 | try sema.ensureLayoutResolved(sema.typeOf(ptr).childType(sema.pt.zcu), src, .init); |
| 4218 | return sema.optEuBasePtrInit(block, ptr, src); |
| 4219 | } |
| 4220 | |
| 4221 | fn zirCoercePtrElemTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 4222 | const pt = sema.pt; |
| 4223 | const zcu = pt.zcu; |
| 4224 | const pl_node = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 4225 | const src = block.nodeOffset(pl_node.src_node); |
| 4226 | const extra = sema.code.extraData(Zir.Inst.Bin, pl_node.payload_index).data; |
| 4227 | const uncoerced_val = sema.resolveInst(extra.rhs); |
| 4228 | const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, extra.lhs) orelse return uncoerced_val; |
| 4229 | const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu); |
| 4230 | assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction |
| 4231 | const elem_ty = ptr_ty.childType(zcu); |
| 4232 | switch (ptr_ty.ptrSize(zcu)) { |
| 4233 | .one => { |
| 4234 | const uncoerced_ty = sema.typeOf(uncoerced_val); |
| 4235 | if (elem_ty.zigTypeTag(zcu) == .array and elem_ty.childType(zcu).toIntern() == uncoerced_ty.toIntern()) { |
| 4236 | // We're trying to initialize a *[1]T with a reference to a T - don't perform any coercion. |
| 4237 | return uncoerced_val; |
| 4238 | } |
| 4239 | // If the destination type is anyopaque, don't coerce - the pointer will coerce instead. |
| 4240 | if (elem_ty.toIntern() == .anyopaque_type) { |
| 4241 | return uncoerced_val; |
| 4242 | } else { |
| 4243 | return sema.coerce(block, elem_ty, uncoerced_val, src); |
| 4244 | } |
| 4245 | }, |
| 4246 | .slice, .many => { |
| 4247 | // Our goal is to coerce `uncoerced_val` to an array of `elem_ty`. |
| 4248 | const val_ty = sema.typeOf(uncoerced_val); |
| 4249 | switch (val_ty.zigTypeTag(zcu)) { |
| 4250 | .array, .vector => {}, |
| 4251 | else => if (!val_ty.isTuple(zcu)) { |
| 4252 | return sema.fail(block, src, "expected array of '{f}', found '{f}'", .{ elem_ty.fmt(pt), val_ty.fmt(pt) }); |
| 4253 | }, |
| 4254 | } |
| 4255 | const want_ty = try pt.arrayType(.{ |
| 4256 | .len = val_ty.arrayLen(zcu), |
| 4257 | .child = elem_ty.toIntern(), |
| 4258 | .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none, |
| 4259 | }); |
| 4260 | return sema.coerce(block, want_ty, uncoerced_val, src); |
| 4261 | }, |
| 4262 | .c => { |
| 4263 | // There's nothing meaningful to do here, because we don't know if this is meant to be a |
| 4264 | // single-pointer or a many-pointer. |
| 4265 | return uncoerced_val; |
| 4266 | }, |
| 4267 | } |
| 4268 | } |
| 4269 | |
| 4270 | fn zirTryOperandTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_ref: bool) CompileError!Air.Inst.Ref { |
| 4271 | const pt = sema.pt; |
| 4272 | const zcu = pt.zcu; |
| 4273 | const comp = zcu.comp; |
| 4274 | const gpa = comp.gpa; |
| 4275 | const io = comp.io; |
| 4276 | |
| 4277 | const un_node = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 4278 | const src = block.nodeOffset(un_node.src_node); |
| 4279 | |
| 4280 | const operand_ty = try sema.resolveTypeOrPoison(block, src, un_node.operand) orelse return .generic_poison_type; |
| 4281 | |
| 4282 | const payload_ty = if (is_ref) ty: { |
| 4283 | if (!operand_ty.isSinglePointer(zcu)) { |
| 4284 | return .generic_poison_type; // we can't get a meaningful result type here, since it will be `*E![n]T`, and we don't know `n`. |
| 4285 | } |
| 4286 | break :ty operand_ty.childType(zcu); |
| 4287 | } else operand_ty; |
| 4288 | |
| 4289 | const err_set_ty: Type = err_set: { |
| 4290 | // There are awkward cases, like `?E`. Our strategy is to repeatedly unwrap optionals |
| 4291 | // until we hit an error union or set. |
| 4292 | var cur_ty = sema.fn_ret_ty; |
| 4293 | while (true) { |
| 4294 | switch (cur_ty.zigTypeTag(zcu)) { |
| 4295 | .error_set => break :err_set cur_ty, |
| 4296 | .error_union => break :err_set cur_ty.errorUnionSet(zcu), |
| 4297 | .optional => cur_ty = cur_ty.optionalChild(zcu), |
| 4298 | else => { |
| 4299 | // This function cannot return an error. |
| 4300 | // `try` is still valid if the error case is impossible, i.e. no error is returned. |
| 4301 | // So, the result type has an error set of `error{}`. |
| 4302 | break :err_set .fromInterned(try zcu.intern_pool.getErrorSetType(gpa, io, pt.tid, &.{})); |
| 4303 | }, |
| 4304 | } |
| 4305 | } |
| 4306 | }; |
| 4307 | |
| 4308 | const eu_ty = try pt.errorUnionType(err_set_ty, payload_ty); |
| 4309 | |
| 4310 | if (is_ref) { |
| 4311 | var ptr_info = operand_ty.ptrInfo(zcu); |
| 4312 | ptr_info.child = eu_ty.toIntern(); |
| 4313 | const eu_ptr_ty = try pt.ptrType(ptr_info); |
| 4314 | return Air.internedToRef(eu_ptr_ty.toIntern()); |
| 4315 | } else { |
| 4316 | return Air.internedToRef(eu_ty.toIntern()); |
| 4317 | } |
| 4318 | } |
| 4319 | |
| 4320 | fn zirValidateRefTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 4321 | const pt = sema.pt; |
| 4322 | const zcu = pt.zcu; |
| 4323 | const un_tok = sema.code.instructions.items(.data)[@backingInt(inst)].un_tok; |
| 4324 | const src = block.tokenOffset(un_tok.src_tok); |
| 4325 | // In case of GenericPoison, we don't actually have a type, so this will be |
| 4326 | // treated as an untyped address-of operator. |
| 4327 | const ty_operand = try sema.resolveTypeOrPoison(block, src, un_tok.operand) orelse return; |
| 4328 | if (ty_operand.optEuBaseType(zcu).zigTypeTag(zcu) != .pointer) { |
| 4329 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 4330 | const msg = try sema.errMsg(src, "expected type '{f}', found pointer", .{ty_operand.fmt(pt)}); |
| 4331 | errdefer msg.destroy(sema.gpa); |
| 4332 | try sema.errNote(src, msg, "address-of operator always returns a pointer", .{}); |
| 4333 | break :msg msg; |
| 4334 | }); |
| 4335 | } |
| 4336 | } |
| 4337 | |
| 4338 | fn zirValidateConst(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 4339 | if (!block.isComptime()) return; |
| 4340 | |
| 4341 | const un_node = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 4342 | const src = block.nodeOffset(un_node.src_node); |
| 4343 | const init_ref = sema.resolveInst(un_node.operand); |
| 4344 | if (!try sema.isComptimeKnown(init_ref)) { |
| 4345 | return sema.failWithNeededComptime(block, src, null); |
| 4346 | } |
| 4347 | } |
| 4348 | |
| 4349 | fn zirValidateArrayInitRefTy( |
| 4350 | sema: *Sema, |
| 4351 | block: *Block, |
| 4352 | inst: Zir.Inst.Index, |
| 4353 | ) CompileError!Air.Inst.Ref { |
| 4354 | const pt = sema.pt; |
| 4355 | const zcu = pt.zcu; |
| 4356 | const pl_node = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 4357 | const src = block.nodeOffset(pl_node.src_node); |
| 4358 | const extra = sema.code.extraData(Zir.Inst.ArrayInitRefTy, pl_node.payload_index).data; |
| 4359 | const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, extra.ptr_ty) orelse return .generic_poison_type; |
| 4360 | const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu); |
| 4361 | assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction |
| 4362 | switch (zcu.intern_pool.indexToKey(ptr_ty.toIntern())) { |
| 4363 | .ptr_type => |ptr_type| switch (ptr_type.flags.size) { |
| 4364 | .slice, .many => { |
| 4365 | // Use array of correct length |
| 4366 | const arr_ty = try pt.arrayType(.{ |
| 4367 | .len = extra.elem_count, |
| 4368 | .child = ptr_ty.childType(zcu).toIntern(), |
| 4369 | .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none, |
| 4370 | }); |
| 4371 | return Air.internedToRef(arr_ty.toIntern()); |
| 4372 | }, |
| 4373 | else => {}, |
| 4374 | }, |
| 4375 | else => {}, |
| 4376 | } |
| 4377 | // Otherwise, we just want the pointer child type |
| 4378 | const ret_ty = ptr_ty.childType(zcu); |
| 4379 | if (ret_ty.toIntern() == .anyopaque_type) { |
| 4380 | // The actual array type is unknown, which we represent with a generic poison. |
| 4381 | return .generic_poison_type; |
| 4382 | } |
| 4383 | const arr_ty = ret_ty.optEuBaseType(zcu); |
| 4384 | try sema.validateArrayInitTy(block, src, src, extra.elem_count, arr_ty); |
| 4385 | return Air.internedToRef(ret_ty.toIntern()); |
| 4386 | } |
| 4387 | |
| 4388 | fn zirValidateArrayInitTy( |
| 4389 | sema: *Sema, |
| 4390 | block: *Block, |
| 4391 | inst: Zir.Inst.Index, |
| 4392 | is_result_ty: bool, |
| 4393 | ) CompileError!void { |
| 4394 | const pt = sema.pt; |
| 4395 | const zcu = pt.zcu; |
| 4396 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 4397 | const src = block.nodeOffset(inst_data.src_node); |
| 4398 | const ty_src: LazySrcLoc = if (is_result_ty) src else block.src(.{ .node_offset_init_ty = inst_data.src_node }); |
| 4399 | const extra = sema.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data; |
| 4400 | // It's okay for the type to be poison: this will result in an anonymous array init. |
| 4401 | const ty = try sema.resolveTypeOrPoison(block, ty_src, extra.ty) orelse return; |
| 4402 | const arr_ty = if (is_result_ty) ty.optEuBaseType(zcu) else ty; |
| 4403 | return sema.validateArrayInitTy(block, src, ty_src, extra.init_count, arr_ty); |
| 4404 | } |
| 4405 | |
| 4406 | fn validateArrayInitTy( |
| 4407 | sema: *Sema, |
| 4408 | block: *Block, |
| 4409 | src: LazySrcLoc, |
| 4410 | ty_src: LazySrcLoc, |
| 4411 | init_count: u32, |
| 4412 | ty: Type, |
| 4413 | ) CompileError!void { |
| 4414 | const pt = sema.pt; |
| 4415 | const zcu = pt.zcu; |
| 4416 | switch (ty.zigTypeTag(zcu)) { |
| 4417 | .array => { |
| 4418 | const array_len = ty.arrayLen(zcu); |
| 4419 | if (init_count != array_len) { |
| 4420 | return sema.fail(block, src, "expected {d} array elements; found {d}", .{ |
| 4421 | array_len, init_count, |
| 4422 | }); |
| 4423 | } |
| 4424 | return; |
| 4425 | }, |
| 4426 | .vector => { |
| 4427 | const array_len = ty.arrayLen(zcu); |
| 4428 | if (init_count != array_len) { |
| 4429 | return sema.fail(block, src, "expected {d} vector elements; found {d}", .{ |
| 4430 | array_len, init_count, |
| 4431 | }); |
| 4432 | } |
| 4433 | return; |
| 4434 | }, |
| 4435 | .@"struct" => if (ty.isTuple(zcu)) { |
| 4436 | const array_len = ty.arrayLen(zcu); |
| 4437 | if (init_count > array_len) { |
| 4438 | return sema.fail(block, src, "expected at most {d} tuple fields; found {d}", .{ |
| 4439 | array_len, init_count, |
| 4440 | }); |
| 4441 | } |
| 4442 | return; |
| 4443 | }, |
| 4444 | else => {}, |
| 4445 | } |
| 4446 | return sema.failWithArrayInitNotSupported(block, ty_src, ty); |
| 4447 | } |
| 4448 | |
| 4449 | fn zirValidateStructInitTy( |
| 4450 | sema: *Sema, |
| 4451 | block: *Block, |
| 4452 | inst: Zir.Inst.Index, |
| 4453 | is_result_ty: bool, |
| 4454 | ) CompileError!void { |
| 4455 | const pt = sema.pt; |
| 4456 | const zcu = pt.zcu; |
| 4457 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 4458 | const src = block.nodeOffset(inst_data.src_node); |
| 4459 | // It's okay for the type to be poison: this will result in an anonymous struct init. |
| 4460 | const ty = try sema.resolveTypeOrPoison(block, src, inst_data.operand) orelse return; |
| 4461 | const struct_ty = if (is_result_ty) ty.optEuBaseType(zcu) else ty; |
| 4462 | |
| 4463 | switch (struct_ty.zigTypeTag(zcu)) { |
| 4464 | .@"struct", .@"union" => return, |
| 4465 | else => {}, |
| 4466 | } |
| 4467 | return sema.failWithStructInitNotSupported(block, src, struct_ty); |
| 4468 | } |
| 4469 | |
| 4470 | fn zirValidatePtrStructInit( |
| 4471 | sema: *Sema, |
| 4472 | block: *Block, |
| 4473 | inst: Zir.Inst.Index, |
| 4474 | ) CompileError!void { |
| 4475 | const pt = sema.pt; |
| 4476 | const zcu = pt.zcu; |
| 4477 | const validate_inst = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 4478 | const init_src = block.nodeOffset(validate_inst.src_node); |
| 4479 | const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index); |
| 4480 | const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len); |
| 4481 | const field_ptr_data = sema.code.instructions.items(.data)[@backingInt(instrs[0])].pl_node; |
| 4482 | const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; |
| 4483 | const object_ptr = sema.resolveInst(field_ptr_extra.lhs); |
| 4484 | const agg_ty = sema.typeOf(object_ptr).childType(zcu).optEuBaseType(zcu); |
| 4485 | switch (agg_ty.zigTypeTag(zcu)) { |
| 4486 | .@"struct" => return sema.validateStructInit( |
| 4487 | block, |
| 4488 | agg_ty, |
| 4489 | init_src, |
| 4490 | instrs, |
| 4491 | object_ptr, |
| 4492 | ), |
| 4493 | .@"union" => return sema.validateUnionInit( |
| 4494 | block, |
| 4495 | agg_ty, |
| 4496 | init_src, |
| 4497 | instrs, |
| 4498 | ), |
| 4499 | else => unreachable, |
| 4500 | } |
| 4501 | } |
| 4502 | |
| 4503 | fn validateUnionInit( |
| 4504 | sema: *Sema, |
| 4505 | block: *Block, |
| 4506 | union_ty: Type, |
| 4507 | init_src: LazySrcLoc, |
| 4508 | instrs: []const Zir.Inst.Index, |
| 4509 | ) CompileError!void { |
| 4510 | if (instrs.len == 1) { |
| 4511 | // Trvial validation done, and the union tag was already set by machinery in `unionFieldPtr`. |
| 4512 | return; |
| 4513 | } |
| 4514 | const msg = msg: { |
| 4515 | const msg = try sema.errMsg( |
| 4516 | init_src, |
| 4517 | "cannot initialize multiple union fields at once; unions can only have one active field", |
| 4518 | .{}, |
| 4519 | ); |
| 4520 | errdefer msg.destroy(sema.gpa); |
| 4521 | |
| 4522 | for (instrs[1..]) |inst| { |
| 4523 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 4524 | const inst_src = block.src(.{ .node_offset_initializer = inst_data.src_node }); |
| 4525 | try sema.errNote(inst_src, msg, "additional initializer here", .{}); |
| 4526 | } |
| 4527 | try sema.addDeclaredHereNote(msg, union_ty); |
| 4528 | break :msg msg; |
| 4529 | }; |
| 4530 | return sema.failWithOwnedErrorMsg(block, msg); |
| 4531 | } |
| 4532 | |
| 4533 | fn validateStructInit( |
| 4534 | sema: *Sema, |
| 4535 | block: *Block, |
| 4536 | struct_ty: Type, |
| 4537 | init_src: LazySrcLoc, |
| 4538 | instrs: []const Zir.Inst.Index, |
| 4539 | struct_ptr: Air.Inst.Ref, |
| 4540 | ) CompileError!void { |
| 4541 | const pt = sema.pt; |
| 4542 | const zcu = pt.zcu; |
| 4543 | const comp = zcu.comp; |
| 4544 | const gpa = comp.gpa; |
| 4545 | const io = comp.io; |
| 4546 | const ip = &zcu.intern_pool; |
| 4547 | |
| 4548 | // Tracks whether each field was explicitly initialized. |
| 4549 | const found_fields = try gpa.alloc(bool, struct_ty.structFieldCount(zcu)); |
| 4550 | defer gpa.free(found_fields); |
| 4551 | @memset(found_fields, false); |
| 4552 | |
| 4553 | for (instrs) |field_ptr| { |
| 4554 | const field_ptr_data = sema.code.instructions.items(.data)[@backingInt(field_ptr)].pl_node; |
| 4555 | const field_src = block.src(.{ .node_offset_initializer = field_ptr_data.src_node }); |
| 4556 | const field_ptr_extra = sema.code.extraData(Zir.Inst.Field, field_ptr_data.payload_index).data; |
| 4557 | const field_name = try ip.getOrPutString( |
| 4558 | gpa, |
| 4559 | io, |
| 4560 | pt.tid, |
| 4561 | sema.code.nullTerminatedString(field_ptr_extra.field_name_start), |
| 4562 | .no_embedded_nulls, |
| 4563 | ); |
| 4564 | const field_index = if (struct_ty.isTuple(zcu)) |
| 4565 | try sema.tupleFieldIndex(block, struct_ty, field_name, field_src) |
| 4566 | else |
| 4567 | try sema.structFieldIndex(block, struct_ty, field_name, field_src); |
| 4568 | assert(found_fields[field_index] == false); |
| 4569 | found_fields[field_index] = true; |
| 4570 | } |
| 4571 | |
| 4572 | // Our job is simply to deal with default field values. Specifically, any field which was not |
| 4573 | // explicitly initialized must have its default value stored to the field pointer, or, if the |
| 4574 | // field has no default value, a compile error must be emitted instead. |
| 4575 | |
| 4576 | // In the past, this code had other responsibilities, which involved some nasty AIR rewrites. However, |
| 4577 | // that work was actually all redundant: |
| 4578 | // |
| 4579 | // * If the struct value is comptime-known, field stores remain a perfectly valid way of initializing |
| 4580 | // the struct through RLS; there is no need to turn the field stores into one store. Comptime-known |
| 4581 | // consts are handled correctly either way thanks to `maybe_comptime_allocs` and friends. |
| 4582 | // |
| 4583 | // * If the struct type is comptime-only, we need to make sure all of the fields were comptime-known. |
| 4584 | // But the comptime-only type means that `struct_ptr` must be a comptime-mutable pointer, so the |
| 4585 | // field stores were to comptime-mutable pointers, so have already errored if not comptime-known. |
| 4586 | // |
| 4587 | // * If the value is runtime-known, then comptime-known fields must be validated as runtime values. |
| 4588 | // But this was already handled for every field store by the machinery in `checkComptimeKnownStore`. |
| 4589 | |
| 4590 | var root_msg: ?*Zcu.ErrorMsg = null; |
| 4591 | errdefer if (root_msg) |msg| msg.destroy(sema.gpa); |
| 4592 | |
| 4593 | for (found_fields, 0..) |explicit, i_usize| { |
| 4594 | const i: u32 = @intCast(i_usize); |
| 4595 | |
| 4596 | if (explicit) continue; |
| 4597 | if (struct_ty.structFieldIsComptime(i, zcu)) continue; |
| 4598 | |
| 4599 | if (!struct_ty.isTuple(zcu)) { |
| 4600 | try sema.ensureStructDefaultsResolved(struct_ty, init_src); |
| 4601 | } |
| 4602 | |
| 4603 | const default_val = struct_ty.structFieldDefaultValue(i, zcu) orelse { |
| 4604 | const field_name = struct_ty.structFieldName(i, zcu).unwrap() orelse { |
| 4605 | const template = "missing tuple field with index {d}"; |
| 4606 | if (root_msg) |msg| { |
| 4607 | try sema.errNote(init_src, msg, template, .{i}); |
| 4608 | } else { |
| 4609 | root_msg = try sema.errMsg(init_src, template, .{i}); |
| 4610 | } |
| 4611 | continue; |
| 4612 | }; |
| 4613 | const template = "missing struct field: {f}"; |
| 4614 | const args = .{field_name.fmt(ip)}; |
| 4615 | if (root_msg) |msg| { |
| 4616 | try sema.errNote(init_src, msg, template, args); |
| 4617 | } else { |
| 4618 | root_msg = try sema.errMsg(init_src, template, args); |
| 4619 | } |
| 4620 | continue; |
| 4621 | }; |
| 4622 | |
| 4623 | const field_src = init_src; // TODO better source location |
| 4624 | const default_field_ptr = try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(i), struct_ty); |
| 4625 | try sema.checkKnownAllocPtr(block, struct_ptr, default_field_ptr); |
| 4626 | try sema.storePtr2(block, init_src, default_field_ptr, init_src, .fromValue(default_val), field_src, .store); |
| 4627 | } |
| 4628 | |
| 4629 | if (root_msg) |msg| { |
| 4630 | try sema.addDeclaredHereNote(msg, struct_ty); |
| 4631 | root_msg = null; |
| 4632 | return sema.failWithOwnedErrorMsg(block, msg); |
| 4633 | } |
| 4634 | } |
| 4635 | |
| 4636 | fn zirValidatePtrArrayInit( |
| 4637 | sema: *Sema, |
| 4638 | block: *Block, |
| 4639 | inst: Zir.Inst.Index, |
| 4640 | ) CompileError!void { |
| 4641 | const pt = sema.pt; |
| 4642 | const zcu = pt.zcu; |
| 4643 | const validate_inst = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 4644 | const init_src = block.nodeOffset(validate_inst.src_node); |
| 4645 | const validate_extra = sema.code.extraData(Zir.Inst.Block, validate_inst.payload_index); |
| 4646 | const instrs = sema.code.bodySlice(validate_extra.end, validate_extra.data.body_len); |
| 4647 | const first_elem_ptr_data = sema.code.instructions.items(.data)[@backingInt(instrs[0])].pl_node; |
| 4648 | const elem_ptr_extra = sema.code.extraData(Zir.Inst.ElemPtrImm, first_elem_ptr_data.payload_index).data; |
| 4649 | const array_ptr = sema.resolveInst(elem_ptr_extra.ptr); |
| 4650 | const array_ty = sema.typeOf(array_ptr).childType(zcu).optEuBaseType(zcu); |
| 4651 | const array_len = array_ty.arrayLen(zcu); |
| 4652 | |
| 4653 | // Analagously to `validateStructInit`, our job is to handle default fields; either emitting AIR |
| 4654 | // to initialize them, or emitting a compile error if an unspecified field has no default. For |
| 4655 | // tuples, there are literally default field values, although they're guaranteed to be comptime |
| 4656 | // fields so we don't need to initialize them. For arrays, we may have a sentinel, which is never |
| 4657 | // specified so we always need to initialize here. For vectors, there's no such thing. |
| 4658 | |
| 4659 | switch (array_ty.zigTypeTag(zcu)) { |
| 4660 | .@"struct" => if (instrs.len != array_len) { |
| 4661 | var root_msg: ?*Zcu.ErrorMsg = null; |
| 4662 | errdefer if (root_msg) |msg| msg.destroy(sema.gpa); |
| 4663 | |
| 4664 | var i = instrs.len; |
| 4665 | while (i < array_len) : (i += 1) { |
| 4666 | if (array_ty.structFieldDefaultValue(i, zcu) == null) { |
| 4667 | const template = "missing tuple field with index {d}"; |
| 4668 | if (root_msg) |msg| { |
| 4669 | try sema.errNote(init_src, msg, template, .{i}); |
| 4670 | } else { |
| 4671 | root_msg = try sema.errMsg(init_src, template, .{i}); |
| 4672 | } |
| 4673 | continue; |
| 4674 | } |
| 4675 | } |
| 4676 | |
| 4677 | if (root_msg) |msg| { |
| 4678 | root_msg = null; |
| 4679 | return sema.failWithOwnedErrorMsg(block, msg); |
| 4680 | } |
| 4681 | }, |
| 4682 | |
| 4683 | .array => if (instrs.len != array_len) { |
| 4684 | return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{ |
| 4685 | array_len, instrs.len, |
| 4686 | }); |
| 4687 | } else if (array_ty.sentinel(zcu)) |sentinel| { |
| 4688 | const array_len_ref = try pt.intRef(.usize, array_len); |
| 4689 | const sentinel_ptr = try sema.elemPtrArray(block, init_src, init_src, array_ptr, init_src, array_len_ref, true, true); |
| 4690 | try sema.checkKnownAllocPtr(block, array_ptr, sentinel_ptr); |
| 4691 | try sema.storePtr2(block, init_src, sentinel_ptr, init_src, .fromValue(sentinel), init_src, .store); |
| 4692 | }, |
| 4693 | |
| 4694 | .vector => if (instrs.len != array_len) { |
| 4695 | return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{ |
| 4696 | array_len, instrs.len, |
| 4697 | }); |
| 4698 | }, |
| 4699 | |
| 4700 | else => unreachable, |
| 4701 | } |
| 4702 | } |
| 4703 | |
| 4704 | fn zirValidateDestructure(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 4705 | const pt = sema.pt; |
| 4706 | const zcu = pt.zcu; |
| 4707 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 4708 | const extra = sema.code.extraData(Zir.Inst.ValidateDestructure, inst_data.payload_index).data; |
| 4709 | const src = block.nodeOffset(inst_data.src_node); |
| 4710 | const destructure_src = block.nodeOffset(extra.destructure_node); |
| 4711 | const operand = sema.resolveInst(extra.operand); |
| 4712 | const operand_ty = sema.typeOf(operand); |
| 4713 | |
| 4714 | if (!operand_ty.destructurable(zcu)) { |
| 4715 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 4716 | const msg = try sema.errMsg(src, "type '{f}' cannot be destructured", .{operand_ty.fmt(pt)}); |
| 4717 | errdefer msg.destroy(sema.gpa); |
| 4718 | try sema.errNote(destructure_src, msg, "result destructured here", .{}); |
| 4719 | if (operand_ty.zigTypeTag(pt.zcu) == .error_union) { |
| 4720 | const base_op_ty = operand_ty.errorUnionPayload(zcu); |
| 4721 | if (base_op_ty.destructurable(zcu)) |
| 4722 | try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); |
| 4723 | } |
| 4724 | break :msg msg; |
| 4725 | }); |
| 4726 | } |
| 4727 | |
| 4728 | if (operand_ty.arrayLen(zcu) != extra.expect_len) { |
| 4729 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 4730 | const msg = try sema.errMsg(src, "expected {d} elements for destructure, found {d}", .{ |
| 4731 | extra.expect_len, operand_ty.arrayLen(zcu), |
| 4732 | }); |
| 4733 | errdefer msg.destroy(sema.gpa); |
| 4734 | try sema.errNote(destructure_src, msg, "result destructured here", .{}); |
| 4735 | break :msg msg; |
| 4736 | }); |
| 4737 | } |
| 4738 | } |
| 4739 | |
| 4740 | fn failWithBadMemberAccess( |
| 4741 | sema: *Sema, |
| 4742 | block: *Block, |
| 4743 | agg_ty: Type, |
| 4744 | field_src: LazySrcLoc, |
| 4745 | field_name: InternPool.NullTerminatedString, |
| 4746 | ) CompileError { |
| 4747 | const pt = sema.pt; |
| 4748 | const zcu = pt.zcu; |
| 4749 | const ip = &zcu.intern_pool; |
| 4750 | const kw_name = switch (agg_ty.zigTypeTag(zcu)) { |
| 4751 | .@"union" => "union", |
| 4752 | .@"struct" => "struct", |
| 4753 | .@"opaque" => "opaque", |
| 4754 | .@"enum" => "enum", |
| 4755 | else => unreachable, |
| 4756 | }; |
| 4757 | if (agg_ty.typeDeclInst(zcu)) |inst| { |
| 4758 | const inst_index = inst.resolve(ip) orelse return sema.failTransitive(.{ .lost_tracking = inst }); |
| 4759 | if (inst_index == .main_struct_inst) { |
| 4760 | return sema.fail(block, field_src, "root source file struct '{f}' has no member named '{f}'", .{ |
| 4761 | agg_ty.fmt(pt), field_name.fmt(ip), |
| 4762 | }); |
| 4763 | } |
| 4764 | } |
| 4765 | |
| 4766 | return sema.fail(block, field_src, "{s} '{f}' has no member named '{f}'", .{ |
| 4767 | kw_name, agg_ty.fmt(pt), field_name.fmt(ip), |
| 4768 | }); |
| 4769 | } |
| 4770 | |
| 4771 | fn failWithBadStructFieldAccess( |
| 4772 | sema: *Sema, |
| 4773 | block: *Block, |
| 4774 | struct_ty: Type, |
| 4775 | struct_type: InternPool.LoadedStructType, |
| 4776 | field_src: LazySrcLoc, |
| 4777 | field_name: InternPool.NullTerminatedString, |
| 4778 | ) CompileError { |
| 4779 | const pt = sema.pt; |
| 4780 | const zcu = pt.zcu; |
| 4781 | const ip = &zcu.intern_pool; |
| 4782 | |
| 4783 | const msg = msg: { |
| 4784 | const msg = try sema.errMsg( |
| 4785 | field_src, |
| 4786 | "no field named '{f}' in struct '{f}'", |
| 4787 | .{ field_name.fmt(ip), struct_type.fqn.fmt(ip) }, |
| 4788 | ); |
| 4789 | errdefer msg.destroy(sema.gpa); |
| 4790 | try sema.errNote(struct_ty.srcLoc(zcu), msg, "struct declared here", .{}); |
| 4791 | break :msg msg; |
| 4792 | }; |
| 4793 | return sema.failWithOwnedErrorMsg(block, msg); |
| 4794 | } |
| 4795 | |
| 4796 | fn failWithBadUnionFieldAccess( |
| 4797 | sema: *Sema, |
| 4798 | block: *Block, |
| 4799 | union_ty: Type, |
| 4800 | union_obj: InternPool.LoadedUnionType, |
| 4801 | field_src: LazySrcLoc, |
| 4802 | field_name: InternPool.NullTerminatedString, |
| 4803 | ) CompileError { |
| 4804 | const pt = sema.pt; |
| 4805 | const zcu = pt.zcu; |
| 4806 | const ip = &zcu.intern_pool; |
| 4807 | const gpa = sema.gpa; |
| 4808 | |
| 4809 | const msg = msg: { |
| 4810 | const msg = try sema.errMsg( |
| 4811 | field_src, |
| 4812 | "no field named '{f}' in union '{f}'", |
| 4813 | .{ field_name.fmt(ip), union_obj.fqn.fmt(ip) }, |
| 4814 | ); |
| 4815 | errdefer msg.destroy(gpa); |
| 4816 | try sema.errNote(union_ty.srcLoc(zcu), msg, "union declared here", .{}); |
| 4817 | break :msg msg; |
| 4818 | }; |
| 4819 | return sema.failWithOwnedErrorMsg(block, msg); |
| 4820 | } |
| 4821 | |
| 4822 | pub fn addDeclaredHereNote(sema: *Sema, parent: *Zcu.ErrorMsg, decl_ty: Type) Allocator.Error!void { |
| 4823 | const zcu = sema.pt.zcu; |
| 4824 | const src_loc = decl_ty.srcLocOrNull(zcu) orelse return; |
| 4825 | const category = switch (decl_ty.zigTypeTag(zcu)) { |
| 4826 | .@"union" => "union", |
| 4827 | .@"struct" => "struct", |
| 4828 | .@"enum" => "enum", |
| 4829 | .@"opaque" => "opaque", |
| 4830 | else => unreachable, |
| 4831 | }; |
| 4832 | try sema.errNote(src_loc, parent, "{s} declared here", .{category}); |
| 4833 | } |
| 4834 | |
| 4835 | fn zirStoreToInferredPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 4836 | const pl_node = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 4837 | const src = block.nodeOffset(pl_node.src_node); |
| 4838 | const bin = sema.code.extraData(Zir.Inst.Bin, pl_node.payload_index).data; |
| 4839 | const ptr = sema.resolveInst(bin.lhs); |
| 4840 | const operand = sema.resolveInst(bin.rhs); |
| 4841 | const ptr_inst = ptr.toIndex().?; |
| 4842 | const air_datas = sema.air_instructions.items(.data); |
| 4843 | |
| 4844 | switch (sema.air_instructions.items(.tag)[@backingInt(ptr_inst)]) { |
| 4845 | .inferred_alloc_comptime => { |
| 4846 | const iac = &air_datas[@backingInt(ptr_inst)].inferred_alloc_comptime; |
| 4847 | return sema.storeToInferredAllocComptime(block, src, operand, iac); |
| 4848 | }, |
| 4849 | .inferred_alloc => { |
| 4850 | const ia = sema.unresolved_inferred_allocs.getPtr(ptr_inst).?; |
| 4851 | return sema.storeToInferredAlloc(block, src, ptr, operand, ia); |
| 4852 | }, |
| 4853 | else => unreachable, |
| 4854 | } |
| 4855 | } |
| 4856 | |
| 4857 | fn storeToInferredAlloc( |
| 4858 | sema: *Sema, |
| 4859 | block: *Block, |
| 4860 | src: LazySrcLoc, |
| 4861 | ptr: Air.Inst.Ref, |
| 4862 | operand: Air.Inst.Ref, |
| 4863 | inferred_alloc: *InferredAlloc, |
| 4864 | ) CompileError!void { |
| 4865 | // Create a store instruction as a placeholder. This will be replaced by a |
| 4866 | // proper store sequence once we know the stored type. |
| 4867 | const dummy_store = try block.addBinOp(.store, ptr, operand); |
| 4868 | try sema.checkComptimeKnownStore(block, dummy_store, src); |
| 4869 | // Add the stored instruction to the set we will use to resolve peer types |
| 4870 | // for the inferred allocation. |
| 4871 | try inferred_alloc.prongs.append(sema.arena, dummy_store.toIndex().?); |
| 4872 | } |
| 4873 | |
| 4874 | fn storeToInferredAllocComptime( |
| 4875 | sema: *Sema, |
| 4876 | block: *Block, |
| 4877 | src: LazySrcLoc, |
| 4878 | operand: Air.Inst.Ref, |
| 4879 | iac: *Air.Inst.Data.InferredAllocComptime, |
| 4880 | ) CompileError!void { |
| 4881 | const pt = sema.pt; |
| 4882 | const zcu = pt.zcu; |
| 4883 | const operand_ty = sema.typeOf(operand); |
| 4884 | // There will be only one store_to_inferred_ptr because we are running at comptime. |
| 4885 | // The alloc will turn into a Decl or a ComptimeAlloc. |
| 4886 | const operand_val = sema.resolveValue(operand) orelse { |
| 4887 | return sema.failWithNeededComptime(block, src, .{ .simple = .stored_to_comptime_var }); |
| 4888 | }; |
| 4889 | const alloc_ty = try pt.ptrType(.{ |
| 4890 | .child = operand_ty.toIntern(), |
| 4891 | .flags = .{ |
| 4892 | .alignment = iac.alignment, |
| 4893 | .is_const = iac.is_const, |
| 4894 | }, |
| 4895 | }); |
| 4896 | if (operand_ty.classify(zcu) == .one_possible_value or |
| 4897 | (iac.is_const and !operand_val.canMutateComptimeVarState(zcu))) |
| 4898 | { |
| 4899 | iac.ptr = try pt.intern(.{ .ptr = .{ |
| 4900 | .ty = alloc_ty.toIntern(), |
| 4901 | .base_addr = .{ .uav = .{ |
| 4902 | .val = operand_val.toIntern(), |
| 4903 | .orig_ty = alloc_ty.toIntern(), |
| 4904 | } }, |
| 4905 | .byte_offset = 0, |
| 4906 | } }); |
| 4907 | } else { |
| 4908 | const alloc_index = try sema.newComptimeAlloc(block, src, operand_ty, iac.alignment); |
| 4909 | sema.getComptimeAlloc(alloc_index).val = .{ .interned = operand_val.toIntern() }; |
| 4910 | iac.ptr = try pt.intern(.{ .ptr = .{ |
| 4911 | .ty = alloc_ty.toIntern(), |
| 4912 | .base_addr = .{ .comptime_alloc = alloc_index }, |
| 4913 | .byte_offset = 0, |
| 4914 | } }); |
| 4915 | } |
| 4916 | } |
| 4917 | |
| 4918 | fn zirSetEvalBranchQuota(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 4919 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 4920 | const src = block.nodeOffset(inst_data.src_node); |
| 4921 | const quota: u32 = @intCast(try sema.resolveInt(block, src, inst_data.operand, .u32, .{ .simple = .operand_setEvalBranchQuota })); |
| 4922 | sema.branch_quota = @max(sema.branch_quota, quota); |
| 4923 | sema.quota_request = @max(sema.quota_request, quota); |
| 4924 | } |
| 4925 | |
| 4926 | fn zirStoreNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 4927 | const zir_tags = sema.code.instructions.items(.tag); |
| 4928 | const zir_datas = sema.code.instructions.items(.data); |
| 4929 | const inst_data = zir_datas[@backingInt(inst)].pl_node; |
| 4930 | const src = block.nodeOffset(inst_data.src_node); |
| 4931 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 4932 | const ptr = sema.resolveInst(extra.lhs); |
| 4933 | const operand = sema.resolveInst(extra.rhs); |
| 4934 | |
| 4935 | const is_ret = if (extra.lhs.toIndex()) |ptr_index| |
| 4936 | zir_tags[@backingInt(ptr_index)] == .ret_ptr |
| 4937 | else |
| 4938 | false; |
| 4939 | |
| 4940 | const ptr_src = block.src(.{ .node_offset_store_ptr = inst_data.src_node }); |
| 4941 | const operand_src = block.src(.{ .node_offset_store_operand = inst_data.src_node }); |
| 4942 | const air_tag: Air.Inst.Tag = if (is_ret) |
| 4943 | .ret_ptr |
| 4944 | else if (block.wantSafety()) |
| 4945 | .store_safe |
| 4946 | else |
| 4947 | .store; |
| 4948 | return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag); |
| 4949 | } |
| 4950 | |
| 4951 | fn zirStr(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 4952 | const pt = sema.pt; |
| 4953 | const zcu = pt.zcu; |
| 4954 | const comp = zcu.comp; |
| 4955 | const gpa = comp.gpa; |
| 4956 | const io = comp.io; |
| 4957 | const ip = &zcu.intern_pool; |
| 4958 | const bytes = sema.code.instructions.items(.data)[@backingInt(inst)].str.get(sema.code); |
| 4959 | return sema.addStrLit( |
| 4960 | try ip.getOrPutString(gpa, io, pt.tid, bytes, .maybe_embedded_nulls), |
| 4961 | bytes.len, |
| 4962 | ); |
| 4963 | } |
| 4964 | |
| 4965 | fn addNullTerminatedStrLit(sema: *Sema, string: InternPool.NullTerminatedString) CompileError!Air.Inst.Ref { |
| 4966 | return sema.addStrLit(string.toString(), string.length(&sema.pt.zcu.intern_pool)); |
| 4967 | } |
| 4968 | |
| 4969 | pub fn addStrLit(sema: *Sema, string: InternPool.String, len: u64) CompileError!Air.Inst.Ref { |
| 4970 | const pt = sema.pt; |
| 4971 | const array_ty = try pt.arrayType(.{ |
| 4972 | .len = len, |
| 4973 | .sentinel = .zero_u8, |
| 4974 | .child = .u8_type, |
| 4975 | }); |
| 4976 | const val = try pt.intern(.{ .aggregate = .{ |
| 4977 | .ty = array_ty.toIntern(), |
| 4978 | .storage = .{ .bytes = string }, |
| 4979 | } }); |
| 4980 | return sema.uavRef(.fromInterned(val)); |
| 4981 | } |
| 4982 | |
| 4983 | fn uavRef(sema: *Sema, val: Value) CompileError!Air.Inst.Ref { |
| 4984 | return .fromValue(try sema.pt.uavValue(val)); |
| 4985 | } |
| 4986 | |
| 4987 | fn zirInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 4988 | _ = block; |
| 4989 | |
| 4990 | const int = sema.code.instructions.items(.data)[@backingInt(inst)].int; |
| 4991 | return sema.pt.intRef(.comptime_int, int); |
| 4992 | } |
| 4993 | |
| 4994 | fn zirIntBig(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 4995 | _ = block; |
| 4996 | |
| 4997 | const int = sema.code.instructions.items(.data)[@backingInt(inst)].str; |
| 4998 | const byte_count = int.len * @sizeOf(std.math.big.Limb); |
| 4999 | const limb_bytes = sema.code.string_bytes[@backingInt(int.start)..][0..byte_count]; |
| 5000 | |
| 5001 | // TODO: this allocation and copy is only needed because the limbs may be unaligned. |
| 5002 | // If ZIR is adjusted so that big int limbs are guaranteed to be aligned, these |
| 5003 | // two lines can be removed. |
| 5004 | const limbs = try sema.arena.alloc(std.math.big.Limb, int.len); |
| 5005 | @memcpy(mem.sliceAsBytes(limbs), limb_bytes); |
| 5006 | |
| 5007 | return Air.internedToRef((try sema.pt.intValue_big(.comptime_int, .{ |
| 5008 | .limbs = limbs, |
| 5009 | .positive = true, |
| 5010 | })).toIntern()); |
| 5011 | } |
| 5012 | |
| 5013 | fn zirFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 5014 | _ = block; |
| 5015 | const number = sema.code.instructions.items(.data)[@backingInt(inst)].float; |
| 5016 | return Air.internedToRef((try sema.pt.floatValue( |
| 5017 | .comptime_float, |
| 5018 | number, |
| 5019 | )).toIntern()); |
| 5020 | } |
| 5021 | |
| 5022 | fn zirFloat128(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 5023 | _ = block; |
| 5024 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 5025 | const extra = sema.code.extraData(Zir.Inst.Float128, inst_data.payload_index).data; |
| 5026 | const number = extra.get(); |
| 5027 | return Air.internedToRef((try sema.pt.floatValue(.comptime_float, number)).toIntern()); |
| 5028 | } |
| 5029 | |
| 5030 | fn zirCompileError(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 5031 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 5032 | const src = block.nodeOffset(inst_data.src_node); |
| 5033 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 5034 | const msg = try sema.resolveConstString(block, operand_src, inst_data.operand, .{ .simple = .compile_error_string }); |
| 5035 | return sema.fail(block, src, "{s}", .{msg}); |
| 5036 | } |
| 5037 | |
| 5038 | fn zirCompileLog( |
| 5039 | sema: *Sema, |
| 5040 | block: *Block, |
| 5041 | extended: Zir.Inst.Extended.InstData, |
| 5042 | ) CompileError!Air.Inst.Ref { |
| 5043 | const pt = sema.pt; |
| 5044 | const zcu = pt.zcu; |
| 5045 | const comp = zcu.comp; |
| 5046 | const gpa = comp.gpa; |
| 5047 | const io = comp.io; |
| 5048 | |
| 5049 | var aw: std.Io.Writer.Allocating = .init(gpa); |
| 5050 | defer aw.deinit(); |
| 5051 | const writer = &aw.writer; |
| 5052 | |
| 5053 | const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); |
| 5054 | const src_node = extra.data.src_node; |
| 5055 | const args = sema.code.refSlice(extra.end, extended.small); |
| 5056 | |
| 5057 | for (args, 0..) |arg_ref, i| { |
| 5058 | if (i != 0) writer.writeAll(", ") catch return error.OutOfMemory; |
| 5059 | |
| 5060 | const arg = sema.resolveInst(arg_ref); |
| 5061 | const arg_ty = sema.typeOf(arg); |
| 5062 | if (sema.resolveValue(arg)) |val| { |
| 5063 | writer.print("@as({f}, {f})", .{ |
| 5064 | arg_ty.fmt(pt), val.fmtValueSema(pt, sema), |
| 5065 | }) catch return error.OutOfMemory; |
| 5066 | } else { |
| 5067 | writer.print("@as({f}, [runtime value])", .{arg_ty.fmt(pt)}) catch return error.OutOfMemory; |
| 5068 | } |
| 5069 | } |
| 5070 | |
| 5071 | const line_data = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, aw.written(), .no_embedded_nulls); |
| 5072 | |
| 5073 | const line_idx: Zcu.CompileLogLine.Index = if (zcu.free_compile_log_lines.pop()) |idx| idx: { |
| 5074 | zcu.compile_log_lines.items[@backingInt(idx)] = .{ |
| 5075 | .next = .none, |
| 5076 | .data = line_data, |
| 5077 | }; |
| 5078 | break :idx idx; |
| 5079 | } else idx: { |
| 5080 | try zcu.compile_log_lines.append(gpa, .{ |
| 5081 | .next = .none, |
| 5082 | .data = line_data, |
| 5083 | }); |
| 5084 | break :idx @fromBackingInt(@intCast(zcu.compile_log_lines.items.len - 1)); |
| 5085 | }; |
| 5086 | |
| 5087 | const gop = try zcu.compile_logs.getOrPut(gpa, sema.owner); |
| 5088 | if (gop.found_existing) { |
| 5089 | const prev_line = gop.value_ptr.last_line.get(zcu); |
| 5090 | assert(prev_line.next == .none); |
| 5091 | prev_line.next = line_idx.toOptional(); |
| 5092 | gop.value_ptr.last_line = line_idx; |
| 5093 | } else { |
| 5094 | gop.value_ptr.* = .{ |
| 5095 | .base_node_inst = block.src_base_inst, |
| 5096 | .node_offset = src_node, |
| 5097 | .first_line = line_idx, |
| 5098 | .last_line = line_idx, |
| 5099 | }; |
| 5100 | } |
| 5101 | return .void_value; |
| 5102 | } |
| 5103 | |
| 5104 | fn zirPanic(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 5105 | const pt = sema.pt; |
| 5106 | const zcu = pt.zcu; |
| 5107 | |
| 5108 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 5109 | const src = block.nodeOffset(inst_data.src_node); |
| 5110 | const msg_inst = sema.resolveInst(inst_data.operand); |
| 5111 | |
| 5112 | const arg_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 5113 | const coerced_msg = try sema.coerce(block, .slice_const_u8, msg_inst, arg_src); |
| 5114 | |
| 5115 | if (block.isComptime()) { |
| 5116 | const string = try sema.resolveConstString(block, arg_src, inst_data.operand, null); |
| 5117 | return sema.fail(block, src, "encountered @panic at comptime: {s}", .{string}); |
| 5118 | } |
| 5119 | |
| 5120 | // We only apply the first hint in a branch. |
| 5121 | // This allows user-provided hints to override implicit cold hints. |
| 5122 | if (sema.branch_hint == null) { |
| 5123 | sema.branch_hint = .cold; |
| 5124 | } |
| 5125 | |
| 5126 | if (!zcu.backendSupportsFeature(.panic_fn)) { |
| 5127 | _ = try block.addNoOp(.trap); |
| 5128 | return; |
| 5129 | } |
| 5130 | |
| 5131 | try sema.ensureMemoizedStateResolved(src, .panic); |
| 5132 | const panic_fn_index = zcu.std_lang_decl_values.get(.@"panic.call"); |
| 5133 | const opt_usize_ty = try pt.optionalType(.usize_type); |
| 5134 | const null_ret_addr = Air.internedToRef((try pt.intern(.{ .opt = .{ |
| 5135 | .ty = opt_usize_ty.toIntern(), |
| 5136 | .val = .none, |
| 5137 | } }))); |
| 5138 | // `callBuiltin` also calls `addReferenceEntry` to the function body for us. |
| 5139 | try sema.callBuiltin( |
| 5140 | block, |
| 5141 | src, |
| 5142 | .fromIntern(panic_fn_index), |
| 5143 | .auto, |
| 5144 | &.{ coerced_msg, null_ret_addr }, |
| 5145 | .@"@panic", |
| 5146 | ); |
| 5147 | } |
| 5148 | |
| 5149 | fn zirTrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 5150 | const src_node = sema.code.instructions.items(.data)[@backingInt(inst)].node; |
| 5151 | const src = block.nodeOffset(src_node); |
| 5152 | if (block.isComptime()) |
| 5153 | return sema.fail(block, src, "encountered @trap at comptime", .{}); |
| 5154 | _ = try block.addNoOp(.trap); |
| 5155 | } |
| 5156 | |
| 5157 | fn zirBreakpoint(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { |
| 5158 | const src_node: std.zig.Ast.Node.Offset = @fromBackingInt(@intCast(@as(i32, @bitCast(extended.operand)))); |
| 5159 | const src = block.nodeOffset(src_node); |
| 5160 | if (block.isComptime()) |
| 5161 | return sema.fail(block, src, "encountered @breakpoint at comptime", .{}); |
| 5162 | _ = try block.addNoOp(.breakpoint); |
| 5163 | } |
| 5164 | |
| 5165 | fn zirLoop(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 5166 | const pt = sema.pt; |
| 5167 | const zcu = pt.zcu; |
| 5168 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 5169 | const src = parent_block.nodeOffset(inst_data.src_node); |
| 5170 | const extra = sema.code.extraData(Zir.Inst.Block, inst_data.payload_index); |
| 5171 | const body = sema.code.bodySlice(extra.end, extra.data.body_len); |
| 5172 | const gpa = sema.gpa; |
| 5173 | |
| 5174 | // AIR expects a block outside the loop block too. |
| 5175 | // Reserve space for a Loop instruction so that generated Break instructions can |
| 5176 | // point to it, even if it doesn't end up getting used because the code ends up being |
| 5177 | // comptime evaluated. |
| 5178 | const block_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len)); |
| 5179 | const loop_inst: Air.Inst.Index = @fromBackingInt(@intCast(@backingInt(block_inst) + 1)); |
| 5180 | try sema.air_instructions.ensureUnusedCapacity(gpa, 2); |
| 5181 | sema.air_instructions.appendAssumeCapacity(.{ |
| 5182 | .tag = .block, |
| 5183 | .data = undefined, |
| 5184 | }); |
| 5185 | sema.air_instructions.appendAssumeCapacity(.{ |
| 5186 | .tag = .loop, |
| 5187 | .data = .{ .ty_pl = .{ |
| 5188 | .ty = .noreturn, |
| 5189 | .payload = undefined, |
| 5190 | } }, |
| 5191 | }); |
| 5192 | var label: Block.Label = .{ |
| 5193 | .zir_block = inst, |
| 5194 | .merges = .{ |
| 5195 | .src_locs = .empty, |
| 5196 | .results = .empty, |
| 5197 | .br_list = .empty, |
| 5198 | .block_inst = block_inst, |
| 5199 | }, |
| 5200 | }; |
| 5201 | var child_block = parent_block.makeSubBlock(); |
| 5202 | child_block.label = &label; |
| 5203 | child_block.runtime_cond = null; |
| 5204 | child_block.runtime_loop = src; |
| 5205 | child_block.runtime_index.increment(); |
| 5206 | const merges = &child_block.label.?.merges; |
| 5207 | |
| 5208 | defer child_block.instructions.deinit(gpa); |
| 5209 | defer merges.deinit(gpa); |
| 5210 | |
| 5211 | var loop_block = child_block.makeSubBlock(); |
| 5212 | defer loop_block.instructions.deinit(gpa); |
| 5213 | |
| 5214 | // Use `analyzeBodyInner` directly to push any comptime control flow up the stack. |
| 5215 | try sema.analyzeBodyInner(&loop_block, body); |
| 5216 | |
| 5217 | // TODO: since AIR has `repeat` now, we could change ZIR to generate |
| 5218 | // more optimal code utilizing `repeat` instructions across blocks! |
| 5219 | // For now, if the generated loop body does not terminate `noreturn`, |
| 5220 | // then `analyzeBodyInner` is signalling that it ended with `repeat`. |
| 5221 | |
| 5222 | const loop_block_len = loop_block.instructions.items.len; |
| 5223 | if (loop_block_len > 0 and sema.typeOf(loop_block.instructions.items[loop_block_len - 1].toRef()).isNoReturn(zcu)) { |
| 5224 | // If the loop ended with a noreturn terminator, then there is no way for it to loop, |
| 5225 | // so we can just use the block instead. |
| 5226 | try child_block.instructions.appendSlice(gpa, loop_block.instructions.items); |
| 5227 | } else { |
| 5228 | _ = try loop_block.addInst(.{ |
| 5229 | .tag = .repeat, |
| 5230 | .data = .{ .repeat = .{ |
| 5231 | .loop_inst = loop_inst, |
| 5232 | } }, |
| 5233 | }); |
| 5234 | // Note that `loop_block_len` is now off by one. |
| 5235 | |
| 5236 | try child_block.instructions.append(gpa, loop_inst); |
| 5237 | |
| 5238 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".field_names.len + loop_block_len + 1); |
| 5239 | sema.air_instructions.items(.data)[@backingInt(loop_inst)].ty_pl.payload = sema.addExtraAssumeCapacity( |
| 5240 | Air.Block{ .body_len = @intCast(loop_block_len + 1) }, |
| 5241 | ); |
| 5242 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(loop_block.instructions.items)); |
| 5243 | } |
| 5244 | return sema.resolveAnalyzedBlock(parent_block, src, &child_block, merges, false); |
| 5245 | } |
| 5246 | |
| 5247 | fn zirSuspendBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 5248 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 5249 | const src = parent_block.nodeOffset(inst_data.src_node); |
| 5250 | return sema.failWithUseOfAsync(parent_block, src); |
| 5251 | } |
| 5252 | |
| 5253 | fn zirBlock(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 5254 | const pl_node = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 5255 | const src = parent_block.nodeOffset(pl_node.src_node); |
| 5256 | const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index); |
| 5257 | const body = sema.code.bodySlice(extra.end, extra.data.body_len); |
| 5258 | const gpa = sema.gpa; |
| 5259 | |
| 5260 | // Reserve space for a Block instruction so that generated Break instructions can |
| 5261 | // point to it, even if it doesn't end up getting used because the code ends up being |
| 5262 | // comptime evaluated or is an unlabeled block. |
| 5263 | const block_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len)); |
| 5264 | try sema.air_instructions.append(gpa, .{ |
| 5265 | .tag = .block, |
| 5266 | .data = undefined, |
| 5267 | }); |
| 5268 | |
| 5269 | var label: Block.Label = .{ |
| 5270 | .zir_block = inst, |
| 5271 | .merges = .{ |
| 5272 | .src_locs = .empty, |
| 5273 | .results = .empty, |
| 5274 | .br_list = .empty, |
| 5275 | .block_inst = block_inst, |
| 5276 | }, |
| 5277 | }; |
| 5278 | |
| 5279 | var child_block: Block = .{ |
| 5280 | .parent = parent_block, |
| 5281 | .sema = sema, |
| 5282 | .namespace = parent_block.namespace, |
| 5283 | .instructions = .empty, |
| 5284 | .label = &label, |
| 5285 | .inlining = parent_block.inlining, |
| 5286 | .comptime_reason = parent_block.comptime_reason, |
| 5287 | .is_typeof = parent_block.is_typeof, |
| 5288 | .want_safety = parent_block.want_safety, |
| 5289 | .float_mode = parent_block.float_mode, |
| 5290 | .runtime_cond = parent_block.runtime_cond, |
| 5291 | .runtime_loop = parent_block.runtime_loop, |
| 5292 | .runtime_index = parent_block.runtime_index, |
| 5293 | .error_return_trace_index = parent_block.error_return_trace_index, |
| 5294 | .src_base_inst = parent_block.src_base_inst, |
| 5295 | .type_name_ctx = parent_block.type_name_ctx, |
| 5296 | .type_fqn_ctx = parent_block.type_fqn_ctx, |
| 5297 | }; |
| 5298 | |
| 5299 | defer child_block.instructions.deinit(gpa); |
| 5300 | defer label.merges.deinit(gpa); |
| 5301 | |
| 5302 | return sema.resolveBlockBody(parent_block, src, &child_block, body, inst, &label.merges); |
| 5303 | } |
| 5304 | |
| 5305 | /// Semantically analyze the given ZIR body, emitting any resulting runtime code into the AIR block |
| 5306 | /// specified by `child_block` if necessary (and emitting this block into `parent_block`). |
| 5307 | /// TODO: `merges` is known from `child_block`, remove this parameter. |
| 5308 | fn resolveBlockBody( |
| 5309 | sema: *Sema, |
| 5310 | parent_block: *Block, |
| 5311 | src: LazySrcLoc, |
| 5312 | child_block: *Block, |
| 5313 | body: []const Zir.Inst.Index, |
| 5314 | /// This is the instruction that a break instruction within `body` can |
| 5315 | /// use to return from the body. |
| 5316 | body_inst: Zir.Inst.Index, |
| 5317 | merges: *Block.Merges, |
| 5318 | ) CompileError!Air.Inst.Ref { |
| 5319 | if (child_block.isComptime()) { |
| 5320 | return sema.resolveInlineBody(child_block, body, body_inst); |
| 5321 | } else { |
| 5322 | assert(sema.air_instructions.items(.tag)[@backingInt(merges.block_inst)] == .block); |
| 5323 | var need_debug_scope = false; |
| 5324 | child_block.need_debug_scope = &need_debug_scope; |
| 5325 | if (sema.analyzeBodyInner(child_block, body)) { |
| 5326 | return sema.resolveAnalyzedBlock(parent_block, src, child_block, merges, need_debug_scope); |
| 5327 | } else |err| switch (err) { |
| 5328 | error.ComptimeBreak => { |
| 5329 | const break_inst = sema.comptime_break_inst; |
| 5330 | const break_data = sema.code.instructions.items(.data)[@backingInt(break_inst)].@"break"; |
| 5331 | const extra = sema.code.extraData(Zir.Inst.Break, break_data.payload_index).data; |
| 5332 | const breaks_to_body = extra.block_inst == body_inst; |
| 5333 | |
| 5334 | // Comptime control flow is happening, however child_block may still contain |
| 5335 | // runtime instructions which need to be copied to the parent block. |
| 5336 | if (need_debug_scope and child_block.instructions.items.len > 0) { |
| 5337 | // We need a runtime block for scoping reasons. The break |
| 5338 | // operand may have been produced by a runtime instruction |
| 5339 | // inside `child_blocks`. |
| 5340 | const operand = sema.resolveInst(break_data.operand); |
| 5341 | const operand_ty = sema.typeOf(operand); |
| 5342 | _ = try child_block.addBr(merges.block_inst, operand); |
| 5343 | try parent_block.instructions.append(sema.gpa, merges.block_inst); |
| 5344 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Block).@"struct".field_names.len + |
| 5345 | child_block.instructions.items.len); |
| 5346 | sema.air_instructions.items(.data)[@backingInt(merges.block_inst)] = .{ .ty_pl = .{ |
| 5347 | .ty = operand_ty, |
| 5348 | .payload = sema.addExtraAssumeCapacity(Air.Block{ |
| 5349 | .body_len = @intCast(child_block.instructions.items.len), |
| 5350 | }), |
| 5351 | } }; |
| 5352 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); |
| 5353 | |
| 5354 | // The block result now holds the operand value, so we remap |
| 5355 | // the operand such that an enclosing scope which resolves |
| 5356 | // it picks up the block result rather than the internal |
| 5357 | // block instruction. |
| 5358 | if (break_data.operand.toIndex()) |operand_zir| { |
| 5359 | sema.inst_map.putAssumeCapacity(operand_zir, merges.block_inst.toRef()); |
| 5360 | } |
| 5361 | if (breaks_to_body) { |
| 5362 | return merges.block_inst.toRef(); |
| 5363 | } else { |
| 5364 | return error.ComptimeBreak; |
| 5365 | } |
| 5366 | } else { |
| 5367 | // We can copy instructions directly to the parent block. |
| 5368 | try parent_block.instructions.appendSlice(sema.gpa, child_block.instructions.items); |
| 5369 | if (breaks_to_body) { |
| 5370 | return sema.resolveInst(break_data.operand); |
| 5371 | } else { |
| 5372 | return error.ComptimeBreak; |
| 5373 | } |
| 5374 | } |
| 5375 | }, |
| 5376 | else => |e| return e, |
| 5377 | } |
| 5378 | } |
| 5379 | } |
| 5380 | |
| 5381 | /// After a body corresponding to an AIR `block` has been analyzed, this function places them into |
| 5382 | /// the block pointed at by `merges.block_inst` if necessary, or the block may be elided in favor of |
| 5383 | /// inlining the instructions directly into the parent block. Either way, it considers all merges of |
| 5384 | /// this block, and combines them appropriately using peer type resolution, returning the final |
| 5385 | /// value of the block. |
| 5386 | fn resolveAnalyzedBlock( |
| 5387 | sema: *Sema, |
| 5388 | parent_block: *Block, |
| 5389 | src: LazySrcLoc, |
| 5390 | child_block: *Block, |
| 5391 | merges: *Block.Merges, |
| 5392 | need_debug_scope: bool, |
| 5393 | ) CompileError!Air.Inst.Ref { |
| 5394 | const gpa = sema.gpa; |
| 5395 | const pt = sema.pt; |
| 5396 | const zcu = pt.zcu; |
| 5397 | |
| 5398 | // Blocks must terminate with noreturn instruction. |
| 5399 | assert(child_block.instructions.items.len != 0); |
| 5400 | assert(sema.typeOf(child_block.instructions.items[child_block.instructions.items.len - 1].toRef()).isNoReturn(zcu)); |
| 5401 | |
| 5402 | const block_tag = sema.air_instructions.items(.tag)[@backingInt(merges.block_inst)]; |
| 5403 | switch (block_tag) { |
| 5404 | .block => {}, |
| 5405 | .dbg_inline_block => assert(need_debug_scope), |
| 5406 | else => unreachable, |
| 5407 | } |
| 5408 | if (merges.results.items.len == 0) { |
| 5409 | switch (block_tag) { |
| 5410 | .block => { |
| 5411 | // No need for a block instruction. We can put the new instructions |
| 5412 | // directly into the parent block. |
| 5413 | if (need_debug_scope) { |
| 5414 | // The code following this block is unreachable, as the block has no |
| 5415 | // merges, so we don't necessarily need to emit this as an AIR block. |
| 5416 | // However, we need a block *somewhere* to make the scoping correct, |
| 5417 | // so forward this request to the parent block. |
| 5418 | if (parent_block.need_debug_scope) |ptr| ptr.* = true; |
| 5419 | } |
| 5420 | try parent_block.instructions.appendSlice(gpa, child_block.instructions.items); |
| 5421 | return child_block.instructions.items[child_block.instructions.items.len - 1].toRef(); |
| 5422 | }, |
| 5423 | .dbg_inline_block => { |
| 5424 | // Create a block containing all instruction from the body. |
| 5425 | try parent_block.instructions.append(gpa, merges.block_inst); |
| 5426 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).@"struct".field_names.len + |
| 5427 | child_block.instructions.items.len); |
| 5428 | sema.air_instructions.items(.data)[@backingInt(merges.block_inst)] = .{ .ty_pl = .{ |
| 5429 | .ty = .noreturn, |
| 5430 | .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{ |
| 5431 | .func = child_block.inlining.?.func, |
| 5432 | .body_len = @intCast(child_block.instructions.items.len), |
| 5433 | }), |
| 5434 | } }; |
| 5435 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); |
| 5436 | return merges.block_inst.toRef(); |
| 5437 | }, |
| 5438 | else => unreachable, |
| 5439 | } |
| 5440 | } |
| 5441 | if (merges.results.items.len == 1) { |
| 5442 | // If the `break` is trailing, we may be able to elide the AIR block here |
| 5443 | // by appending the new instructions directly to the parent block. |
| 5444 | if (!need_debug_scope) { |
| 5445 | const last_inst_index = child_block.instructions.items.len - 1; |
| 5446 | const last_inst = child_block.instructions.items[last_inst_index]; |
| 5447 | if (sema.getBreakBlock(last_inst)) |br_block| { |
| 5448 | if (br_block == merges.block_inst) { |
| 5449 | // Great, the last instruction is the break! Put the instructions |
| 5450 | // directly into the parent block. |
| 5451 | try parent_block.instructions.appendSlice(gpa, child_block.instructions.items[0..last_inst_index]); |
| 5452 | return merges.results.items[0]; |
| 5453 | } |
| 5454 | } |
| 5455 | } |
| 5456 | // Okay, we need a runtime block. If the value is comptime-known, the |
| 5457 | // block should just return void, and we return the merge result |
| 5458 | // directly. Otherwise, we can defer to the logic below. |
| 5459 | if (sema.resolveValue(merges.results.items[0])) |result_val| { |
| 5460 | // Create a block containing all instruction from the body. |
| 5461 | try parent_block.instructions.append(gpa, merges.block_inst); |
| 5462 | switch (block_tag) { |
| 5463 | .block => { |
| 5464 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".field_names.len + |
| 5465 | child_block.instructions.items.len); |
| 5466 | sema.air_instructions.items(.data)[@backingInt(merges.block_inst)] = .{ .ty_pl = .{ |
| 5467 | .ty = .void, |
| 5468 | .payload = sema.addExtraAssumeCapacity(Air.Block{ |
| 5469 | .body_len = @intCast(child_block.instructions.items.len), |
| 5470 | }), |
| 5471 | } }; |
| 5472 | }, |
| 5473 | .dbg_inline_block => { |
| 5474 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).@"struct".field_names.len + |
| 5475 | child_block.instructions.items.len); |
| 5476 | sema.air_instructions.items(.data)[@backingInt(merges.block_inst)] = .{ .ty_pl = .{ |
| 5477 | .ty = .void, |
| 5478 | .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{ |
| 5479 | .func = child_block.inlining.?.func, |
| 5480 | .body_len = @intCast(child_block.instructions.items.len), |
| 5481 | }), |
| 5482 | } }; |
| 5483 | }, |
| 5484 | else => unreachable, |
| 5485 | } |
| 5486 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); |
| 5487 | // Rewrite the break to just give value {}; the value is |
| 5488 | // comptime-known and will be returned directly. |
| 5489 | sema.air_instructions.items(.data)[@backingInt(merges.br_list.items[0])].br.operand = .void_value; |
| 5490 | return Air.internedToRef(result_val.toIntern()); |
| 5491 | } |
| 5492 | } |
| 5493 | // It is impossible to have the number of results be > 1 in a comptime scope. |
| 5494 | assert(!child_block.isComptime()); // Should already got a compile error in the condbr condition. |
| 5495 | |
| 5496 | // Note that we'll always create an AIR block here, so `need_debug_scope` is irrelevant. |
| 5497 | |
| 5498 | // Need to set the type and emit the Block instruction. This allows machine code generation |
| 5499 | // to emit a jump instruction to after the block when it encounters the break. |
| 5500 | try parent_block.instructions.append(gpa, merges.block_inst); |
| 5501 | const resolved_ty = try sema.resolvePeerTypes(parent_block, src, merges.results.items, .{ .override = merges.src_locs.items }); |
| 5502 | resolved_ty.assertHasLayout(zcu); |
| 5503 | // TODO add note "missing else causes void value" |
| 5504 | |
| 5505 | const type_src = src; // TODO: better source location |
| 5506 | if (resolved_ty.comptimeOnly(zcu)) { |
| 5507 | const msg = msg: { |
| 5508 | const msg = try sema.errMsg(type_src, "value with comptime-only type '{f}' depends on runtime control flow", .{resolved_ty.fmt(pt)}); |
| 5509 | errdefer msg.destroy(sema.gpa); |
| 5510 | |
| 5511 | const runtime_src = child_block.runtime_cond orelse child_block.runtime_loop.?; |
| 5512 | try sema.errNote(runtime_src, msg, "runtime control flow here", .{}); |
| 5513 | |
| 5514 | try sema.explainWhyTypeIsComptime(msg, type_src, resolved_ty); |
| 5515 | |
| 5516 | break :msg msg; |
| 5517 | }; |
| 5518 | return sema.failWithOwnedErrorMsg(child_block, msg); |
| 5519 | } |
| 5520 | for (merges.results.items, merges.src_locs.items) |merge_inst, merge_src| { |
| 5521 | try sema.validateRuntimeValue(child_block, merge_src orelse src, merge_inst); |
| 5522 | } |
| 5523 | |
| 5524 | try sema.checkMergeAllowed(child_block, type_src, resolved_ty); |
| 5525 | |
| 5526 | switch (block_tag) { |
| 5527 | .block => { |
| 5528 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".field_names.len + |
| 5529 | child_block.instructions.items.len); |
| 5530 | sema.air_instructions.items(.data)[@backingInt(merges.block_inst)] = .{ .ty_pl = .{ |
| 5531 | .ty = resolved_ty, |
| 5532 | .payload = sema.addExtraAssumeCapacity(Air.Block{ |
| 5533 | .body_len = @intCast(child_block.instructions.items.len), |
| 5534 | }), |
| 5535 | } }; |
| 5536 | }, |
| 5537 | .dbg_inline_block => { |
| 5538 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.DbgInlineBlock).@"struct".field_names.len + |
| 5539 | child_block.instructions.items.len); |
| 5540 | sema.air_instructions.items(.data)[@backingInt(merges.block_inst)] = .{ .ty_pl = .{ |
| 5541 | .ty = resolved_ty, |
| 5542 | .payload = sema.addExtraAssumeCapacity(Air.DbgInlineBlock{ |
| 5543 | .func = child_block.inlining.?.func, |
| 5544 | .body_len = @intCast(child_block.instructions.items.len), |
| 5545 | }), |
| 5546 | } }; |
| 5547 | }, |
| 5548 | else => unreachable, |
| 5549 | } |
| 5550 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); |
| 5551 | // Now that the block has its type resolved, we need to go back into all the break |
| 5552 | // instructions, and insert type coercion on the operands. |
| 5553 | for (merges.br_list.items) |br| { |
| 5554 | const br_operand = sema.air_instructions.items(.data)[@backingInt(br)].br.operand; |
| 5555 | const br_operand_src = src; |
| 5556 | const br_operand_ty = sema.typeOf(br_operand); |
| 5557 | if (br_operand_ty.eql(resolved_ty)) { |
| 5558 | // No type coercion needed. |
| 5559 | continue; |
| 5560 | } |
| 5561 | var coerce_block = parent_block.makeSubBlock(); |
| 5562 | defer coerce_block.instructions.deinit(gpa); |
| 5563 | const coerced_operand = try sema.coerce(&coerce_block, resolved_ty, br_operand, br_operand_src); |
| 5564 | // If no instructions were produced, such as in the case of a coercion of a |
| 5565 | // constant value to a new type, we can simply point the br operand to it. |
| 5566 | if (coerce_block.instructions.items.len == 0) { |
| 5567 | sema.air_instructions.items(.data)[@backingInt(br)].br.operand = coerced_operand; |
| 5568 | continue; |
| 5569 | } |
| 5570 | assert(coerce_block.instructions.items[coerce_block.instructions.items.len - 1].toRef() == coerced_operand); |
| 5571 | |
| 5572 | // Convert the br instruction to a block instruction that has the coercion |
| 5573 | // and then a new br inside that returns the coerced instruction. |
| 5574 | const sub_block_len: u32 = @intCast(coerce_block.instructions.items.len + 1); |
| 5575 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".field_names.len + |
| 5576 | sub_block_len); |
| 5577 | try sema.air_instructions.ensureUnusedCapacity(gpa, 1); |
| 5578 | const sub_br_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len)); |
| 5579 | |
| 5580 | sema.air_instructions.items(.tag)[@backingInt(br)] = .block; |
| 5581 | sema.air_instructions.items(.data)[@backingInt(br)] = .{ .ty_pl = .{ |
| 5582 | .ty = .noreturn, |
| 5583 | .payload = sema.addExtraAssumeCapacity(Air.Block{ |
| 5584 | .body_len = sub_block_len, |
| 5585 | }), |
| 5586 | } }; |
| 5587 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); |
| 5588 | sema.air_extra.appendAssumeCapacity(@backingInt(sub_br_inst)); |
| 5589 | |
| 5590 | sema.air_instructions.appendAssumeCapacity(.{ |
| 5591 | .tag = .br, |
| 5592 | .data = .{ .br = .{ |
| 5593 | .block_inst = merges.block_inst, |
| 5594 | .operand = coerced_operand, |
| 5595 | } }, |
| 5596 | }); |
| 5597 | } |
| 5598 | |
| 5599 | if (try resolved_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 5600 | return merges.block_inst.toRef(); |
| 5601 | } |
| 5602 | |
| 5603 | fn zirExport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 5604 | const pt = sema.pt; |
| 5605 | const zcu = pt.zcu; |
| 5606 | const ip = &zcu.intern_pool; |
| 5607 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 5608 | const extra = sema.code.extraData(Zir.Inst.Export, inst_data.payload_index).data; |
| 5609 | |
| 5610 | const src = block.nodeOffset(inst_data.src_node); |
| 5611 | const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 5612 | const options_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 5613 | |
| 5614 | const ptr = sema.resolveInst(extra.exported); |
| 5615 | const ptr_val = try sema.resolveConstDefinedValue(block, ptr_src, ptr, .{ .simple = .export_target }); |
| 5616 | const ptr_ty = ptr_val.typeOf(zcu); |
| 5617 | |
| 5618 | const options = try sema.resolveExportOptions(block, options_src, extra.options); |
| 5619 | |
| 5620 | { |
| 5621 | if (ptr_ty.zigTypeTag(zcu) != .pointer) { |
| 5622 | return sema.fail(block, ptr_src, "expected pointer type, found '{f}'", .{ptr_ty.fmt(pt)}); |
| 5623 | } |
| 5624 | const ptr_ty_info = ptr_ty.ptrInfo(zcu); |
| 5625 | if (ptr_ty_info.flags.size == .slice) { |
| 5626 | return sema.fail(block, ptr_src, "export target cannot be slice", .{}); |
| 5627 | } |
| 5628 | if (ptr_ty_info.packed_offset.host_size != 0) { |
| 5629 | return sema.fail(block, ptr_src, "export target cannot be bit-pointer", .{}); |
| 5630 | } |
| 5631 | } |
| 5632 | |
| 5633 | const export_ty = ptr_ty.childType(zcu); |
| 5634 | try sema.ensureLayoutResolved(export_ty, src, .@"export"); |
| 5635 | if (!export_ty.validateExtern(.other, zcu)) { |
| 5636 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 5637 | const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)}); |
| 5638 | errdefer msg.destroy(sema.gpa); |
| 5639 | try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other); |
| 5640 | try sema.addDeclaredHereNote(msg, export_ty); |
| 5641 | break :msg msg; |
| 5642 | }); |
| 5643 | } |
| 5644 | |
| 5645 | const ptr_info = ip.indexToKey(ptr_val.toIntern()).ptr; |
| 5646 | const target: Zcu.Exported = switch (ptr_info.base_addr) { |
| 5647 | .comptime_alloc, .int, .comptime_field => return sema.fail(block, ptr_src, "export target must be a global variable or a comptime-known constant", .{}), |
| 5648 | .eu_payload, .opt_payload, .field, .arr_elem => return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}), |
| 5649 | .uav => |uav| .{ .uav = uav.val }, |
| 5650 | .nav => |orig_nav| target: { |
| 5651 | try sema.ensureNavResolved(block, src, orig_nav, .fully); |
| 5652 | const export_nav = switch (ip.indexToKey(ip.getNav(orig_nav).resolved.?.value)) { |
| 5653 | .@"extern" => |e| e.owner_nav, |
| 5654 | .func => |f| f.owner_nav, |
| 5655 | else => orig_nav, |
| 5656 | }; |
| 5657 | if (ip.getNav(export_nav).getExtern(ip) != null) { |
| 5658 | return sema.fail(block, src, "export target cannot be extern", .{}); |
| 5659 | } |
| 5660 | try sema.maybeQueueFuncBodyAnalysis(block, src, export_nav); |
| 5661 | break :target .{ .nav = export_nav }; |
| 5662 | }, |
| 5663 | }; |
| 5664 | if (ptr_info.byte_offset != 0) { |
| 5665 | return sema.fail(block, ptr_src, "TODO: export pointer in middle of value", .{}); |
| 5666 | } |
| 5667 | try sema.exports.append(zcu.gpa, .{ |
| 5668 | .opts = options, |
| 5669 | .src = src, |
| 5670 | .exported = target, |
| 5671 | }); |
| 5672 | } |
| 5673 | |
| 5674 | /// Asserts that `sema.owner` is a `.nav_val` whose value is resolved. |
| 5675 | /// |
| 5676 | /// Exports that `Nav` by the given name with all other options set to default. |
| 5677 | pub fn analyzeExportSelfNav( |
| 5678 | sema: *Sema, |
| 5679 | block: *Block, |
| 5680 | src: LazySrcLoc, |
| 5681 | name: InternPool.NullTerminatedString, |
| 5682 | ) !void { |
| 5683 | const gpa = sema.gpa; |
| 5684 | const pt = sema.pt; |
| 5685 | const zcu = pt.zcu; |
| 5686 | const ip = &zcu.intern_pool; |
| 5687 | |
| 5688 | const orig_nav = sema.owner.unwrap().nav_val; |
| 5689 | const export_val: Value = .fromInterned(ip.getNav(orig_nav).resolved.?.value); |
| 5690 | const export_ty = export_val.typeOf(zcu); |
| 5691 | |
| 5692 | if (!export_ty.validateExtern(.other, zcu)) { |
| 5693 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 5694 | const msg = try sema.errMsg(src, "unable to export type '{f}'", .{export_ty.fmt(pt)}); |
| 5695 | errdefer msg.destroy(gpa); |
| 5696 | try sema.explainWhyTypeIsNotExtern(msg, src, export_ty, .other); |
| 5697 | try sema.addDeclaredHereNote(msg, export_ty); |
| 5698 | break :msg msg; |
| 5699 | }); |
| 5700 | } |
| 5701 | |
| 5702 | const export_nav = switch (ip.indexToKey(export_val.toIntern())) { |
| 5703 | .@"extern" => |e| e.owner_nav, |
| 5704 | .func => |f| export_nav: { |
| 5705 | assert(export_ty.fnHasRuntimeBits(zcu)); // otherwise `validateExtern` failed above |
| 5706 | const orig_fn_index = ip.unwrapCoercedFunc(export_val.toIntern()); |
| 5707 | try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_fn_index })); |
| 5708 | try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index); |
| 5709 | break :export_nav f.owner_nav; |
| 5710 | }, |
| 5711 | else => orig_nav, |
| 5712 | }; |
| 5713 | |
| 5714 | try sema.exports.append(gpa, .{ |
| 5715 | .opts = .{ .name = name }, |
| 5716 | .src = src, |
| 5717 | .exported = .{ .nav = export_nav }, |
| 5718 | }); |
| 5719 | } |
| 5720 | |
| 5721 | fn zirDisableInstrumentation(sema: *Sema) CompileError!void { |
| 5722 | const pt = sema.pt; |
| 5723 | const zcu = pt.zcu; |
| 5724 | const io = zcu.comp.io; |
| 5725 | const ip = &zcu.intern_pool; |
| 5726 | const func = switch (sema.owner.unwrap()) { |
| 5727 | .func => |func| func, |
| 5728 | .@"comptime", |
| 5729 | .nav_val, |
| 5730 | .nav_ty, |
| 5731 | .type_layout, |
| 5732 | .struct_defaults, |
| 5733 | .memoized_state, |
| 5734 | => return, // does nothing outside a function |
| 5735 | }; |
| 5736 | ip.funcSetDisableInstrumentation(io, func); |
| 5737 | sema.allow_memoize = false; |
| 5738 | } |
| 5739 | |
| 5740 | fn zirDisableIntrinsics(sema: *Sema) CompileError!void { |
| 5741 | const pt = sema.pt; |
| 5742 | const zcu = pt.zcu; |
| 5743 | const io = zcu.comp.io; |
| 5744 | const ip = &zcu.intern_pool; |
| 5745 | const func = switch (sema.owner.unwrap()) { |
| 5746 | .func => |func| func, |
| 5747 | .@"comptime", |
| 5748 | .nav_val, |
| 5749 | .nav_ty, |
| 5750 | .type_layout, |
| 5751 | .struct_defaults, |
| 5752 | .memoized_state, |
| 5753 | => return, // does nothing outside a function |
| 5754 | }; |
| 5755 | ip.funcSetDisableIntrinsics(io, func); |
| 5756 | sema.allow_memoize = false; |
| 5757 | } |
| 5758 | |
| 5759 | fn zirSetFloatMode(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { |
| 5760 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 5761 | const src = block.builtinCallArgSrc(extra.node, 0); |
| 5762 | block.float_mode = try sema.resolveStdLangEnum(block, src, extra.operand, .FloatMode, .{ .simple = .operand_setFloatMode }); |
| 5763 | } |
| 5764 | |
| 5765 | fn zirSetRuntimeSafety(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 5766 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 5767 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 5768 | block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand, .{ .simple = .operand_setRuntimeSafety }); |
| 5769 | } |
| 5770 | |
| 5771 | fn zirBreak(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 5772 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].@"break"; |
| 5773 | const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data; |
| 5774 | const operand = sema.resolveInst(inst_data.operand); |
| 5775 | const zir_block = extra.block_inst; |
| 5776 | |
| 5777 | var block = start_block; |
| 5778 | while (true) { |
| 5779 | if (block.label) |label| { |
| 5780 | if (label.zir_block == zir_block) { |
| 5781 | const br_ref = try start_block.addBr(label.merges.block_inst, operand); |
| 5782 | const src_loc = if (extra.operand_src_node.unwrap()) |operand_src_node| |
| 5783 | start_block.nodeOffset(operand_src_node) |
| 5784 | else |
| 5785 | null; |
| 5786 | try label.merges.src_locs.append(sema.gpa, src_loc); |
| 5787 | try label.merges.results.append(sema.gpa, operand); |
| 5788 | try label.merges.br_list.append(sema.gpa, br_ref.toIndex().?); |
| 5789 | block.runtime_index.increment(); |
| 5790 | if (block.runtime_cond == null and block.runtime_loop == null) { |
| 5791 | block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop; |
| 5792 | block.runtime_loop = start_block.runtime_loop; |
| 5793 | } |
| 5794 | return; |
| 5795 | } |
| 5796 | } |
| 5797 | block = block.parent.?; |
| 5798 | } |
| 5799 | } |
| 5800 | |
| 5801 | fn zirSwitchContinue(sema: *Sema, start_block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 5802 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].@"break"; |
| 5803 | const extra = sema.code.extraData(Zir.Inst.Break, inst_data.payload_index).data; |
| 5804 | const operand_src = start_block.nodeOffset(extra.operand_src_node.unwrap().?); |
| 5805 | const uncoerced_operand = sema.resolveInst(inst_data.operand); |
| 5806 | const switch_inst = extra.block_inst; |
| 5807 | |
| 5808 | switch (sema.code.instructions.items(.tag)[@backingInt(switch_inst)]) { |
| 5809 | .switch_block, .switch_block_ref => {}, |
| 5810 | .switch_block_err_union => unreachable, // wrong code path! |
| 5811 | else => unreachable, // assertion failure |
| 5812 | } |
| 5813 | |
| 5814 | const operand_ty = (sema.resolveInst(switch_inst.toRef())).toType(); |
| 5815 | const operand = try sema.coerce(start_block, operand_ty, uncoerced_operand, operand_src); |
| 5816 | try sema.validateRuntimeValue(start_block, operand_src, operand); |
| 5817 | |
| 5818 | // We want to generate a `switch_dispatch` instruction with the switch condition, |
| 5819 | // possibly preceded by a store to the stack alloc containing the raw operand. |
| 5820 | // However, to avoid too much special-case state in Sema, this is handled by the |
| 5821 | // `switch` lowering logic. As such, we will find the `Block` corresponding to |
| 5822 | // the parent `switch_block[_ref]` instruction, create a dummy `br`, and add a |
| 5823 | // merge to signal to the switch logic to rewrite this into an appropriate dispatch. |
| 5824 | |
| 5825 | var block = start_block; |
| 5826 | while (true) : (block = block.parent.?) { |
| 5827 | if (block.label) |label| { |
| 5828 | if (label.zir_block == switch_inst) { |
| 5829 | const br_ref = try start_block.addBr(label.merges.block_inst, operand); |
| 5830 | try label.merges.extra_insts.append(sema.gpa, br_ref.toIndex().?); |
| 5831 | try label.merges.extra_src_locs.append(sema.gpa, operand_src); |
| 5832 | block.runtime_index.increment(); |
| 5833 | if (block.runtime_cond == null and block.runtime_loop == null) { |
| 5834 | block.runtime_cond = start_block.runtime_cond orelse start_block.runtime_loop; |
| 5835 | block.runtime_loop = start_block.runtime_loop; |
| 5836 | } |
| 5837 | return; |
| 5838 | } |
| 5839 | } |
| 5840 | } |
| 5841 | } |
| 5842 | |
| 5843 | fn zirDbgStmt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 5844 | if (block.isComptime() or block.ownerModule().strip) return; |
| 5845 | |
| 5846 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].dbg_stmt; |
| 5847 | |
| 5848 | if (block.instructions.items.len != 0) { |
| 5849 | const idx = block.instructions.items[block.instructions.items.len - 1]; |
| 5850 | if (sema.air_instructions.items(.tag)[@backingInt(idx)] == .dbg_stmt) { |
| 5851 | // The previous dbg_stmt didn't correspond to any actual code, so replace it. |
| 5852 | sema.air_instructions.items(.data)[@backingInt(idx)].dbg_stmt = .{ |
| 5853 | .line = inst_data.line, |
| 5854 | .column = inst_data.column, |
| 5855 | }; |
| 5856 | return; |
| 5857 | } |
| 5858 | } |
| 5859 | |
| 5860 | _ = try block.addInst(.{ |
| 5861 | .tag = .dbg_stmt, |
| 5862 | .data = .{ .dbg_stmt = .{ |
| 5863 | .line = inst_data.line, |
| 5864 | .column = inst_data.column, |
| 5865 | } }, |
| 5866 | }); |
| 5867 | } |
| 5868 | |
| 5869 | fn zirDbgEmptyStmt(_: *Sema, block: *Block, _: Zir.Inst.Index) CompileError!void { |
| 5870 | if (block.isComptime() or block.ownerModule().strip) return; |
| 5871 | _ = try block.addNoOp(.dbg_empty_stmt); |
| 5872 | } |
| 5873 | |
| 5874 | fn zirDbgVar( |
| 5875 | sema: *Sema, |
| 5876 | block: *Block, |
| 5877 | inst: Zir.Inst.Index, |
| 5878 | air_tag: Air.Inst.Tag, |
| 5879 | ) CompileError!void { |
| 5880 | const str_op = sema.code.instructions.items(.data)[@backingInt(inst)].str_op; |
| 5881 | const operand = sema.resolveInst(str_op.operand); |
| 5882 | const name = str_op.getStr(sema.code); |
| 5883 | try sema.addDbgVar(block, operand, air_tag, name); |
| 5884 | } |
| 5885 | |
| 5886 | fn addDbgVar( |
| 5887 | sema: *Sema, |
| 5888 | block: *Block, |
| 5889 | operand: Air.Inst.Ref, |
| 5890 | air_tag: Air.Inst.Tag, |
| 5891 | name: []const u8, |
| 5892 | ) CompileError!void { |
| 5893 | if (block.isComptime() or block.ownerModule().strip) return; |
| 5894 | |
| 5895 | const pt = sema.pt; |
| 5896 | const zcu = pt.zcu; |
| 5897 | const operand_ty = sema.typeOf(operand); |
| 5898 | const val_ty = switch (air_tag) { |
| 5899 | .dbg_var_ptr => operand_ty.childType(zcu), |
| 5900 | .dbg_var_val, .dbg_arg_inline => operand_ty, |
| 5901 | else => unreachable, |
| 5902 | }; |
| 5903 | if (val_ty.comptimeOnly(zcu)) return; |
| 5904 | if (!val_ty.hasRuntimeBits(zcu)) return; |
| 5905 | if (sema.resolveValue(operand)) |operand_val| { |
| 5906 | if (operand_val.canMutateComptimeVarState(zcu)) return; |
| 5907 | } |
| 5908 | |
| 5909 | // To ensure the lexical scoping is known to backends, this alloc must be |
| 5910 | // within a real runtime block. We set a flag which communicates information |
| 5911 | // to the closest lexically enclosing block: |
| 5912 | // * If it is a `block_inline`, communicates to logic in `analyzeBodyInner` |
| 5913 | // to create a post-hoc block. |
| 5914 | // * Otherwise, communicates to logic in `resolveBlockBody` to create a |
| 5915 | // real `block` instruction. |
| 5916 | if (block.need_debug_scope) |ptr| ptr.* = true; |
| 5917 | |
| 5918 | // Add the name to the AIR. |
| 5919 | const name_nts = try sema.appendAirString(name); |
| 5920 | |
| 5921 | _ = try block.addInst(.{ |
| 5922 | .tag = air_tag, |
| 5923 | .data = .{ .pl_op = .{ |
| 5924 | .payload = @backingInt(name_nts), |
| 5925 | .operand = operand, |
| 5926 | } }, |
| 5927 | }); |
| 5928 | } |
| 5929 | |
| 5930 | pub fn appendAirString(sema: *Sema, str: []const u8) Allocator.Error!Air.NullTerminatedString { |
| 5931 | if (str.len == 0) return .none; |
| 5932 | const nts: Air.NullTerminatedString = @fromBackingInt(@intCast(sema.air_extra.items.len)); |
| 5933 | const elements_used = str.len / 4 + 1; |
| 5934 | const elements = try sema.air_extra.addManyAsSlice(sema.gpa, elements_used); |
| 5935 | const buffer = mem.sliceAsBytes(elements); |
| 5936 | @memcpy(buffer[0..str.len], str); |
| 5937 | buffer[str.len] = 0; |
| 5938 | return nts; |
| 5939 | } |
| 5940 | |
| 5941 | fn zirDeclRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 5942 | const pt = sema.pt; |
| 5943 | const zcu = pt.zcu; |
| 5944 | const comp = zcu.comp; |
| 5945 | const gpa = comp.gpa; |
| 5946 | const io = comp.io; |
| 5947 | |
| 5948 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].str_tok; |
| 5949 | const src = block.tokenOffset(inst_data.src_tok); |
| 5950 | const decl_name = try zcu.intern_pool.getOrPutString( |
| 5951 | gpa, |
| 5952 | io, |
| 5953 | pt.tid, |
| 5954 | inst_data.get(sema.code), |
| 5955 | .no_embedded_nulls, |
| 5956 | ); |
| 5957 | const nav_index = try sema.lookupIdentifier(block, decl_name); |
| 5958 | return sema.analyzeNavRef(block, src, nav_index); |
| 5959 | } |
| 5960 | |
| 5961 | fn zirDeclVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 5962 | const pt = sema.pt; |
| 5963 | const zcu = pt.zcu; |
| 5964 | const comp = zcu.comp; |
| 5965 | const gpa = comp.gpa; |
| 5966 | const io = comp.io; |
| 5967 | |
| 5968 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].str_tok; |
| 5969 | const src = block.tokenOffset(inst_data.src_tok); |
| 5970 | const decl_name = try zcu.intern_pool.getOrPutString( |
| 5971 | gpa, |
| 5972 | io, |
| 5973 | pt.tid, |
| 5974 | inst_data.get(sema.code), |
| 5975 | .no_embedded_nulls, |
| 5976 | ); |
| 5977 | const nav = try sema.lookupIdentifier(block, decl_name); |
| 5978 | return sema.analyzeNavVal(block, src, nav); |
| 5979 | } |
| 5980 | |
| 5981 | fn lookupIdentifier(sema: *Sema, block: *Block, name: InternPool.NullTerminatedString) !InternPool.Nav.Index { |
| 5982 | const pt = sema.pt; |
| 5983 | const zcu = pt.zcu; |
| 5984 | var namespace = block.namespace; |
| 5985 | while (true) { |
| 5986 | if (try sema.lookupInNamespace(block, namespace, name)) |lookup| { |
| 5987 | assert(lookup.accessible == .public or lookup.accessible == .private_same_file); |
| 5988 | return lookup.nav; |
| 5989 | } |
| 5990 | namespace = zcu.namespacePtr(namespace).parent.unwrap() orelse break; |
| 5991 | } |
| 5992 | unreachable; // AstGen detects use of undeclared identifiers. |
| 5993 | } |
| 5994 | |
| 5995 | /// This looks up a member of a specific namespace. |
| 5996 | fn lookupInNamespace( |
| 5997 | sema: *Sema, |
| 5998 | block: *Block, |
| 5999 | namespace_index: InternPool.NamespaceIndex, |
| 6000 | ident_name: InternPool.NullTerminatedString, |
| 6001 | ) CompileError!?struct { |
| 6002 | nav: InternPool.Nav.Index, |
| 6003 | accessible: enum { public, private_same_file, private }, |
| 6004 | } { |
| 6005 | const pt = sema.pt; |
| 6006 | const zcu = pt.zcu; |
| 6007 | |
| 6008 | pt.ensureNamespaceUpToDate(namespace_index) catch |err| switch (err) { |
| 6009 | error.LostZirContainerDecl => { |
| 6010 | const namespace = zcu.namespacePtr(namespace_index); |
| 6011 | const ns_ty: Type = .fromInterned(namespace.owner_type); |
| 6012 | return sema.failTransitive(.{ .lost_tracking = ns_ty.typeDeclInstAllowGeneratedTag(zcu).? }); |
| 6013 | }, |
| 6014 | else => |e| return e, |
| 6015 | }; |
| 6016 | |
| 6017 | const namespace = zcu.namespacePtr(namespace_index); |
| 6018 | |
| 6019 | const adapter: Zcu.Namespace.NameAdapter = .{ .zcu = zcu }; |
| 6020 | |
| 6021 | const src_file = zcu.namespacePtr(block.namespace).file_scope; |
| 6022 | |
| 6023 | if (Type.fromInterned(namespace.owner_type).typeDeclInst(zcu)) |type_decl_inst| { |
| 6024 | try sema.declareDependency(.{ .namespace_name = .{ |
| 6025 | .namespace = type_decl_inst, |
| 6026 | .name = ident_name, |
| 6027 | } }); |
| 6028 | } |
| 6029 | |
| 6030 | if (namespace.pub_decls.getKeyAdapted(ident_name, adapter)) |nav_index| { |
| 6031 | return .{ |
| 6032 | .nav = nav_index, |
| 6033 | .accessible = .public, |
| 6034 | }; |
| 6035 | } else if (namespace.priv_decls.getKeyAdapted(ident_name, adapter)) |nav_index| { |
| 6036 | return .{ |
| 6037 | .nav = nav_index, |
| 6038 | .accessible = if (src_file == namespace.file_scope) .private_same_file else .private, |
| 6039 | }; |
| 6040 | } |
| 6041 | |
| 6042 | return null; |
| 6043 | } |
| 6044 | |
| 6045 | fn funcDeclSrcInst(sema: *Sema, func_inst: Air.Inst.Ref) !?InternPool.TrackedInst.Index { |
| 6046 | const pt = sema.pt; |
| 6047 | const zcu = pt.zcu; |
| 6048 | const ip = &zcu.intern_pool; |
| 6049 | const func_val = sema.resolveValue(func_inst) orelse return null; |
| 6050 | if (func_val.isUndef(zcu)) return null; |
| 6051 | const nav = switch (ip.indexToKey(func_val.toIntern())) { |
| 6052 | .@"extern" => |e| e.owner_nav, |
| 6053 | .func => |f| f.owner_nav, |
| 6054 | .ptr => |ptr| switch (ptr.base_addr) { |
| 6055 | .nav => |nav| if (ptr.byte_offset == 0) nav else return null, |
| 6056 | else => return null, |
| 6057 | }, |
| 6058 | else => return null, |
| 6059 | }; |
| 6060 | return ip.getNav(nav).srcInst(ip); |
| 6061 | } |
| 6062 | |
| 6063 | pub fn analyzeSaveErrRetIndex(sema: *Sema, block: *Block) SemaError!Air.Inst.Ref { |
| 6064 | const pt = sema.pt; |
| 6065 | const zcu = pt.zcu; |
| 6066 | const comp = zcu.comp; |
| 6067 | const gpa = comp.gpa; |
| 6068 | const io = comp.io; |
| 6069 | |
| 6070 | if (block.isComptime() or block.is_typeof) { |
| 6071 | const index_val = try pt.intValue_u64(.usize, sema.comptime_err_ret_trace.items.len); |
| 6072 | return Air.internedToRef(index_val.toIntern()); |
| 6073 | } |
| 6074 | |
| 6075 | if (!block.ownerModule().error_tracing) return .none; |
| 6076 | |
| 6077 | const stack_trace_ty = try sema.getStdLangType(block.nodeOffset(.zero), .StackTrace); |
| 6078 | const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls); |
| 6079 | const field_index = sema.structFieldIndex(block, stack_trace_ty, field_name, LazySrcLoc.unneeded) catch |err| switch (err) { |
| 6080 | error.AlreadyReported => @panic("std.lang.StackTrace is corrupt"), |
| 6081 | error.ComptimeReturn, error.ComptimeBreak => unreachable, |
| 6082 | error.OutOfMemory, error.Canceled => |e| return e, |
| 6083 | }; |
| 6084 | |
| 6085 | return try block.addInst(.{ |
| 6086 | .tag = .save_err_return_trace_index, |
| 6087 | .data = .{ .ty_pl = .{ |
| 6088 | .ty = stack_trace_ty, |
| 6089 | .payload = @intCast(field_index), |
| 6090 | } }, |
| 6091 | }); |
| 6092 | } |
| 6093 | |
| 6094 | /// Add instructions to block to "pop" the error return trace. |
| 6095 | /// If `operand` is provided, only pops if operand is non-error. |
| 6096 | fn popErrorReturnTrace( |
| 6097 | sema: *Sema, |
| 6098 | block: *Block, |
| 6099 | src: LazySrcLoc, |
| 6100 | operand: Air.Inst.Ref, |
| 6101 | saved_error_trace_index: Air.Inst.Ref, |
| 6102 | ) CompileError!void { |
| 6103 | const pt = sema.pt; |
| 6104 | const zcu = pt.zcu; |
| 6105 | const comp = zcu.comp; |
| 6106 | const gpa = comp.gpa; |
| 6107 | const io = comp.io; |
| 6108 | var is_non_error: ?bool = null; |
| 6109 | var is_non_error_inst: Air.Inst.Ref = undefined; |
| 6110 | if (operand != .none) { |
| 6111 | is_non_error_inst = try sema.analyzeIsNonErr(block, src, operand); |
| 6112 | if (try sema.resolveDefinedValue(block, src, is_non_error_inst)) |cond_val| |
| 6113 | is_non_error = cond_val.toBool(); |
| 6114 | } else is_non_error = true; // no operand means pop unconditionally |
| 6115 | |
| 6116 | if (is_non_error == true) { |
| 6117 | // AstGen determined this result does not go to an error-handling expr (try/catch/return etc.), or |
| 6118 | // the result is comptime-known to be a non-error. Either way, pop unconditionally. |
| 6119 | |
| 6120 | const stack_trace_ty = try sema.getStdLangType(src, .StackTrace); |
| 6121 | const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty); |
| 6122 | const err_return_trace = try block.addTy(.err_return_trace, ptr_stack_trace_ty); |
| 6123 | const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls); |
| 6124 | const field_ptr = try sema.structFieldPtr(block, src, err_return_trace, field_name, src, stack_trace_ty); |
| 6125 | try sema.storePtr2(block, src, field_ptr, src, saved_error_trace_index, src, .store); |
| 6126 | } else if (is_non_error == null) { |
| 6127 | // The result might be an error. If it is, we leave the error trace alone. If it isn't, we need |
| 6128 | // to pop any error trace that may have been propagated from our arguments. |
| 6129 | |
| 6130 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".field_names.len); |
| 6131 | const cond_block_inst = try block.addInstAsIndex(.{ |
| 6132 | .tag = .block, |
| 6133 | .data = .{ |
| 6134 | .ty_pl = .{ |
| 6135 | .ty = .void, |
| 6136 | .payload = undefined, // updated below |
| 6137 | }, |
| 6138 | }, |
| 6139 | }); |
| 6140 | |
| 6141 | var then_block = block.makeSubBlock(); |
| 6142 | defer then_block.instructions.deinit(gpa); |
| 6143 | |
| 6144 | // If non-error, then pop the error return trace by restoring the index. |
| 6145 | const stack_trace_ty = try sema.getStdLangType(src, .StackTrace); |
| 6146 | const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty); |
| 6147 | const err_return_trace = try then_block.addTy(.err_return_trace, ptr_stack_trace_ty); |
| 6148 | const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls); |
| 6149 | const field_ptr = try sema.structFieldPtr(&then_block, src, err_return_trace, field_name, src, stack_trace_ty); |
| 6150 | try sema.storePtr2(&then_block, src, field_ptr, src, saved_error_trace_index, src, .store); |
| 6151 | _ = try then_block.addBr(cond_block_inst, .void_value); |
| 6152 | |
| 6153 | // Otherwise, do nothing |
| 6154 | var else_block = block.makeSubBlock(); |
| 6155 | defer else_block.instructions.deinit(gpa); |
| 6156 | _ = try else_block.addBr(cond_block_inst, .void_value); |
| 6157 | |
| 6158 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".field_names.len + |
| 6159 | then_block.instructions.items.len + else_block.instructions.items.len + |
| 6160 | @typeInfo(Air.Block).@"struct".field_names.len + 1); // +1 for the sole .cond_br instruction in the .block |
| 6161 | |
| 6162 | const cond_br_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len)); |
| 6163 | try sema.air_instructions.append(gpa, .{ |
| 6164 | .tag = .cond_br, |
| 6165 | .data = .{ |
| 6166 | .pl_op = .{ |
| 6167 | .operand = is_non_error_inst, |
| 6168 | .payload = sema.addExtraAssumeCapacity(Air.CondBr{ |
| 6169 | .then_body_len = @intCast(then_block.instructions.items.len), |
| 6170 | .else_body_len = @intCast(else_block.instructions.items.len), |
| 6171 | .branch_hints = .{ |
| 6172 | // Weight against error branch. |
| 6173 | .true = .likely, |
| 6174 | .false = .unlikely, |
| 6175 | // Code coverage is not valuable on either branch. |
| 6176 | .then_cov = .none, |
| 6177 | .else_cov = .none, |
| 6178 | }, |
| 6179 | }), |
| 6180 | }, |
| 6181 | }, |
| 6182 | }); |
| 6183 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items)); |
| 6184 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items)); |
| 6185 | |
| 6186 | sema.air_instructions.items(.data)[@backingInt(cond_block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 }); |
| 6187 | sema.air_extra.appendAssumeCapacity(@backingInt(cond_br_inst)); |
| 6188 | } |
| 6189 | } |
| 6190 | |
| 6191 | fn zirCall( |
| 6192 | sema: *Sema, |
| 6193 | block: *Block, |
| 6194 | inst: Zir.Inst.Index, |
| 6195 | comptime kind: enum { direct, field }, |
| 6196 | ) CompileError!Air.Inst.Ref { |
| 6197 | const pt = sema.pt; |
| 6198 | const zcu = pt.zcu; |
| 6199 | const comp = zcu.comp; |
| 6200 | const gpa = comp.gpa; |
| 6201 | const io = comp.io; |
| 6202 | |
| 6203 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 6204 | const callee_src = block.src(.{ .node_offset_call_func = inst_data.src_node }); |
| 6205 | const call_src = block.nodeOffset(inst_data.src_node); |
| 6206 | const ExtraType = switch (kind) { |
| 6207 | .direct => Zir.Inst.Call, |
| 6208 | .field => Zir.Inst.FieldCall, |
| 6209 | }; |
| 6210 | const extra = sema.code.extraData(ExtraType, inst_data.payload_index); |
| 6211 | const args_len = extra.data.flags.args_len; |
| 6212 | |
| 6213 | const modifier: std.lang.CallModifier = @fromBackingInt(@intCast(extra.data.flags.packed_modifier)); |
| 6214 | const ensure_result_used = extra.data.flags.ensure_result_used; |
| 6215 | const pop_error_return_trace = extra.data.flags.pop_error_return_trace; |
| 6216 | |
| 6217 | const callee: ResolvedFieldCallee = switch (kind) { |
| 6218 | .direct => .{ .direct = sema.resolveInst(extra.data.callee) }, |
| 6219 | .field => blk: { |
| 6220 | const object_ptr = sema.resolveInst(extra.data.obj_ptr); |
| 6221 | const field_name = try zcu.intern_pool.getOrPutString( |
| 6222 | gpa, |
| 6223 | io, |
| 6224 | pt.tid, |
| 6225 | sema.code.nullTerminatedString(extra.data.field_name_start), |
| 6226 | .no_embedded_nulls, |
| 6227 | ); |
| 6228 | const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node }); |
| 6229 | break :blk try sema.fieldCallBind(block, callee_src, object_ptr, field_name, field_name_src); |
| 6230 | }, |
| 6231 | }; |
| 6232 | const func: Air.Inst.Ref = switch (callee) { |
| 6233 | .direct => |func_inst| func_inst, |
| 6234 | .method => |method| method.func_inst, |
| 6235 | }; |
| 6236 | |
| 6237 | const callee_ty = sema.typeOf(func); |
| 6238 | const total_args = args_len + @intFromBool(callee == .method); |
| 6239 | const func_ty = try sema.checkCallArgumentCount(block, func, callee_src, callee_ty, total_args, callee == .method); |
| 6240 | |
| 6241 | // The block index before the call, so we can potentially insert an error trace save here later. |
| 6242 | const block_index: Air.Inst.Index = @fromBackingInt(@intCast(block.instructions.items.len)); |
| 6243 | |
| 6244 | // This will be set by `analyzeCall` to indicate whether any parameter was an error (making the |
| 6245 | // error trace potentially dirty). |
| 6246 | var input_is_error = false; |
| 6247 | |
| 6248 | const args_info: CallArgsInfo = .{ .zir_call = .{ |
| 6249 | .bound_arg = switch (callee) { |
| 6250 | .direct => .none, |
| 6251 | .method => |method| method.arg0_inst, |
| 6252 | }, |
| 6253 | .bound_arg_src = callee_src, |
| 6254 | .call_inst = inst, |
| 6255 | .call_node_offset = inst_data.src_node, |
| 6256 | .num_args = args_len, |
| 6257 | .args_body = @ptrCast(sema.code.extra[extra.end..]), |
| 6258 | .any_arg_is_error = &input_is_error, |
| 6259 | } }; |
| 6260 | |
| 6261 | // AstGen ensures that a call instruction is always preceded by a dbg_stmt instruction. |
| 6262 | const call_dbg_node: Zir.Inst.Index = @fromBackingInt(@intCast(@backingInt(inst) - 1)); |
| 6263 | const call_inst = try sema.analyzeCall(block, func, func_ty, callee_src, call_src, modifier, ensure_result_used, args_info, call_dbg_node, .call); |
| 6264 | |
| 6265 | if (block.ownerModule().error_tracing and |
| 6266 | !block.isComptime() and !block.is_typeof and (input_is_error or pop_error_return_trace)) |
| 6267 | { |
| 6268 | const return_ty = sema.typeOf(call_inst); |
| 6269 | if (modifier != .always_tail and return_ty.isNoReturn(zcu)) |
| 6270 | return call_inst; // call to "fn (...) noreturn", don't pop |
| 6271 | |
| 6272 | // TODO: we don't fix up the error trace for always_tail correctly, we should be doing it |
| 6273 | // *before* the recursive call. This will be a bit tricky to do and probably requires |
| 6274 | // moving this logic into analyzeCall. But that's probably a good idea anyway. |
| 6275 | if (modifier == .always_tail) |
| 6276 | return call_inst; |
| 6277 | |
| 6278 | // If any input is an error-type, we might need to pop any trace it generated. Otherwise, we only |
| 6279 | // need to clean-up our own trace if we were passed to a non-error-handling expression. |
| 6280 | if (input_is_error or (pop_error_return_trace and return_ty.isError(zcu))) { |
| 6281 | const stack_trace_ty = try sema.getStdLangType(call_src, .StackTrace); |
| 6282 | const field_name = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "index", .no_embedded_nulls); |
| 6283 | const field_index = try sema.structFieldIndex(block, stack_trace_ty, field_name, call_src); |
| 6284 | |
| 6285 | // Insert a save instruction before the arg resolution + call instructions we just generated |
| 6286 | const save_inst = try block.insertInst(block_index, .{ |
| 6287 | .tag = .save_err_return_trace_index, |
| 6288 | .data = .{ .ty_pl = .{ |
| 6289 | .ty = stack_trace_ty, |
| 6290 | .payload = @intCast(field_index), |
| 6291 | } }, |
| 6292 | }); |
| 6293 | |
| 6294 | // Pop the error return trace, testing the result for non-error if necessary |
| 6295 | const operand = if (pop_error_return_trace or modifier == .always_tail) .none else call_inst; |
| 6296 | try sema.popErrorReturnTrace(block, call_src, operand, save_inst); |
| 6297 | } |
| 6298 | |
| 6299 | return call_inst; |
| 6300 | } else { |
| 6301 | return call_inst; |
| 6302 | } |
| 6303 | } |
| 6304 | |
| 6305 | fn checkCallArgumentCount( |
| 6306 | sema: *Sema, |
| 6307 | block: *Block, |
| 6308 | func: Air.Inst.Ref, |
| 6309 | func_src: LazySrcLoc, |
| 6310 | callee_ty: Type, |
| 6311 | total_args: usize, |
| 6312 | member_fn: bool, |
| 6313 | ) !Type { |
| 6314 | const pt = sema.pt; |
| 6315 | const zcu = pt.zcu; |
| 6316 | const func_ty: Type = func_ty: { |
| 6317 | switch (callee_ty.zigTypeTag(zcu)) { |
| 6318 | .@"fn" => break :func_ty callee_ty, |
| 6319 | .pointer => { |
| 6320 | const ptr_info = callee_ty.ptrInfo(zcu); |
| 6321 | if (ptr_info.flags.size == .one and Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .@"fn") { |
| 6322 | break :func_ty .fromInterned(ptr_info.child); |
| 6323 | } |
| 6324 | }, |
| 6325 | .optional => { |
| 6326 | const opt_child = callee_ty.optionalChild(zcu); |
| 6327 | if (opt_child.zigTypeTag(zcu) == .@"fn" or (opt_child.isSinglePointer(zcu) and |
| 6328 | opt_child.childType(zcu).zigTypeTag(zcu) == .@"fn")) |
| 6329 | { |
| 6330 | const msg = msg: { |
| 6331 | const msg = try sema.errMsg(func_src, "cannot call optional type '{f}'", .{ |
| 6332 | callee_ty.fmt(pt), |
| 6333 | }); |
| 6334 | errdefer msg.destroy(sema.gpa); |
| 6335 | try sema.errNote(func_src, msg, "consider using '.?', 'orelse' or 'if'", .{}); |
| 6336 | break :msg msg; |
| 6337 | }; |
| 6338 | return sema.failWithOwnedErrorMsg(block, msg); |
| 6339 | } |
| 6340 | }, |
| 6341 | else => {}, |
| 6342 | } |
| 6343 | return sema.fail(block, func_src, "type '{f}' not a function", .{callee_ty.fmt(pt)}); |
| 6344 | }; |
| 6345 | |
| 6346 | const func_ty_info = zcu.typeToFunc(func_ty).?; |
| 6347 | const fn_params_len = func_ty_info.param_types.len; |
| 6348 | const args_len = total_args - @intFromBool(member_fn); |
| 6349 | if (func_ty_info.is_var_args) { |
| 6350 | assert(callConvSupportsVarArgs(func_ty_info.cc)); |
| 6351 | if (total_args >= fn_params_len) return func_ty; |
| 6352 | } else if (fn_params_len == total_args) { |
| 6353 | return func_ty; |
| 6354 | } |
| 6355 | |
| 6356 | const maybe_func_inst = try sema.funcDeclSrcInst(func); |
| 6357 | const member_str = if (member_fn) "member function " else ""; |
| 6358 | const variadic_str = if (func_ty_info.is_var_args) "at least " else ""; |
| 6359 | const msg = msg: { |
| 6360 | const msg = try sema.errMsg( |
| 6361 | func_src, |
| 6362 | "{s}expected {s}{d} argument(s), found {d}", |
| 6363 | .{ |
| 6364 | member_str, |
| 6365 | variadic_str, |
| 6366 | fn_params_len - @intFromBool(member_fn), |
| 6367 | args_len, |
| 6368 | }, |
| 6369 | ); |
| 6370 | errdefer msg.destroy(sema.gpa); |
| 6371 | |
| 6372 | if (maybe_func_inst) |func_inst| { |
| 6373 | try sema.errNote(.{ |
| 6374 | .base_node_inst = func_inst, |
| 6375 | .offset = LazySrcLoc.Offset.nodeOffset(.zero), |
| 6376 | }, msg, "function declared here", .{}); |
| 6377 | } |
| 6378 | break :msg msg; |
| 6379 | }; |
| 6380 | return sema.failWithOwnedErrorMsg(block, msg); |
| 6381 | } |
| 6382 | |
| 6383 | fn callBuiltin( |
| 6384 | sema: *Sema, |
| 6385 | block: *Block, |
| 6386 | call_src: LazySrcLoc, |
| 6387 | builtin_fn: Air.Inst.Ref, |
| 6388 | modifier: std.lang.CallModifier, |
| 6389 | args: []const Air.Inst.Ref, |
| 6390 | operation: CallOperation, |
| 6391 | ) !void { |
| 6392 | const pt = sema.pt; |
| 6393 | const zcu = pt.zcu; |
| 6394 | const callee_ty = sema.typeOf(builtin_fn); |
| 6395 | const func_ty: Type = func_ty: { |
| 6396 | switch (callee_ty.zigTypeTag(zcu)) { |
| 6397 | .@"fn" => break :func_ty callee_ty, |
| 6398 | .pointer => { |
| 6399 | const ptr_info = callee_ty.ptrInfo(zcu); |
| 6400 | if (ptr_info.flags.size == .one and Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .@"fn") { |
| 6401 | break :func_ty .fromInterned(ptr_info.child); |
| 6402 | } |
| 6403 | }, |
| 6404 | else => {}, |
| 6405 | } |
| 6406 | std.debug.panic("type '{f}' is not a function calling builtin fn", .{callee_ty.fmt(pt)}); |
| 6407 | }; |
| 6408 | |
| 6409 | const func_ty_info = zcu.typeToFunc(func_ty).?; |
| 6410 | const fn_params_len = func_ty_info.param_types.len; |
| 6411 | if (args.len != fn_params_len or (func_ty_info.is_var_args and args.len < fn_params_len)) { |
| 6412 | std.debug.panic("parameter count mismatch calling builtin fn, expected {d}, found {d}", .{ fn_params_len, args.len }); |
| 6413 | } |
| 6414 | |
| 6415 | _ = try sema.analyzeCall( |
| 6416 | block, |
| 6417 | builtin_fn, |
| 6418 | func_ty, |
| 6419 | call_src, |
| 6420 | call_src, |
| 6421 | modifier, |
| 6422 | false, |
| 6423 | .{ .resolved = .{ .src = call_src, .args = args } }, |
| 6424 | null, |
| 6425 | operation, |
| 6426 | ); |
| 6427 | } |
| 6428 | |
| 6429 | const CallOperation = enum { |
| 6430 | call, |
| 6431 | @"@call", |
| 6432 | @"@panic", |
| 6433 | @"safety check", |
| 6434 | @"error return", |
| 6435 | }; |
| 6436 | |
| 6437 | const CallArgsInfo = union(enum) { |
| 6438 | /// The full list of resolved (but uncoerced) arguments is known ahead of time. |
| 6439 | resolved: struct { |
| 6440 | src: LazySrcLoc, |
| 6441 | args: []const Air.Inst.Ref, |
| 6442 | }, |
| 6443 | |
| 6444 | /// The list of resolved (but uncoerced) arguments is known ahead of time, but |
| 6445 | /// originated from a usage of the @call builtin at the given node offset. |
| 6446 | call_builtin: struct { |
| 6447 | call_node_offset: std.zig.Ast.Node.Offset, |
| 6448 | args: []const Air.Inst.Ref, |
| 6449 | }, |
| 6450 | |
| 6451 | /// This call corresponds to a ZIR call instruction. The arguments have not yet been |
| 6452 | /// resolved. They must be resolved by `analyzeCall` so that argument resolution and |
| 6453 | /// generic instantiation may be interleaved. This is required for RLS to work on |
| 6454 | /// generic parameters. |
| 6455 | zir_call: struct { |
| 6456 | /// This may be `none`, in which case it is ignored. Otherwise, it is the |
| 6457 | /// already-resolved value of the first argument, from method call syntax. |
| 6458 | bound_arg: Air.Inst.Ref, |
| 6459 | /// The source location of `bound_arg` if it is not `null`. Otherwise `undefined`. |
| 6460 | bound_arg_src: LazySrcLoc, |
| 6461 | /// The ZIR call instruction. The parameter type is placed at this index while |
| 6462 | /// analyzing arguments. |
| 6463 | call_inst: Zir.Inst.Index, |
| 6464 | /// The node offset of `call_inst`. |
| 6465 | call_node_offset: std.zig.Ast.Node.Offset, |
| 6466 | /// The number of arguments to this call, not including `bound_arg`. |
| 6467 | num_args: u32, |
| 6468 | /// The ZIR corresponding to all function arguments (other than `bound_arg`, if it |
| 6469 | /// is not `none`). Format is precisely the same as trailing data of ZIR `call`. |
| 6470 | args_body: []const Zir.Inst.Index, |
| 6471 | /// This bool will be set to true if any argument evaluated turns out to have an error set or error union type. |
| 6472 | /// This is used by the caller to restore the error return trace when necessary. |
| 6473 | any_arg_is_error: *bool, |
| 6474 | }, |
| 6475 | |
| 6476 | fn count(cai: CallArgsInfo) usize { |
| 6477 | return switch (cai) { |
| 6478 | inline .resolved, .call_builtin => |resolved| resolved.args.len, |
| 6479 | .zir_call => |zir_call| zir_call.num_args + @intFromBool(zir_call.bound_arg != .none), |
| 6480 | }; |
| 6481 | } |
| 6482 | |
| 6483 | fn argSrc(cai: CallArgsInfo, block: *Block, arg_index: usize) LazySrcLoc { |
| 6484 | return switch (cai) { |
| 6485 | .resolved => |resolved| resolved.src, |
| 6486 | .call_builtin => |call_builtin| block.src(.{ .call_arg = .{ |
| 6487 | .call_node_offset = call_builtin.call_node_offset, |
| 6488 | .arg_index = @intCast(arg_index), |
| 6489 | } }), |
| 6490 | .zir_call => |zir_call| if (arg_index == 0 and zir_call.bound_arg != .none) { |
| 6491 | return zir_call.bound_arg_src; |
| 6492 | } else block.src(.{ .call_arg = .{ |
| 6493 | .call_node_offset = zir_call.call_node_offset, |
| 6494 | .arg_index = @intCast(arg_index - @intFromBool(zir_call.bound_arg != .none)), |
| 6495 | } }), |
| 6496 | }; |
| 6497 | } |
| 6498 | |
| 6499 | /// Analyzes the arg at `arg_index` and coerces it to `param_ty`. |
| 6500 | /// `param_ty` may be `generic_poison`. A value of `null` indicates a varargs parameter. |
| 6501 | /// `func_ty_info` may be the type before instantiation, even if a generic instantiation is in progress. |
| 6502 | /// Emits a compile error if the argument is not comptime-known despite either `block.isComptime()` or |
| 6503 | /// the parameter being marked `comptime`. |
| 6504 | fn analyzeArg( |
| 6505 | cai: CallArgsInfo, |
| 6506 | sema: *Sema, |
| 6507 | block: *Block, |
| 6508 | arg_index: usize, |
| 6509 | maybe_param_ty: ?Type, |
| 6510 | func_ty_info: InternPool.Key.FuncType, |
| 6511 | func_inst: Air.Inst.Ref, |
| 6512 | maybe_func_src_inst: ?InternPool.TrackedInst.Index, |
| 6513 | ) CompileError!Air.Inst.Ref { |
| 6514 | const pt = sema.pt; |
| 6515 | const zcu = pt.zcu; |
| 6516 | const param_count = func_ty_info.param_types.len; |
| 6517 | const uncoerced_arg: Air.Inst.Ref = switch (cai) { |
| 6518 | inline .resolved, .call_builtin => |resolved| resolved.args[arg_index], |
| 6519 | .zir_call => |zir_call| arg_val: { |
| 6520 | // Generate args to comptime params in comptime block |
| 6521 | const parent_comptime = block.comptime_reason; |
| 6522 | defer block.comptime_reason = parent_comptime; |
| 6523 | // Note that we are indexing into parameters, not arguments, so use `arg_index` instead of `real_arg_idx` |
| 6524 | if (std.math.cast(u5, arg_index)) |i| { |
| 6525 | if (i < param_count and func_ty_info.paramIsComptime(i)) { |
| 6526 | block.comptime_reason = .{ |
| 6527 | .reason = .{ |
| 6528 | .src = cai.argSrc(block, arg_index), |
| 6529 | .r = .{ |
| 6530 | .comptime_param = .{ |
| 6531 | .comptime_src = if (maybe_func_src_inst) |src_inst| .{ |
| 6532 | .base_node_inst = src_inst, |
| 6533 | .offset = .{ .func_decl_param_comptime = @intCast(arg_index) }, |
| 6534 | } else unreachable, // should be non-null because the function is generic |
| 6535 | }, |
| 6536 | }, |
| 6537 | }, |
| 6538 | }; |
| 6539 | } |
| 6540 | } |
| 6541 | |
| 6542 | const has_bound_arg = zir_call.bound_arg != .none; |
| 6543 | const uncoerced_arg = if (arg_index == 0 and has_bound_arg) zir_call.bound_arg else arg: { |
| 6544 | const real_arg_idx = arg_index - @intFromBool(has_bound_arg); |
| 6545 | |
| 6546 | const arg_body = if (real_arg_idx == 0) blk: { |
| 6547 | const start = zir_call.num_args; |
| 6548 | const end = @backingInt(zir_call.args_body[0]); |
| 6549 | break :blk zir_call.args_body[start..end]; |
| 6550 | } else blk: { |
| 6551 | const start = @backingInt(zir_call.args_body[real_arg_idx - 1]); |
| 6552 | const end = @backingInt(zir_call.args_body[real_arg_idx]); |
| 6553 | break :blk zir_call.args_body[start..end]; |
| 6554 | }; |
| 6555 | |
| 6556 | // Give the arg its result type |
| 6557 | const provide_param_ty: Type = maybe_param_ty orelse .generic_poison; |
| 6558 | sema.inst_map.putAssumeCapacity(zir_call.call_inst, Air.internedToRef(provide_param_ty.toIntern())); |
| 6559 | // Resolve the arg! |
| 6560 | break :arg try sema.resolveInlineBody(block, arg_body, zir_call.call_inst); |
| 6561 | }; |
| 6562 | |
| 6563 | if (block.isComptime() and !try sema.isComptimeKnown(uncoerced_arg)) { |
| 6564 | return sema.failWithNeededComptime(block, cai.argSrc(block, arg_index), null); |
| 6565 | } |
| 6566 | |
| 6567 | if (sema.typeOf(uncoerced_arg).classify(zcu) == .no_possible_value) { |
| 6568 | // This terminates resolution of arguments. The caller should |
| 6569 | // propagate this. |
| 6570 | return uncoerced_arg; |
| 6571 | } |
| 6572 | |
| 6573 | if (sema.typeOf(uncoerced_arg).isError(zcu)) { |
| 6574 | zir_call.any_arg_is_error.* = true; |
| 6575 | } |
| 6576 | |
| 6577 | break :arg_val uncoerced_arg; |
| 6578 | }, |
| 6579 | }; |
| 6580 | const param_ty = maybe_param_ty orelse { |
| 6581 | return sema.coerceVarArgParam(block, uncoerced_arg, cai.argSrc(block, arg_index)); |
| 6582 | }; |
| 6583 | switch (param_ty.toIntern()) { |
| 6584 | .generic_poison_type => return uncoerced_arg, |
| 6585 | else => return sema.coerceExtra( |
| 6586 | block, |
| 6587 | param_ty, |
| 6588 | uncoerced_arg, |
| 6589 | cai.argSrc(block, arg_index), |
| 6590 | .{ .param_src = .{ |
| 6591 | .func_inst = func_inst, |
| 6592 | .param_i = @intCast(arg_index), |
| 6593 | } }, |
| 6594 | ) catch |err| switch (err) { |
| 6595 | error.NotCoercible => unreachable, |
| 6596 | else => |e| return e, |
| 6597 | }, |
| 6598 | } |
| 6599 | } |
| 6600 | }; |
| 6601 | |
| 6602 | fn analyzeCall( |
| 6603 | sema: *Sema, |
| 6604 | block: *Block, |
| 6605 | callee: Air.Inst.Ref, |
| 6606 | func_ty: Type, |
| 6607 | func_src: LazySrcLoc, |
| 6608 | call_src: LazySrcLoc, |
| 6609 | modifier: std.lang.CallModifier, |
| 6610 | ensure_result_used: bool, |
| 6611 | args_info: CallArgsInfo, |
| 6612 | call_dbg_node: ?Zir.Inst.Index, |
| 6613 | operation: CallOperation, |
| 6614 | ) CompileError!Air.Inst.Ref { |
| 6615 | const pt = sema.pt; |
| 6616 | const zcu = pt.zcu; |
| 6617 | const comp = zcu.comp; |
| 6618 | const gpa = comp.gpa; |
| 6619 | const io = comp.io; |
| 6620 | const ip = &zcu.intern_pool; |
| 6621 | const arena = sema.arena; |
| 6622 | |
| 6623 | const maybe_func_inst = try sema.funcDeclSrcInst(callee); |
| 6624 | const func_ret_ty_src: LazySrcLoc = if (maybe_func_inst) |fn_decl_inst| .{ |
| 6625 | .base_node_inst = fn_decl_inst, |
| 6626 | .offset = .{ .node_offset_fn_type_ret_ty = .zero }, |
| 6627 | } else func_src; |
| 6628 | |
| 6629 | const func_ty_info = zcu.typeToFunc(func_ty).?; |
| 6630 | |
| 6631 | for (func_ty_info.param_types.get(ip), 0..) |param_ty_ip, param_index| { |
| 6632 | const arg_src = args_info.argSrc(block, param_index); |
| 6633 | try sema.ensureLayoutResolved(.fromInterned(param_ty_ip), arg_src, .init); |
| 6634 | } |
| 6635 | try sema.ensureLayoutResolved(.fromInterned(func_ty_info.return_type), func_ret_ty_src, .return_type); |
| 6636 | try sema.validateResolvedFuncType( |
| 6637 | block, |
| 6638 | func_ty_info.cc, |
| 6639 | func_ty_info.param_types.get(ip), |
| 6640 | .fromInterned(func_ty_info.return_type), |
| 6641 | func_src, |
| 6642 | maybe_func_inst, |
| 6643 | ); |
| 6644 | |
| 6645 | if (!callConvIsCallable(func_ty_info.cc)) { |
| 6646 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 6647 | const msg = try sema.errMsg( |
| 6648 | func_src, |
| 6649 | "unable to call function with calling convention '{s}'", |
| 6650 | .{@tagName(func_ty_info.cc)}, |
| 6651 | ); |
| 6652 | errdefer msg.destroy(gpa); |
| 6653 | if (maybe_func_inst) |func_inst| try sema.errNote(.{ |
| 6654 | .base_node_inst = func_inst, |
| 6655 | .offset = .nodeOffset(.zero), |
| 6656 | }, msg, "function declared here", .{}); |
| 6657 | break :msg msg; |
| 6658 | }); |
| 6659 | } |
| 6660 | |
| 6661 | const any_comptime_params = func_ty_info.comptime_bits != 0 or ct: { |
| 6662 | for (func_ty_info.param_types.get(ip)) |param_ty| { |
| 6663 | if (Type.fromInterned(param_ty).comptimeOnly(zcu)) break :ct true; |
| 6664 | } |
| 6665 | break :ct Type.fromInterned(func_ty_info.return_type).comptimeOnly(zcu); |
| 6666 | }; |
| 6667 | const any_generic_types = generic: { |
| 6668 | for (func_ty_info.param_types.get(ip)) |param_ty| { |
| 6669 | if (param_ty == .generic_poison_type) break :generic true; |
| 6670 | } |
| 6671 | const ret_ty: Type = .fromInterned(func_ty_info.return_type); |
| 6672 | if (ret_ty.toIntern() == .generic_poison_type) { |
| 6673 | break :generic true; |
| 6674 | } |
| 6675 | if (ret_ty.zigTypeTag(zcu) == .error_union and |
| 6676 | ret_ty.errorUnionPayload(zcu).toIntern() == .generic_poison_type) |
| 6677 | { |
| 6678 | break :generic true; |
| 6679 | } |
| 6680 | break :generic false; |
| 6681 | }; |
| 6682 | |
| 6683 | // We need this value in a few code paths. |
| 6684 | const callee_val = try sema.resolveDefinedValue(block, call_src, callee); |
| 6685 | // If the callee is a comptime-known *non-extern* function, `func_val` is populated. |
| 6686 | // If it is a comptime-known extern function, `func_is_extern` is set instead. |
| 6687 | // If it is not comptime-known, neither is set. |
| 6688 | const func_val: ?Value, const func_is_extern: bool = if (callee_val) |c| switch (ip.indexToKey(c.toIntern())) { |
| 6689 | .func => .{ c, false }, |
| 6690 | .ptr => switch (try sema.pointerDerefExtra(block, func_src, c)) { |
| 6691 | .runtime_load, .needed_well_defined, .out_of_bounds => .{ null, false }, |
| 6692 | .val => |pointee| switch (ip.indexToKey(pointee.toIntern())) { |
| 6693 | .func => .{ pointee, false }, |
| 6694 | .@"extern" => .{ null, true }, |
| 6695 | else => unreachable, |
| 6696 | }, |
| 6697 | }, |
| 6698 | .@"extern" => .{ null, true }, |
| 6699 | else => unreachable, |
| 6700 | } else .{ null, false }; |
| 6701 | |
| 6702 | if ((any_generic_types or any_comptime_params) and func_val == null) { |
| 6703 | return sema.failWithNeededComptime(block, func_src, .{ .simple = .generic_call_target }); |
| 6704 | } |
| 6705 | |
| 6706 | const inline_requested = func_ty_info.cc == .@"inline" or modifier == .always_inline; |
| 6707 | |
| 6708 | // If the modifier is `.compile_time`, or if the return type is non-generic and comptime-only, |
| 6709 | // then we need to enter a comptime scope *now* to make sure the args are comptime-eval'd. |
| 6710 | const old_block_comptime_reason = block.comptime_reason; |
| 6711 | defer block.comptime_reason = old_block_comptime_reason; |
| 6712 | if (!block.isComptime()) { |
| 6713 | if (modifier == .compile_time) { |
| 6714 | block.comptime_reason = .{ .reason = .{ |
| 6715 | .src = call_src, |
| 6716 | .r = .{ .simple = .comptime_call_modifier }, |
| 6717 | } }; |
| 6718 | } else if (!inline_requested) { |
| 6719 | const ret_ty: Type = .fromInterned(func_ty_info.return_type); |
| 6720 | if (ret_ty.comptimeOnly(zcu)) { |
| 6721 | block.comptime_reason = .{ .reason = .{ |
| 6722 | .src = call_src, |
| 6723 | .r = .{ .comptime_only_ret_ty = .{ |
| 6724 | .ty = .fromInterned(func_ty_info.return_type), |
| 6725 | .is_generic_inst = false, |
| 6726 | .ret_ty_src = func_ret_ty_src, |
| 6727 | } }, |
| 6728 | } }; |
| 6729 | } |
| 6730 | } |
| 6731 | } |
| 6732 | |
| 6733 | // This is whether we already know this to be an inline call. |
| 6734 | // If so, then comptime-known arguments are propagated when evaluating generic parameter/return types. |
| 6735 | // We might still learn that this call is inline *after* evaluating the generic return type. |
| 6736 | const early_known_inline = inline_requested or block.isComptime(); |
| 6737 | |
| 6738 | // These values are undefined if `func_val == null`. |
| 6739 | const fn_nav: InternPool.Nav, const fn_zir: Zir, const fn_tracked_inst: InternPool.TrackedInst.Index, const fn_zir_inst: Zir.Inst.Index, const fn_zir_info: Zir.FnInfo = if (func_val) |f| b: { |
| 6740 | const info = ip.indexToKey(f.toIntern()).func; |
| 6741 | const nav = ip.getNav(info.owner_nav); |
| 6742 | const resolved_func_inst = info.zir_body_inst.resolveFull(ip) orelse { |
| 6743 | return sema.failTransitive(.{ .lost_tracking = info.zir_body_inst }); |
| 6744 | }; |
| 6745 | const file = zcu.fileByIndex(resolved_func_inst.file); |
| 6746 | const zir_info = file.zir.?.getFnInfo(resolved_func_inst.inst); |
| 6747 | break :b .{ nav, file.zir.?, info.zir_body_inst, resolved_func_inst.inst, zir_info }; |
| 6748 | } else .{ undefined, undefined, undefined, undefined, undefined }; |
| 6749 | |
| 6750 | // This is the `inst_map` used when evaluating generic parameters and return types. |
| 6751 | var generic_inst_map: InstMap = .{}; |
| 6752 | defer generic_inst_map.deinit(gpa); |
| 6753 | if (any_generic_types) { |
| 6754 | try generic_inst_map.ensureSpaceForInstructions(gpa, fn_zir_info.param_body); |
| 6755 | } |
| 6756 | |
| 6757 | // This exists so that `generic_block` below can include a "called from here" note back to this |
| 6758 | // call site when analyzing generic parameter/return types. |
| 6759 | var generic_inlining: Block.Inlining = if (any_generic_types) .{ |
| 6760 | .call_block = block, |
| 6761 | .call_src = call_src, |
| 6762 | .func = func_val.?.toIntern(), |
| 6763 | .is_generic_instantiation = true, // this allows the following fields to be `undefined` |
| 6764 | .has_comptime_args = undefined, |
| 6765 | .comptime_result = undefined, |
| 6766 | .merges = undefined, |
| 6767 | } else undefined; |
| 6768 | |
| 6769 | // This is the block in which we evaluate generic function components: that is, generic parameter |
| 6770 | // types and the generic return type. This must not be used if the function is not generic. |
| 6771 | // `comptime_reason` is set as needed. |
| 6772 | var generic_block: Block = if (any_generic_types) .{ |
| 6773 | .parent = null, |
| 6774 | .sema = sema, |
| 6775 | .namespace = fn_nav.analysis.?.namespace, |
| 6776 | .instructions = .empty, |
| 6777 | .inlining = &generic_inlining, |
| 6778 | .src_base_inst = fn_nav.analysis.?.zir_index, |
| 6779 | .type_name_ctx = fn_nav.name, |
| 6780 | .type_fqn_ctx = fn_nav.fqn, |
| 6781 | } else undefined; |
| 6782 | defer if (any_generic_types) generic_block.instructions.deinit(gpa); |
| 6783 | |
| 6784 | if (any_generic_types) { |
| 6785 | // We certainly depend on the generic owner's signature! |
| 6786 | try sema.declareDependency(.{ .src_hash = fn_tracked_inst }); |
| 6787 | } |
| 6788 | |
| 6789 | const args = try arena.alloc(Air.Inst.Ref, args_info.count()); |
| 6790 | for (args, 0..) |*arg, arg_idx| { |
| 6791 | const param_ty: ?Type = if (arg_idx < func_ty_info.param_types.len) ty: { |
| 6792 | const raw = func_ty_info.param_types.get(ip)[arg_idx]; |
| 6793 | if (raw != .generic_poison_type) break :ty .fromInterned(raw); |
| 6794 | |
| 6795 | // We must discover the generic parameter type. |
| 6796 | assert(any_generic_types); |
| 6797 | const param_inst_idx = fn_zir_info.param_body[arg_idx]; |
| 6798 | const param_inst = fn_zir.instructions.get(@backingInt(param_inst_idx)); |
| 6799 | switch (param_inst.tag) { |
| 6800 | .param_anytype, .param_anytype_comptime => break :ty .generic_poison, |
| 6801 | .param, .param_comptime => {}, |
| 6802 | else => unreachable, |
| 6803 | } |
| 6804 | |
| 6805 | // Evaluate the generic parameter type. We need to switch out `sema.code` and `sema.inst_map`, because |
| 6806 | // the function definition may be in a different file to the call site. |
| 6807 | const old_code = sema.code; |
| 6808 | const old_inst_map = sema.inst_map; |
| 6809 | defer { |
| 6810 | generic_inst_map = sema.inst_map; |
| 6811 | sema.code = old_code; |
| 6812 | sema.inst_map = old_inst_map; |
| 6813 | } |
| 6814 | sema.code = fn_zir; |
| 6815 | sema.inst_map = generic_inst_map; |
| 6816 | |
| 6817 | const extra = sema.code.extraData(Zir.Inst.Param, param_inst.data.pl_tok.payload_index); |
| 6818 | const param_src = generic_block.tokenOffset(param_inst.data.pl_tok.src_tok); |
| 6819 | const body = sema.code.bodySlice(extra.end, extra.data.type.body_len); |
| 6820 | |
| 6821 | generic_block.comptime_reason = .{ .reason = .{ |
| 6822 | .r = .{ .simple = .fn_param_types }, |
| 6823 | .src = param_src, |
| 6824 | } }; |
| 6825 | |
| 6826 | const ty_ref = try sema.resolveInlineBody(&generic_block, body, param_inst_idx); |
| 6827 | const param_ty = try sema.analyzeAsType(&generic_block, param_src, .fn_param_types, ty_ref); |
| 6828 | |
| 6829 | if (!param_ty.isValidParamType(zcu)) { |
| 6830 | const opaque_str = if (param_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; |
| 6831 | return sema.fail(block, param_src, "parameter of {s}type '{f}' not allowed", .{ |
| 6832 | opaque_str, param_ty.fmt(pt), |
| 6833 | }); |
| 6834 | } |
| 6835 | |
| 6836 | break :ty param_ty; |
| 6837 | } else null; // vararg |
| 6838 | |
| 6839 | arg.* = try args_info.analyzeArg(sema, block, arg_idx, param_ty, func_ty_info, callee, maybe_func_inst); |
| 6840 | const arg_ty = sema.typeOf(arg.*); |
| 6841 | if (arg_ty.classify(zcu) == .no_possible_value) { |
| 6842 | return arg.*; // terminate analysis here |
| 6843 | } |
| 6844 | |
| 6845 | if (any_generic_types) { |
| 6846 | // We need to put the argument into `generic_inst_map` so that other parameters can refer to it. |
| 6847 | const param_inst_idx = fn_zir_info.param_body[arg_idx]; |
| 6848 | const declared_comptime = if (std.math.cast(u5, arg_idx)) |i| func_ty_info.paramIsComptime(i) else false; |
| 6849 | const param_is_comptime = declared_comptime or arg_ty.comptimeOnly(zcu); |
| 6850 | // We allow comptime-known arguments to propagate to generic types not only for comptime |
| 6851 | // parameters, but if the call is known to be inline. |
| 6852 | if (param_is_comptime or early_known_inline) { |
| 6853 | if (param_is_comptime and !try sema.isComptimeKnown(arg.*)) { |
| 6854 | assert(!declared_comptime); // `analyzeArg` handles this |
| 6855 | const arg_src = args_info.argSrc(block, arg_idx); |
| 6856 | const param_ty_src: LazySrcLoc = .{ |
| 6857 | .base_node_inst = maybe_func_inst.?, // the function is generic |
| 6858 | .offset = .{ .func_decl_param_ty = @intCast(arg_idx) }, |
| 6859 | }; |
| 6860 | return sema.failWithNeededComptime( |
| 6861 | block, |
| 6862 | arg_src, |
| 6863 | .{ .comptime_only_param_ty = .{ .ty = arg_ty, .param_ty_src = param_ty_src } }, |
| 6864 | ); |
| 6865 | } |
| 6866 | generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, arg.*); |
| 6867 | } else if (try arg_ty.onePossibleValue(pt)) |opv| { |
| 6868 | // The argument is comptime-known, even though this is a generic instantiation (as |
| 6869 | // opposed to an inline call), because the parameter type is OPV. |
| 6870 | generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, .fromValue(opv)); |
| 6871 | } else { |
| 6872 | // We need a dummy instruction with this type. It doesn't actually need to be in any block, |
| 6873 | // since it will never be referenced at runtime! |
| 6874 | const dummy: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len)); |
| 6875 | try sema.air_instructions.append(gpa, .{ .tag = .alloc, .data = .{ .ty = arg_ty } }); |
| 6876 | generic_inst_map.putAssumeCapacityNoClobber(param_inst_idx, dummy.toRef()); |
| 6877 | } |
| 6878 | } |
| 6879 | } |
| 6880 | |
| 6881 | // This return type is never generic poison. |
| 6882 | // However, if it has an IES, it is always associated with the callee value. |
| 6883 | // This is not correct for inline calls (where it should be an ad-hoc IES), nor for generic |
| 6884 | // calls (where it should be the IES of the instantiation). However, it's how we print this |
| 6885 | // in error messages. |
| 6886 | const resolved_ret_ty: Type = ret_ty: { |
| 6887 | if (!any_generic_types) break :ret_ty .fromInterned(func_ty_info.return_type); |
| 6888 | |
| 6889 | const maybe_poison_bare = if (fn_zir_info.inferred_error_set) maybe_poison: { |
| 6890 | break :maybe_poison ip.errorUnionPayload(func_ty_info.return_type); |
| 6891 | } else func_ty_info.return_type; |
| 6892 | |
| 6893 | if (maybe_poison_bare != .generic_poison_type) break :ret_ty .fromInterned(func_ty_info.return_type); |
| 6894 | |
| 6895 | // Evaluate the generic return type. As with generic parameters, we switch out `sema.code` and `sema.inst_map`. |
| 6896 | |
| 6897 | assert(any_generic_types); |
| 6898 | |
| 6899 | const old_code = sema.code; |
| 6900 | const old_inst_map = sema.inst_map; |
| 6901 | defer { |
| 6902 | generic_inst_map = sema.inst_map; |
| 6903 | sema.code = old_code; |
| 6904 | sema.inst_map = old_inst_map; |
| 6905 | } |
| 6906 | sema.code = fn_zir; |
| 6907 | sema.inst_map = generic_inst_map; |
| 6908 | |
| 6909 | generic_block.comptime_reason = .{ .reason = .{ |
| 6910 | .r = .{ .simple = .fn_ret_ty }, |
| 6911 | .src = func_ret_ty_src, |
| 6912 | } }; |
| 6913 | |
| 6914 | const bare_ty = if (fn_zir_info.ret_ty_ref != .none) bare: { |
| 6915 | assert(fn_zir_info.ret_ty_body.len == 0); |
| 6916 | break :bare try sema.resolveType(&generic_block, func_ret_ty_src, fn_zir_info.ret_ty_ref); |
| 6917 | } else bare: { |
| 6918 | assert(fn_zir_info.ret_ty_body.len != 0); |
| 6919 | const ty_ref = try sema.resolveInlineBody(&generic_block, fn_zir_info.ret_ty_body, fn_zir_inst); |
| 6920 | break :bare try sema.analyzeAsType(&generic_block, func_ret_ty_src, .fn_ret_ty, ty_ref); |
| 6921 | }; |
| 6922 | assert(bare_ty.toIntern() != .generic_poison_type); |
| 6923 | |
| 6924 | const full_ty = if (fn_zir_info.inferred_error_set) full: { |
| 6925 | try sema.validateErrorUnionPayloadType(block, bare_ty, func_ret_ty_src); |
| 6926 | const set = ip.errorUnionSet(func_ty_info.return_type); |
| 6927 | break :full try pt.errorUnionType(.fromInterned(set), bare_ty); |
| 6928 | } else bare_ty; |
| 6929 | |
| 6930 | if (!full_ty.isValidReturnType(zcu)) { |
| 6931 | const opaque_str = if (full_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; |
| 6932 | return sema.fail(block, func_ret_ty_src, "{s}return type '{f}' not allowed", .{ |
| 6933 | opaque_str, full_ty.fmt(pt), |
| 6934 | }); |
| 6935 | } |
| 6936 | |
| 6937 | break :ret_ty full_ty; |
| 6938 | }; |
| 6939 | try sema.ensureLayoutResolved(resolved_ret_ty, func_ret_ty_src, .return_type); |
| 6940 | |
| 6941 | // If we've discovered after evaluating arguments that a generic function instantiation is |
| 6942 | // comptime-only, then we can mark the block as comptime *now*. |
| 6943 | if (!inline_requested and !block.isComptime() and resolved_ret_ty.comptimeOnly(zcu)) { |
| 6944 | block.comptime_reason = .{ |
| 6945 | .reason = .{ |
| 6946 | .src = call_src, |
| 6947 | .r = .{ |
| 6948 | .comptime_only_ret_ty = .{ |
| 6949 | .ty = resolved_ret_ty, |
| 6950 | .is_generic_inst = true, |
| 6951 | .ret_ty_src = func_ret_ty_src, |
| 6952 | }, |
| 6953 | }, |
| 6954 | }, |
| 6955 | }; |
| 6956 | } |
| 6957 | |
| 6958 | if (call_dbg_node) |some| try sema.zirDbgStmt(block, some); |
| 6959 | |
| 6960 | const is_inline_call = block.isComptime() or inline_requested; |
| 6961 | |
| 6962 | if (!is_inline_call) { |
| 6963 | if (func_val == null and !func_is_extern and !block.is_typeof and zcu.getTarget().cpu.arch.isSpirV()) { |
| 6964 | return sema.fail(block, func_src, "SPIR-V does not support calling function pointers", .{}); |
| 6965 | } |
| 6966 | if (sema.func_is_naked) return sema.failWithOwnedErrorMsg(block, msg: { |
| 6967 | const msg = try sema.errMsg(call_src, "runtime {s} not allowed in naked function", .{@tagName(operation)}); |
| 6968 | errdefer msg.destroy(gpa); |
| 6969 | switch (operation) { |
| 6970 | .call, .@"@call", .@"@panic", .@"error return" => {}, |
| 6971 | .@"safety check" => try sema.errNote(call_src, msg, "use @setRuntimeSafety to disable runtime safety", .{}), |
| 6972 | } |
| 6973 | break :msg msg; |
| 6974 | }); |
| 6975 | if (func_ty_info.cc == .auto) { |
| 6976 | switch (sema.owner.unwrap()) { |
| 6977 | .@"comptime", |
| 6978 | .nav_ty, |
| 6979 | .nav_val, |
| 6980 | .type_layout, |
| 6981 | .struct_defaults, |
| 6982 | .memoized_state, |
| 6983 | => {}, |
| 6984 | |
| 6985 | .func => |owner_func| ip.funcSetHasErrorTrace(io, owner_func, true), |
| 6986 | } |
| 6987 | } |
| 6988 | for (args, 0..) |arg, arg_idx| { |
| 6989 | const arg_src = args_info.argSrc(block, arg_idx); |
| 6990 | try sema.validateRuntimeValue(block, arg_src, arg); |
| 6991 | } |
| 6992 | const runtime_func: Air.Inst.Ref, const runtime_args: []const Air.Inst.Ref = func: { |
| 6993 | if (!any_generic_types and !any_comptime_params) break :func .{ callee, args }; |
| 6994 | |
| 6995 | // Instantiate the generic function! |
| 6996 | |
| 6997 | // This may be an overestimate, but it's definitely sufficient. |
| 6998 | const max_runtime_args = args_info.count() - @popCount(func_ty_info.comptime_bits); |
| 6999 | var runtime_args: std.ArrayList(Air.Inst.Ref) = try .initCapacity(arena, max_runtime_args); |
| 7000 | var runtime_param_tys: std.ArrayList(InternPool.Index) = try .initCapacity(arena, max_runtime_args); |
| 7001 | |
| 7002 | const comptime_args = try arena.alloc(InternPool.Index, args_info.count()); |
| 7003 | |
| 7004 | var noalias_bits: u32 = 0; |
| 7005 | |
| 7006 | for (args, comptime_args, 0..) |arg, *comptime_arg, arg_idx| { |
| 7007 | const arg_ty = sema.typeOf(arg); |
| 7008 | |
| 7009 | const is_comptime = c: { |
| 7010 | if (std.math.cast(u5, arg_idx)) |i| { |
| 7011 | if (func_ty_info.paramIsComptime(i)) { |
| 7012 | break :c true; |
| 7013 | } |
| 7014 | } |
| 7015 | break :c arg_ty.comptimeOnly(zcu); |
| 7016 | }; |
| 7017 | const is_noalias = if (std.math.cast(u5, arg_idx)) |i| func_ty_info.paramIsNoalias(i) else false; |
| 7018 | |
| 7019 | if (is_comptime) { |
| 7020 | // We already emitted an error if the argument isn't comptime-known. |
| 7021 | comptime_arg.* = sema.resolveValue(arg).?.toIntern(); |
| 7022 | } else { |
| 7023 | comptime_arg.* = .none; |
| 7024 | if (is_noalias) { |
| 7025 | const runtime_idx = runtime_args.items.len; |
| 7026 | noalias_bits |= @as(u32, 1) << @intCast(runtime_idx); |
| 7027 | } |
| 7028 | runtime_args.appendAssumeCapacity(arg); |
| 7029 | runtime_param_tys.appendAssumeCapacity(arg_ty.toIntern()); |
| 7030 | } |
| 7031 | } |
| 7032 | |
| 7033 | const bare_ret_ty = if (fn_zir_info.inferred_error_set) t: { |
| 7034 | break :t resolved_ret_ty.errorUnionPayload(zcu); |
| 7035 | } else resolved_ret_ty; |
| 7036 | |
| 7037 | // We now need to actually create the function instance. |
| 7038 | const func_instance = try ip.getFuncInstance(gpa, io, pt.tid, .{ |
| 7039 | .param_types = runtime_param_tys.items, |
| 7040 | .noalias_bits = noalias_bits, |
| 7041 | .bare_return_type = bare_ret_ty.toIntern(), |
| 7042 | .is_noinline = func_ty_info.is_noinline, |
| 7043 | .inferred_error_set = fn_zir_info.inferred_error_set, |
| 7044 | .generic_owner = func_val.?.toIntern(), |
| 7045 | .comptime_args = comptime_args, |
| 7046 | .anon_name_counter = &zcu.anon_name_counter, |
| 7047 | }); |
| 7048 | if (zcu.comp.debugIncremental()) { |
| 7049 | const nav = ip.indexToKey(func_instance).func.owner_nav; |
| 7050 | const gop = try zcu.incremental_debug_state.navs.getOrPut(gpa, nav); |
| 7051 | if (!gop.found_existing) gop.value_ptr.* = zcu.generation; |
| 7052 | } |
| 7053 | |
| 7054 | // This call is problematic as it breaks guarantees about order-independency of semantic analysis. |
| 7055 | // These guarantees are necessary for incremental compilation and parallel semantic analysis. |
| 7056 | // See: #22410 |
| 7057 | zcu.funcInfo(func_instance).maxBranchQuota(ip, io, sema.branch_quota); |
| 7058 | |
| 7059 | break :func .{ Air.internedToRef(func_instance), runtime_args.items }; |
| 7060 | }; |
| 7061 | |
| 7062 | ref_func: { |
| 7063 | const runtime_func_val = sema.resolveValue(runtime_func) orelse break :ref_func; |
| 7064 | if (!ip.isFuncBody(runtime_func_val.toIntern())) break :ref_func; |
| 7065 | const orig_fn_index = ip.unwrapCoercedFunc(runtime_func_val.toIntern()); |
| 7066 | try sema.addReferenceEntry(block, call_src, .wrap(.{ .func = orig_fn_index })); |
| 7067 | try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index); |
| 7068 | } |
| 7069 | |
| 7070 | const call_tag: Air.Inst.Tag = switch (modifier) { |
| 7071 | .auto, .no_suspend => .call, |
| 7072 | .never_tail => .call_never_tail, |
| 7073 | .never_inline => .call_never_inline, |
| 7074 | .always_tail => .call_always_tail, |
| 7075 | |
| 7076 | .always_inline, |
| 7077 | .compile_time, |
| 7078 | => unreachable, |
| 7079 | }; |
| 7080 | |
| 7081 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).@"struct".field_names.len + runtime_args.len); |
| 7082 | const call_ref = try block.addInst(.{ |
| 7083 | .tag = call_tag, |
| 7084 | .data = .{ .pl_op = .{ |
| 7085 | .operand = runtime_func, |
| 7086 | .payload = sema.addExtraAssumeCapacity(Air.Call{ |
| 7087 | .args_len = @intCast(runtime_args.len), |
| 7088 | }), |
| 7089 | } }, |
| 7090 | }); |
| 7091 | sema.appendRefsAssumeCapacity(runtime_args); |
| 7092 | |
| 7093 | const actual_ret_ty = sema.typeOf(call_ref); |
| 7094 | |
| 7095 | if (ensure_result_used) { |
| 7096 | try sema.ensureResultUsed(block, actual_ret_ty, call_src); |
| 7097 | } |
| 7098 | |
| 7099 | if (call_tag == .call_always_tail) { |
| 7100 | const func_or_ptr_ty = sema.typeOf(runtime_func); |
| 7101 | const runtime_func_ty = switch (func_or_ptr_ty.zigTypeTag(zcu)) { |
| 7102 | .@"fn" => func_or_ptr_ty, |
| 7103 | .pointer => func_or_ptr_ty.childType(zcu), |
| 7104 | else => unreachable, |
| 7105 | }; |
| 7106 | const result = sema.coerceExtra(block, sema.fn_ret_ty, call_ref, call_src, .{ .is_ret = true }) catch |err| switch (err) { |
| 7107 | error.NotCoercible => unreachable, |
| 7108 | else => |e| return e, |
| 7109 | }; |
| 7110 | return sema.handleTailCall(block, call_src, runtime_func_ty, result); |
| 7111 | } |
| 7112 | |
| 7113 | switch (actual_ret_ty.classify(zcu)) { |
| 7114 | .no_possible_value => { |
| 7115 | const want_check = c: { |
| 7116 | if (!block.wantSafety()) break :c false; |
| 7117 | if (func_val != null) break :c false; |
| 7118 | break :c true; |
| 7119 | }; |
| 7120 | if (want_check) { |
| 7121 | try sema.safetyPanic(block, call_src, .noreturn_returned); |
| 7122 | } else { |
| 7123 | _ = try block.addNoOp(.unreach); |
| 7124 | } |
| 7125 | return .unreachable_value; |
| 7126 | }, |
| 7127 | .one_possible_value => { |
| 7128 | return .fromValue((try actual_ret_ty.onePossibleValue(pt)).?); |
| 7129 | }, |
| 7130 | .runtime => { |
| 7131 | return call_ref; |
| 7132 | }, |
| 7133 | .partially_comptime => unreachable, |
| 7134 | .fully_comptime => unreachable, |
| 7135 | } |
| 7136 | } |
| 7137 | |
| 7138 | // This is an inline call. The function must be comptime-known. We will analyze its body directly using this `Sema`. |
| 7139 | |
| 7140 | if (zcu.comp.time_report) |*tr| { |
| 7141 | if (!block.isComptime()) { |
| 7142 | tr.stats.n_inline_calls += 1; |
| 7143 | } |
| 7144 | } |
| 7145 | |
| 7146 | if (func_ty_info.is_noinline and !block.isComptime()) { |
| 7147 | return sema.fail(block, call_src, "inline call of noinline function", .{}); |
| 7148 | } |
| 7149 | |
| 7150 | const call_type: []const u8 = if (block.isComptime()) "comptime" else "inline"; |
| 7151 | if (modifier == .never_inline) { |
| 7152 | const msg, const fail_block = msg: { |
| 7153 | const msg = try sema.errMsg(call_src, "cannot perform {s} call with 'never_inline' modifier", .{call_type}); |
| 7154 | errdefer msg.destroy(gpa); |
| 7155 | const fail_block = if (block.isComptime()) b: { |
| 7156 | break :b try block.explainWhyBlockIsComptime(msg); |
| 7157 | } else block; |
| 7158 | break :msg .{ msg, fail_block }; |
| 7159 | }; |
| 7160 | return sema.failWithOwnedErrorMsg(fail_block, msg); |
| 7161 | } |
| 7162 | if (func_ty_info.is_var_args) { |
| 7163 | const msg, const fail_block = msg: { |
| 7164 | const msg = try sema.errMsg(call_src, "{s} call of variadic function", .{call_type}); |
| 7165 | errdefer msg.destroy(gpa); |
| 7166 | const fail_block = if (block.isComptime()) b: { |
| 7167 | break :b try block.explainWhyBlockIsComptime(msg); |
| 7168 | } else block; |
| 7169 | break :msg .{ msg, fail_block }; |
| 7170 | }; |
| 7171 | return sema.failWithOwnedErrorMsg(fail_block, msg); |
| 7172 | } |
| 7173 | if (func_val == null) { |
| 7174 | if (func_is_extern) { |
| 7175 | const msg, const fail_block = msg: { |
| 7176 | const msg = try sema.errMsg(call_src, "{s} call of extern function", .{call_type}); |
| 7177 | errdefer msg.destroy(gpa); |
| 7178 | const fail_block = if (block.isComptime()) b: { |
| 7179 | break :b try block.explainWhyBlockIsComptime(msg); |
| 7180 | } else block; |
| 7181 | break :msg .{ msg, fail_block }; |
| 7182 | }; |
| 7183 | return sema.failWithOwnedErrorMsg(fail_block, msg); |
| 7184 | } |
| 7185 | return sema.failWithNeededComptime( |
| 7186 | block, |
| 7187 | func_src, |
| 7188 | if (block.isComptime()) null else .{ .simple = .inline_call_target }, |
| 7189 | ); |
| 7190 | } |
| 7191 | |
| 7192 | if (block.isComptime()) { |
| 7193 | for (args, 0..) |arg, arg_idx| { |
| 7194 | if (!try sema.isComptimeKnown(arg)) { |
| 7195 | const arg_src = args_info.argSrc(block, arg_idx); |
| 7196 | return sema.failWithNeededComptime(block, arg_src, null); |
| 7197 | } |
| 7198 | } |
| 7199 | } |
| 7200 | |
| 7201 | // For an inline call, we depend on the source code of the whole function definition. |
| 7202 | try sema.declareDependency(.{ .src_hash = fn_nav.analysis.?.zir_index }); |
| 7203 | |
| 7204 | try sema.emitBackwardBranch(block, call_src); |
| 7205 | |
| 7206 | const want_memoize = m: { |
| 7207 | // TODO: comptime call memoization is currently not supported under incremental compilation |
| 7208 | // since dependencies are not marked on callers. If we want to keep this around (we should |
| 7209 | // check that it's worthwhile first!), each memoized call needs an `AnalUnit`. |
| 7210 | if (zcu.comp.config.incremental) break :m false; |
| 7211 | if (!block.isComptime()) break :m false; |
| 7212 | for (args) |a| { |
| 7213 | const val = sema.resolveValue(a).?; |
| 7214 | if (val.canMutateComptimeVarState(zcu)) break :m false; |
| 7215 | } |
| 7216 | break :m true; |
| 7217 | }; |
| 7218 | const memoized_arg_values: []const InternPool.Index = if (want_memoize) arg_vals: { |
| 7219 | const vals = try sema.arena.alloc(InternPool.Index, args.len); |
| 7220 | for (vals, args) |*v, a| v.* = sema.resolveValue(a).?.toIntern(); |
| 7221 | break :arg_vals vals; |
| 7222 | } else undefined; |
| 7223 | if (want_memoize) memoize: { |
| 7224 | const memoized_call_index = ip.getIfExists(.{ |
| 7225 | .memoized_call = .{ |
| 7226 | .func = func_val.?.toIntern(), |
| 7227 | .arg_values = memoized_arg_values, |
| 7228 | .result = undefined, // ignored by hash+eql |
| 7229 | .branch_count = undefined, // ignored by hash+eql |
| 7230 | .branch_quota = undefined, // ignored by hash+eql |
| 7231 | }, |
| 7232 | }) orelse break :memoize; |
| 7233 | const memoized_call = ip.indexToKey(memoized_call_index).memoized_call; |
| 7234 | if (sema.branch_count + memoized_call.branch_count > sema.branch_quota) { |
| 7235 | // Let the call play out se we get the correct source location for the |
| 7236 | // "evaluation exceeded X backwards branches" error. |
| 7237 | break :memoize; |
| 7238 | } |
| 7239 | sema.branch_count += memoized_call.branch_count; |
| 7240 | sema.branch_quota = @max(sema.branch_quota, memoized_call.branch_quota); |
| 7241 | sema.quota_request = @max(sema.quota_request, memoized_call.branch_quota); |
| 7242 | const result = Air.internedToRef(memoized_call.result); |
| 7243 | if (ensure_result_used) { |
| 7244 | try sema.ensureResultUsed(block, sema.typeOf(result), call_src); |
| 7245 | } |
| 7246 | return result; |
| 7247 | } |
| 7248 | |
| 7249 | var new_ies: InferredErrorSet = .{ .func = .none }; |
| 7250 | |
| 7251 | const old_inst_map = sema.inst_map; |
| 7252 | const old_code = sema.code; |
| 7253 | const old_func_index = sema.func_index; |
| 7254 | const old_fn_ret_ty = sema.fn_ret_ty; |
| 7255 | const old_fn_ret_ty_ies = sema.fn_ret_ty_ies; |
| 7256 | const old_error_return_trace_index_on_fn_entry = sema.error_return_trace_index_on_fn_entry; |
| 7257 | defer { |
| 7258 | sema.inst_map.deinit(gpa); |
| 7259 | sema.inst_map = old_inst_map; |
| 7260 | sema.code = old_code; |
| 7261 | sema.func_index = old_func_index; |
| 7262 | sema.fn_ret_ty = old_fn_ret_ty; |
| 7263 | sema.fn_ret_ty_ies = old_fn_ret_ty_ies; |
| 7264 | sema.error_return_trace_index_on_fn_entry = old_error_return_trace_index_on_fn_entry; |
| 7265 | } |
| 7266 | sema.inst_map = .{}; |
| 7267 | sema.code = fn_zir; |
| 7268 | sema.func_index = func_val.?.toIntern(); |
| 7269 | sema.fn_ret_ty = if (fn_zir_info.inferred_error_set) try pt.errorUnionType( |
| 7270 | .fromInterned(.adhoc_inferred_error_set_type), |
| 7271 | resolved_ret_ty.errorUnionPayload(zcu), |
| 7272 | ) else resolved_ret_ty; |
| 7273 | sema.fn_ret_ty_ies = if (fn_zir_info.inferred_error_set) &new_ies else null; |
| 7274 | |
| 7275 | try sema.inst_map.ensureSpaceForInstructions(gpa, fn_zir_info.param_body); |
| 7276 | for (args, 0..) |arg, arg_idx| { |
| 7277 | sema.inst_map.putAssumeCapacityNoClobber(fn_zir_info.param_body[arg_idx], arg); |
| 7278 | } |
| 7279 | |
| 7280 | const need_debug_scope = !block.isComptime() and !block.is_typeof and !block.ownerModule().strip; |
| 7281 | const block_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len)); |
| 7282 | try sema.air_instructions.append(gpa, .{ |
| 7283 | .tag = if (need_debug_scope) .dbg_inline_block else .block, |
| 7284 | .data = undefined, |
| 7285 | }); |
| 7286 | |
| 7287 | var inlining: Block.Inlining = .{ |
| 7288 | .call_block = block, |
| 7289 | .call_src = call_src, |
| 7290 | .func = func_val.?.toIntern(), |
| 7291 | .is_generic_instantiation = false, |
| 7292 | .has_comptime_args = for (args) |a| { |
| 7293 | if (try sema.isComptimeKnown(a)) break true; |
| 7294 | } else false, |
| 7295 | .comptime_result = undefined, |
| 7296 | .merges = .{ |
| 7297 | .block_inst = block_inst, |
| 7298 | .results = .empty, |
| 7299 | .br_list = .empty, |
| 7300 | .src_locs = .empty, |
| 7301 | }, |
| 7302 | }; |
| 7303 | var child_block: Block = .{ |
| 7304 | .parent = null, |
| 7305 | .sema = sema, |
| 7306 | .namespace = fn_nav.analysis.?.namespace, |
| 7307 | .instructions = .empty, |
| 7308 | .inlining = &inlining, |
| 7309 | .is_typeof = block.is_typeof, |
| 7310 | .comptime_reason = if (block.isComptime()) .inlining_parent else null, |
| 7311 | .error_return_trace_index = block.error_return_trace_index, |
| 7312 | .runtime_cond = block.runtime_cond, |
| 7313 | .runtime_loop = block.runtime_loop, |
| 7314 | .runtime_index = block.runtime_index, |
| 7315 | .src_base_inst = fn_nav.analysis.?.zir_index, |
| 7316 | .type_name_ctx = fn_nav.name, |
| 7317 | .type_fqn_ctx = fn_nav.fqn, |
| 7318 | }; |
| 7319 | |
| 7320 | defer child_block.instructions.deinit(gpa); |
| 7321 | defer inlining.merges.deinit(gpa); |
| 7322 | |
| 7323 | if (!inlining.has_comptime_args) { |
| 7324 | var block_it = block; |
| 7325 | while (block_it.inlining) |parent_inlining| { |
| 7326 | if (!parent_inlining.is_generic_instantiation and |
| 7327 | !parent_inlining.has_comptime_args and |
| 7328 | parent_inlining.func == func_val.?.toIntern()) |
| 7329 | { |
| 7330 | return sema.fail(block, call_src, "inline call is recursive", .{}); |
| 7331 | } |
| 7332 | block_it = parent_inlining.call_block; |
| 7333 | } |
| 7334 | } |
| 7335 | |
| 7336 | if (!block.isComptime() and !block.is_typeof) { |
| 7337 | const zir_tags = sema.code.instructions.items(.tag); |
| 7338 | const zir_datas = sema.code.instructions.items(.data); |
| 7339 | for (fn_zir_info.param_body) |inst| switch (zir_tags[@backingInt(inst)]) { |
| 7340 | .param, .param_comptime => { |
| 7341 | const extra = sema.code.extraData(Zir.Inst.Param, zir_datas[@backingInt(inst)].pl_tok.payload_index); |
| 7342 | const param_name = sema.code.nullTerminatedString(extra.data.name); |
| 7343 | const air_inst = sema.inst_map.get(inst).?; |
| 7344 | try sema.addDbgVar(&child_block, air_inst, .dbg_arg_inline, param_name); |
| 7345 | }, |
| 7346 | .param_anytype, .param_anytype_comptime => { |
| 7347 | const param_name = zir_datas[@backingInt(inst)].str_tok.get(sema.code); |
| 7348 | const air_inst = sema.inst_map.get(inst).?; |
| 7349 | try sema.addDbgVar(&child_block, air_inst, .dbg_arg_inline, param_name); |
| 7350 | }, |
| 7351 | else => {}, |
| 7352 | }; |
| 7353 | } |
| 7354 | |
| 7355 | child_block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(&child_block); |
| 7356 | // Save the error trace as our first action in the function |
| 7357 | // to match the behavior of runtime function calls. |
| 7358 | const error_return_trace_index_on_parent_fn_entry = sema.error_return_trace_index_on_fn_entry; |
| 7359 | sema.error_return_trace_index_on_fn_entry = child_block.error_return_trace_index; |
| 7360 | defer sema.error_return_trace_index_on_fn_entry = error_return_trace_index_on_parent_fn_entry; |
| 7361 | |
| 7362 | // We temporarily set `allow_memoize` to `true` to track this comptime call. |
| 7363 | // It is restored after the call finishes analysis, so that a caller may |
| 7364 | // know whether an in-progress call (containing this call) may be memoized. |
| 7365 | const old_allow_memoize = sema.allow_memoize; |
| 7366 | defer sema.allow_memoize = old_allow_memoize and sema.allow_memoize; |
| 7367 | sema.allow_memoize = true; |
| 7368 | |
| 7369 | const old_quota_request = sema.quota_request; |
| 7370 | defer sema.quota_request = @max(old_quota_request, sema.quota_request); |
| 7371 | sema.quota_request = 0; |
| 7372 | |
| 7373 | // Store the current eval branch count so we can find out how many eval branches |
| 7374 | // the comptime call caused. |
| 7375 | const old_branch_count = sema.branch_count; |
| 7376 | |
| 7377 | const result_raw: Air.Inst.Ref = result: { |
| 7378 | sema.analyzeFnBody(&child_block, fn_zir_info.body) catch |err| switch (err) { |
| 7379 | error.ComptimeReturn => break :result inlining.comptime_result, |
| 7380 | else => |e| return e, |
| 7381 | }; |
| 7382 | break :result try sema.resolveAnalyzedBlock(block, call_src, &child_block, &inlining.merges, need_debug_scope); |
| 7383 | }; |
| 7384 | |
| 7385 | if (sema.typeOf(result_raw).isNoReturn(zcu)) { |
| 7386 | return .unreachable_value; |
| 7387 | } |
| 7388 | |
| 7389 | const maybe_opv: Air.Inst.Ref = if (sema.resolveValue(result_raw)) |result_val| r: { |
| 7390 | const val_resolved = try sema.resolveAdHocInferredErrorSet(block, call_src, result_val.toIntern()); |
| 7391 | break :r Air.internedToRef(val_resolved); |
| 7392 | } else r: { |
| 7393 | const resolved_ty = try sema.resolveAdHocInferredErrorSetTy(block, call_src, sema.typeOf(result_raw).toIntern()); |
| 7394 | if (resolved_ty == .none) break :r result_raw; |
| 7395 | // TODO: mutate in place the previous instruction if possible |
| 7396 | // rather than adding a bitcast instruction. |
| 7397 | break :r try block.addTyOp(.error_cast, .fromInterned(resolved_ty), result_raw); |
| 7398 | }; |
| 7399 | |
| 7400 | if (block.isComptime()) { |
| 7401 | const result_val = sema.resolveValue(maybe_opv).?; |
| 7402 | if (want_memoize and sema.allow_memoize and !result_val.canMutateComptimeVarState(zcu)) { |
| 7403 | _ = try pt.intern(.{ .memoized_call = .{ |
| 7404 | .func = func_val.?.toIntern(), |
| 7405 | .arg_values = memoized_arg_values, |
| 7406 | .result = result_val.toIntern(), |
| 7407 | .branch_count = sema.branch_count - old_branch_count, |
| 7408 | .branch_quota = sema.quota_request, |
| 7409 | } }); |
| 7410 | } |
| 7411 | } |
| 7412 | |
| 7413 | if (ensure_result_used) { |
| 7414 | try sema.ensureResultUsed(block, sema.typeOf(maybe_opv), call_src); |
| 7415 | } |
| 7416 | |
| 7417 | return maybe_opv; |
| 7418 | } |
| 7419 | |
| 7420 | fn handleTailCall(sema: *Sema, block: *Block, call_src: LazySrcLoc, func_ty: Type, result: Air.Inst.Ref) !Air.Inst.Ref { |
| 7421 | const pt = sema.pt; |
| 7422 | const zcu = pt.zcu; |
| 7423 | const target = zcu.getTarget(); |
| 7424 | const backend = zcu.comp.getZigBackend(); |
| 7425 | if (!target_util.supportsTailCall(target, backend)) { |
| 7426 | return sema.fail(block, call_src, "unable to perform tail call: compiler backend '{s}' does not support tail calls on target architecture '{s}' with the selected CPU feature flags", .{ |
| 7427 | @tagName(backend), @tagName(target.cpu.arch), |
| 7428 | }); |
| 7429 | } |
| 7430 | const owner_func_ty: Type = .fromInterned(zcu.funcInfo(sema.owner.unwrap().func).ty); |
| 7431 | if (owner_func_ty.toIntern() != func_ty.toIntern()) { |
| 7432 | return sema.fail(block, call_src, "unable to perform tail call: type of function being called '{f}' does not match type of calling function '{f}'", .{ |
| 7433 | func_ty.fmt(pt), owner_func_ty.fmt(pt), |
| 7434 | }); |
| 7435 | } |
| 7436 | _ = try block.addUnOp(.ret, result); |
| 7437 | return .unreachable_value; |
| 7438 | } |
| 7439 | |
| 7440 | fn zirIntType(sema: *Sema, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7441 | const int_type = sema.code.instructions.items(.data)[@backingInt(inst)].int_type; |
| 7442 | const ty = try sema.pt.intType(int_type.signedness, int_type.bit_count); |
| 7443 | return Air.internedToRef(ty.toIntern()); |
| 7444 | } |
| 7445 | |
| 7446 | fn zirOptionalType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7447 | const pt = sema.pt; |
| 7448 | const zcu = pt.zcu; |
| 7449 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 7450 | const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); |
| 7451 | const child_type = try sema.resolveType(block, operand_src, inst_data.operand); |
| 7452 | if (child_type.zigTypeTag(zcu) == .@"opaque") { |
| 7453 | return sema.fail(block, operand_src, "opaque type '{f}' cannot be optional", .{child_type.fmt(pt)}); |
| 7454 | } else if (child_type.zigTypeTag(zcu) == .null) { |
| 7455 | return sema.fail(block, operand_src, "type '{f}' cannot be optional", .{child_type.fmt(pt)}); |
| 7456 | } |
| 7457 | const opt_type = try pt.optionalType(child_type.toIntern()); |
| 7458 | |
| 7459 | return Air.internedToRef(opt_type.toIntern()); |
| 7460 | } |
| 7461 | |
| 7462 | fn zirArrayInitElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7463 | const pt = sema.pt; |
| 7464 | const zcu = pt.zcu; |
| 7465 | const bin = sema.code.instructions.items(.data)[@backingInt(inst)].bin; |
| 7466 | const maybe_wrapped_indexable_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, bin.lhs) orelse return .generic_poison_type; |
| 7467 | const indexable_ty = maybe_wrapped_indexable_ty.optEuBaseType(zcu); |
| 7468 | assert(indexable_ty.isIndexable(zcu)); // validated by a previous instruction |
| 7469 | const elem_ty = switch (indexable_ty.zigTypeTag(zcu)) { |
| 7470 | .@"struct" => indexable_ty.fieldType(@backingInt(bin.rhs), zcu), |
| 7471 | else => indexable_ty.indexableElem(zcu), |
| 7472 | }; |
| 7473 | return .fromType(elem_ty); |
| 7474 | } |
| 7475 | |
| 7476 | fn zirElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7477 | const pt = sema.pt; |
| 7478 | const zcu = pt.zcu; |
| 7479 | const un_node = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 7480 | const maybe_wrapped_ptr_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, un_node.operand) orelse return .generic_poison_type; |
| 7481 | const ptr_ty = maybe_wrapped_ptr_ty.optEuBaseType(zcu); |
| 7482 | assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction |
| 7483 | const elem_ty = ptr_ty.childType(zcu); |
| 7484 | if (elem_ty.toIntern() == .anyopaque_type) { |
| 7485 | // The pointer's actual child type is effectively unknown, so it makes |
| 7486 | // sense to represent it with a generic poison. |
| 7487 | return .generic_poison_type; |
| 7488 | } |
| 7489 | return Air.internedToRef(ptr_ty.childType(zcu).toIntern()); |
| 7490 | } |
| 7491 | |
| 7492 | fn zirIndexablePtrElemType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7493 | const pt = sema.pt; |
| 7494 | const zcu = pt.zcu; |
| 7495 | const un_node = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 7496 | const src = block.nodeOffset(un_node.src_node); |
| 7497 | const ptr_ty = try sema.resolveTypeOrPoison(block, src, un_node.operand) orelse return .generic_poison_type; |
| 7498 | try sema.checkMemOperand(block, src, ptr_ty); |
| 7499 | const elem_ty = switch (ptr_ty.ptrSize(zcu)) { |
| 7500 | .slice, .many, .c => ptr_ty.childType(zcu), |
| 7501 | .one => ptr_ty.childType(zcu).childType(zcu), |
| 7502 | }; |
| 7503 | return Air.internedToRef(elem_ty.toIntern()); |
| 7504 | } |
| 7505 | |
| 7506 | fn zirSplatOpResultType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7507 | const pt = sema.pt; |
| 7508 | const zcu = pt.zcu; |
| 7509 | const un_node = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 7510 | |
| 7511 | const raw_ty = try sema.resolveTypeOrPoison(block, LazySrcLoc.unneeded, un_node.operand) orelse return .generic_poison_type; |
| 7512 | const vec_ty = raw_ty.optEuBaseType(zcu); |
| 7513 | |
| 7514 | switch (vec_ty.zigTypeTag(zcu)) { |
| 7515 | .array, .vector => {}, |
| 7516 | else => return sema.fail(block, block.nodeOffset(un_node.src_node), "expected array or vector type, found '{f}'", .{vec_ty.fmt(pt)}), |
| 7517 | } |
| 7518 | return Air.internedToRef(vec_ty.childType(zcu).toIntern()); |
| 7519 | } |
| 7520 | |
| 7521 | fn zirVectorType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7522 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 7523 | const len_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 7524 | const elem_type_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 7525 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 7526 | const len: u32 = @intCast(try sema.resolveInt(block, len_src, extra.lhs, .u32, .{ .simple = .vector_length })); |
| 7527 | const elem_type = try sema.resolveType(block, elem_type_src, extra.rhs); |
| 7528 | try sema.checkVectorElemType(block, elem_type_src, elem_type); |
| 7529 | const vector_type = try sema.pt.vectorType(.{ |
| 7530 | .len = len, |
| 7531 | .child = elem_type.toIntern(), |
| 7532 | }); |
| 7533 | return Air.internedToRef(vector_type.toIntern()); |
| 7534 | } |
| 7535 | |
| 7536 | fn zirArrayType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7537 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 7538 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 7539 | const len_src = block.src(.{ .node_offset_array_type_len = inst_data.src_node }); |
| 7540 | const elem_src = block.src(.{ .node_offset_array_type_elem = inst_data.src_node }); |
| 7541 | const len = try sema.resolveInt(block, len_src, extra.lhs, .usize, .{ .simple = .array_length }); |
| 7542 | const elem_type = try sema.resolveType(block, elem_src, extra.rhs); |
| 7543 | try sema.validateArrayElemType(block, elem_type, elem_src); |
| 7544 | const array_ty = try sema.pt.arrayType(.{ |
| 7545 | .len = len, |
| 7546 | .child = elem_type.toIntern(), |
| 7547 | }); |
| 7548 | |
| 7549 | return Air.internedToRef(array_ty.toIntern()); |
| 7550 | } |
| 7551 | |
| 7552 | fn zirArrayTypeSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7553 | const pt = sema.pt; |
| 7554 | const zcu = pt.zcu; |
| 7555 | const comp = zcu.comp; |
| 7556 | const gpa = comp.gpa; |
| 7557 | const io = comp.io; |
| 7558 | const ip = &zcu.intern_pool; |
| 7559 | |
| 7560 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 7561 | const extra = sema.code.extraData(Zir.Inst.ArrayTypeSentinel, inst_data.payload_index).data; |
| 7562 | const len_src = block.src(.{ .node_offset_array_type_len = inst_data.src_node }); |
| 7563 | const sentinel_src = block.src(.{ .node_offset_array_type_sentinel = inst_data.src_node }); |
| 7564 | const elem_src = block.src(.{ .node_offset_array_type_elem = inst_data.src_node }); |
| 7565 | const len = try sema.resolveInt(block, len_src, extra.len, .usize, .{ .simple = .array_length }); |
| 7566 | const elem_type = try sema.resolveType(block, elem_src, extra.elem_type); |
| 7567 | try sema.validateArrayElemType(block, elem_type, elem_src); |
| 7568 | const uncasted_sentinel = sema.resolveInst(extra.sentinel); |
| 7569 | const sentinel = try sema.coerce(block, elem_type, uncasted_sentinel, sentinel_src); |
| 7570 | const sentinel_val = try sema.resolveConstDefinedValue(block, sentinel_src, sentinel, .{ .simple = .array_sentinel }); |
| 7571 | if (sentinel_val.canMutateComptimeVarState(zcu)) { |
| 7572 | const sentinel_name = try ip.getOrPutString(gpa, io, pt.tid, "sentinel", .no_embedded_nulls); |
| 7573 | return sema.failWithContainsReferenceToComptimeVar(block, sentinel_src, sentinel_name, "sentinel", sentinel_val); |
| 7574 | } |
| 7575 | const array_ty = try pt.arrayType(.{ |
| 7576 | .len = len, |
| 7577 | .sentinel = sentinel_val.toIntern(), |
| 7578 | .child = elem_type.toIntern(), |
| 7579 | }); |
| 7580 | try sema.checkSentinelType(block, sentinel_src, elem_type); |
| 7581 | |
| 7582 | return Air.internedToRef(array_ty.toIntern()); |
| 7583 | } |
| 7584 | |
| 7585 | fn validateArrayElemType(sema: *Sema, block: *Block, elem_type: Type, elem_src: LazySrcLoc) !void { |
| 7586 | const pt = sema.pt; |
| 7587 | const zcu = pt.zcu; |
| 7588 | if (elem_type.zigTypeTag(zcu) == .@"opaque") { |
| 7589 | return sema.fail(block, elem_src, "array of opaque type '{f}' not allowed", .{elem_type.fmt(pt)}); |
| 7590 | } else if (elem_type.zigTypeTag(zcu) == .noreturn) { |
| 7591 | return sema.fail(block, elem_src, "array of 'noreturn' not allowed", .{}); |
| 7592 | } |
| 7593 | } |
| 7594 | |
| 7595 | fn zirAnyframeType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7596 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 7597 | if (true) { |
| 7598 | return sema.failWithUseOfAsync(block, block.nodeOffset(inst_data.src_node)); |
| 7599 | } |
| 7600 | const zcu = sema.zcu; |
| 7601 | const operand_src = block.src(.{ .node_offset_anyframe_type = inst_data.src_node }); |
| 7602 | const return_type = try sema.resolveType(block, operand_src, inst_data.operand); |
| 7603 | const anyframe_type = try zcu.anyframeType(return_type); |
| 7604 | |
| 7605 | return Air.internedToRef(anyframe_type.toIntern()); |
| 7606 | } |
| 7607 | |
| 7608 | fn zirErrorUnionType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7609 | const pt = sema.pt; |
| 7610 | const zcu = pt.zcu; |
| 7611 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 7612 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 7613 | const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); |
| 7614 | const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); |
| 7615 | const error_set = try sema.resolveType(block, lhs_src, extra.lhs); |
| 7616 | const payload = try sema.resolveType(block, rhs_src, extra.rhs); |
| 7617 | |
| 7618 | if (error_set.zigTypeTag(zcu) != .error_set) { |
| 7619 | return sema.fail(block, lhs_src, "expected error set type, found '{f}'", .{ |
| 7620 | error_set.fmt(pt), |
| 7621 | }); |
| 7622 | } |
| 7623 | try sema.validateErrorUnionPayloadType(block, payload, rhs_src); |
| 7624 | const err_union_ty = try pt.errorUnionType(error_set, payload); |
| 7625 | return Air.internedToRef(err_union_ty.toIntern()); |
| 7626 | } |
| 7627 | |
| 7628 | fn validateErrorUnionPayloadType(sema: *Sema, block: *Block, payload_ty: Type, payload_src: LazySrcLoc) !void { |
| 7629 | const pt = sema.pt; |
| 7630 | const zcu = pt.zcu; |
| 7631 | if (payload_ty.zigTypeTag(zcu) == .@"opaque") { |
| 7632 | return sema.fail(block, payload_src, "error union with payload of opaque type '{f}' not allowed", .{ |
| 7633 | payload_ty.fmt(pt), |
| 7634 | }); |
| 7635 | } else if (payload_ty.zigTypeTag(zcu) == .error_set) { |
| 7636 | return sema.fail(block, payload_src, "error union with payload of error set type '{f}' not allowed", .{ |
| 7637 | payload_ty.fmt(pt), |
| 7638 | }); |
| 7639 | } |
| 7640 | } |
| 7641 | |
| 7642 | fn zirErrorValue(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7643 | _ = block; |
| 7644 | |
| 7645 | const pt = sema.pt; |
| 7646 | const zcu = pt.zcu; |
| 7647 | const comp = zcu.comp; |
| 7648 | const gpa = comp.gpa; |
| 7649 | const io = comp.io; |
| 7650 | |
| 7651 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].str_tok; |
| 7652 | const name = try pt.zcu.intern_pool.getOrPutString( |
| 7653 | gpa, |
| 7654 | io, |
| 7655 | pt.tid, |
| 7656 | inst_data.get(sema.code), |
| 7657 | .no_embedded_nulls, |
| 7658 | ); |
| 7659 | _ = try pt.getErrorValue(name); |
| 7660 | // Create an error set type with only this error value, and return the value. |
| 7661 | const error_set_type = try pt.singleErrorSetType(name); |
| 7662 | return Air.internedToRef((try pt.intern(.{ .err = .{ |
| 7663 | .ty = error_set_type.toIntern(), |
| 7664 | .name = name, |
| 7665 | } }))); |
| 7666 | } |
| 7667 | |
| 7668 | fn zirIntFromError(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 7669 | const pt = sema.pt; |
| 7670 | const zcu = pt.zcu; |
| 7671 | const ip = &zcu.intern_pool; |
| 7672 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 7673 | const src = block.nodeOffset(extra.node); |
| 7674 | const operand_src = block.builtinCallArgSrc(extra.node, 0); |
| 7675 | const uncasted_operand = sema.resolveInst(extra.operand); |
| 7676 | const operand = try sema.coerce(block, .anyerror, uncasted_operand, operand_src); |
| 7677 | const err_int_ty = try pt.errorIntType(); |
| 7678 | |
| 7679 | if (sema.resolveValue(operand)) |val| { |
| 7680 | if (val.isUndef(zcu)) { |
| 7681 | return pt.undefRef(err_int_ty); |
| 7682 | } |
| 7683 | const err_name = ip.indexToKey(val.toIntern()).err.name; |
| 7684 | return Air.internedToRef((try pt.intValue( |
| 7685 | err_int_ty, |
| 7686 | try pt.getErrorValue(err_name), |
| 7687 | )).toIntern()); |
| 7688 | } |
| 7689 | |
| 7690 | const op_ty = sema.typeOf(uncasted_operand); |
| 7691 | switch (try sema.resolveInferredErrorSetTy(block, src, op_ty.toIntern())) { |
| 7692 | .anyerror_type => {}, |
| 7693 | else => |err_set_ty_index| { |
| 7694 | const names = ip.indexToKey(err_set_ty_index).error_set_type.names; |
| 7695 | switch (names.len) { |
| 7696 | 0 => return Air.internedToRef((try pt.intValue(err_int_ty, 0)).toIntern()), |
| 7697 | 1 => return pt.intRef(err_int_ty, ip.getErrorValueIfExists(names.get(ip)[0]).?), |
| 7698 | else => {}, |
| 7699 | } |
| 7700 | }, |
| 7701 | } |
| 7702 | |
| 7703 | try sema.requireRuntimeBlock(block, src, operand_src); |
| 7704 | return block.addTyOp(.int_from_error, err_int_ty, operand); |
| 7705 | } |
| 7706 | |
| 7707 | fn zirErrorFromInt(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 7708 | const pt = sema.pt; |
| 7709 | const zcu = pt.zcu; |
| 7710 | const io = zcu.comp.io; |
| 7711 | const ip = &zcu.intern_pool; |
| 7712 | |
| 7713 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 7714 | const src = block.nodeOffset(extra.node); |
| 7715 | const operand_src = block.builtinCallArgSrc(extra.node, 0); |
| 7716 | const uncasted_operand = sema.resolveInst(extra.operand); |
| 7717 | const err_int_ty = try pt.errorIntType(); |
| 7718 | const operand = try sema.coerce(block, err_int_ty, uncasted_operand, operand_src); |
| 7719 | |
| 7720 | if (try sema.resolveDefinedValue(block, operand_src, operand)) |value| { |
| 7721 | const int = try sema.usizeCast(block, operand_src, value.toUnsignedInt(zcu)); |
| 7722 | if (int > len: { |
| 7723 | const mutate = &ip.global_error_set.mutate; |
| 7724 | mutate.map.mutex.lockUncancelable(io); |
| 7725 | defer mutate.map.mutex.unlock(io); |
| 7726 | break :len mutate.names.len; |
| 7727 | } or int == 0) |
| 7728 | return sema.fail(block, operand_src, "integer value '{d}' represents no error", .{int}); |
| 7729 | return Air.internedToRef((try pt.intern(.{ .err = .{ |
| 7730 | .ty = .anyerror_type, |
| 7731 | .name = ip.global_error_set.shared.names.acquire().view().items(.@"0")[int - 1], |
| 7732 | } }))); |
| 7733 | } |
| 7734 | try sema.requireRuntimeBlock(block, src, operand_src); |
| 7735 | if (block.wantSafety()) { |
| 7736 | const is_lte_len = try block.addUnOp(.cmp_lte_errors_len, operand); |
| 7737 | const zero_val = Air.internedToRef((try pt.intValue(err_int_ty, 0)).toIntern()); |
| 7738 | const is_non_zero = try block.addBinOp(.cmp_neq, operand, zero_val); |
| 7739 | const ok = try block.addBinOp(.bit_and, is_lte_len, is_non_zero); |
| 7740 | try sema.addSafetyCheck(block, src, ok, .invalid_error_code); |
| 7741 | } |
| 7742 | return block.addTyOp(.error_from_int, .anyerror, operand); |
| 7743 | } |
| 7744 | |
| 7745 | fn zirMergeErrorSets(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7746 | const pt = sema.pt; |
| 7747 | const zcu = pt.zcu; |
| 7748 | const ip = &zcu.intern_pool; |
| 7749 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 7750 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 7751 | const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); |
| 7752 | const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); |
| 7753 | const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); |
| 7754 | const lhs = sema.resolveInst(extra.lhs); |
| 7755 | const rhs = sema.resolveInst(extra.rhs); |
| 7756 | if (sema.typeOf(lhs).zigTypeTag(zcu) == .bool and sema.typeOf(rhs).zigTypeTag(zcu) == .bool) { |
| 7757 | const msg = msg: { |
| 7758 | const msg = try sema.errMsg(lhs_src, "expected error set type, found 'bool'", .{}); |
| 7759 | errdefer msg.destroy(sema.gpa); |
| 7760 | try sema.errNote(src, msg, "'||' merges error sets; 'or' performs boolean OR", .{}); |
| 7761 | break :msg msg; |
| 7762 | }; |
| 7763 | return sema.failWithOwnedErrorMsg(block, msg); |
| 7764 | } |
| 7765 | const lhs_ty = try sema.analyzeAsType(block, lhs_src, .type, lhs); |
| 7766 | const rhs_ty = try sema.analyzeAsType(block, rhs_src, .type, rhs); |
| 7767 | if (lhs_ty.zigTypeTag(zcu) != .error_set) |
| 7768 | return sema.fail(block, lhs_src, "expected error set type, found '{f}'", .{lhs_ty.fmt(pt)}); |
| 7769 | if (rhs_ty.zigTypeTag(zcu) != .error_set) |
| 7770 | return sema.fail(block, rhs_src, "expected error set type, found '{f}'", .{rhs_ty.fmt(pt)}); |
| 7771 | |
| 7772 | // Anything merged with anyerror is anyerror. |
| 7773 | if (lhs_ty.toIntern() == .anyerror_type or rhs_ty.toIntern() == .anyerror_type) { |
| 7774 | return .anyerror_type; |
| 7775 | } |
| 7776 | |
| 7777 | switch (ip.indexToKey(lhs_ty.toIntern())) { |
| 7778 | .inferred_error_set_type => |func_index| { |
| 7779 | try sema.ensureFuncIesResolved(block, src, func_index); |
| 7780 | if (ip.funcIesResolvedUnordered(func_index) == .anyerror_type) return .anyerror_type; |
| 7781 | }, |
| 7782 | .error_set_type => {}, |
| 7783 | else => unreachable, |
| 7784 | } |
| 7785 | switch (ip.indexToKey(rhs_ty.toIntern())) { |
| 7786 | .inferred_error_set_type => |func_index| { |
| 7787 | try sema.ensureFuncIesResolved(block, src, func_index); |
| 7788 | if (ip.funcIesResolvedUnordered(func_index) == .anyerror_type) return .anyerror_type; |
| 7789 | }, |
| 7790 | .error_set_type => {}, |
| 7791 | else => unreachable, |
| 7792 | } |
| 7793 | |
| 7794 | const err_set_ty = try sema.errorSetMerge(lhs_ty, rhs_ty); |
| 7795 | return Air.internedToRef(err_set_ty.toIntern()); |
| 7796 | } |
| 7797 | |
| 7798 | fn zirEnumLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7799 | _ = block; |
| 7800 | |
| 7801 | const pt = sema.pt; |
| 7802 | const zcu = pt.zcu; |
| 7803 | const comp = zcu.comp; |
| 7804 | const gpa = comp.gpa; |
| 7805 | const io = comp.io; |
| 7806 | |
| 7807 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].str_tok; |
| 7808 | const name = inst_data.get(sema.code); |
| 7809 | return Air.internedToRef((try pt.intern(.{ |
| 7810 | .enum_literal = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls), |
| 7811 | }))); |
| 7812 | } |
| 7813 | |
| 7814 | fn zirDeclLiteral(sema: *Sema, block: *Block, inst: Zir.Inst.Index, do_coerce: bool) CompileError!Air.Inst.Ref { |
| 7815 | const pt = sema.pt; |
| 7816 | const zcu = pt.zcu; |
| 7817 | const comp = zcu.comp; |
| 7818 | const gpa = comp.gpa; |
| 7819 | const io = comp.io; |
| 7820 | |
| 7821 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 7822 | const src = block.nodeOffset(inst_data.src_node); |
| 7823 | const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; |
| 7824 | const name = try zcu.intern_pool.getOrPutString( |
| 7825 | gpa, |
| 7826 | io, |
| 7827 | pt.tid, |
| 7828 | sema.code.nullTerminatedString(extra.field_name_start), |
| 7829 | .no_embedded_nulls, |
| 7830 | ); |
| 7831 | const orig_ty: Type = try sema.resolveTypeOrPoison(block, src, extra.lhs) orelse .generic_poison; |
| 7832 | return sema.analyzeDeclLiteral(block, src, name, orig_ty, do_coerce); |
| 7833 | } |
| 7834 | |
| 7835 | fn analyzeDeclLiteral( |
| 7836 | sema: *Sema, |
| 7837 | block: *Block, |
| 7838 | src: LazySrcLoc, |
| 7839 | name: InternPool.NullTerminatedString, |
| 7840 | orig_ty: Type, |
| 7841 | do_coerce: bool, |
| 7842 | ) CompileError!Air.Inst.Ref { |
| 7843 | const pt = sema.pt; |
| 7844 | const zcu = pt.zcu; |
| 7845 | |
| 7846 | const uncoerced_result = res: { |
| 7847 | if (orig_ty.toIntern() == .generic_poison_type) { |
| 7848 | // Treat this as a normal enum literal. |
| 7849 | break :res Air.internedToRef(try pt.intern(.{ .enum_literal = name })); |
| 7850 | } |
| 7851 | |
| 7852 | var ty = orig_ty; |
| 7853 | while (true) switch (ty.zigTypeTag(zcu)) { |
| 7854 | .error_union => ty = ty.errorUnionPayload(zcu), |
| 7855 | .optional => ty = ty.optionalChild(zcu), |
| 7856 | .pointer => ty = if (ty.isSinglePointer(zcu)) ty.childType(zcu) else break, |
| 7857 | .enum_literal, .error_set => { |
| 7858 | // Treat this as a normal enum literal. |
| 7859 | break :res Air.internedToRef(try pt.intern(.{ .enum_literal = name })); |
| 7860 | }, |
| 7861 | else => break, |
| 7862 | }; |
| 7863 | |
| 7864 | break :res try sema.fieldVal(block, src, Air.internedToRef(ty.toIntern()), name, src); |
| 7865 | }; |
| 7866 | |
| 7867 | // Decl literals cannot lookup runtime `var`s. |
| 7868 | if (!try sema.isComptimeKnown(uncoerced_result)) { |
| 7869 | return sema.fail(block, src, "decl literal must be comptime-known", .{}); |
| 7870 | } |
| 7871 | |
| 7872 | if (do_coerce) { |
| 7873 | return sema.coerce(block, orig_ty, uncoerced_result, src); |
| 7874 | } else { |
| 7875 | return uncoerced_result; |
| 7876 | } |
| 7877 | } |
| 7878 | |
| 7879 | fn zirIntFromEnum(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7880 | const pt = sema.pt; |
| 7881 | const zcu = pt.zcu; |
| 7882 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 7883 | const src = block.nodeOffset(inst_data.src_node); |
| 7884 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 7885 | const operand = sema.resolveInst(inst_data.operand); |
| 7886 | const operand_ty = sema.typeOf(operand); |
| 7887 | |
| 7888 | const enum_tag: Air.Inst.Ref = switch (operand_ty.zigTypeTag(zcu)) { |
| 7889 | .@"enum" => operand, |
| 7890 | .@"union" => blk: { |
| 7891 | if (operand_ty.unionTagType(zcu) == null) { |
| 7892 | return sema.fail( |
| 7893 | block, |
| 7894 | operand_src, |
| 7895 | "untagged union '{f}' cannot be converted to integer", |
| 7896 | .{operand_ty.fmt(pt)}, |
| 7897 | ); |
| 7898 | } |
| 7899 | |
| 7900 | break :blk try sema.unionToTag(block, operand); |
| 7901 | }, |
| 7902 | else => { |
| 7903 | return sema.fail(block, operand_src, "expected enum or tagged union, found '{f}'", .{ |
| 7904 | operand_ty.fmt(pt), |
| 7905 | }); |
| 7906 | }, |
| 7907 | }; |
| 7908 | const enum_tag_ty = sema.typeOf(enum_tag); |
| 7909 | const int_tag_ty = enum_tag_ty.backingIntType(zcu); |
| 7910 | assert(int_tag_ty.classify(zcu) != .no_possible_value); |
| 7911 | |
| 7912 | if (sema.resolveValue(enum_tag)) |enum_tag_val| { |
| 7913 | if (enum_tag_val.isUndef(zcu)) return pt.undefRef(int_tag_ty); |
| 7914 | return .fromValue(enum_tag_val.backingInt(zcu)); |
| 7915 | } |
| 7916 | |
| 7917 | try sema.requireRuntimeBlock(block, src, operand_src); |
| 7918 | return block.addTyOp(.bit_cast, int_tag_ty, enum_tag); |
| 7919 | } |
| 7920 | |
| 7921 | fn zirEnumFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 7922 | const pt = sema.pt; |
| 7923 | const zcu = pt.zcu; |
| 7924 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 7925 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 7926 | const src = block.nodeOffset(inst_data.src_node); |
| 7927 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 7928 | const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@enumFromInt"); |
| 7929 | const operand = sema.resolveInst(extra.rhs); |
| 7930 | const operand_ty = sema.typeOf(operand); |
| 7931 | |
| 7932 | if (dest_ty.zigTypeTag(zcu) != .@"enum") { |
| 7933 | return sema.fail(block, src, "expected enum, found '{f}'", .{dest_ty.fmt(pt)}); |
| 7934 | } |
| 7935 | try sema.ensureLayoutResolved(dest_ty, src, .init); |
| 7936 | _ = try sema.checkIntType(block, operand_src, operand_ty); |
| 7937 | |
| 7938 | if (sema.resolveValue(operand)) |int_val| { |
| 7939 | if (dest_ty.isNonexhaustiveEnum(zcu)) { |
| 7940 | const int_tag_ty = dest_ty.backingIntType(zcu); |
| 7941 | if (int_val.intFitsInType(int_tag_ty, null, zcu)) { |
| 7942 | return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern()); |
| 7943 | } |
| 7944 | return sema.fail(block, src, "int value '{f}' out of range of non-exhaustive enum '{f}'", .{ |
| 7945 | int_val.fmtValueSema(pt, sema), dest_ty.fmt(pt), |
| 7946 | }); |
| 7947 | } |
| 7948 | if (int_val.isUndef(zcu)) { |
| 7949 | return sema.failWithUseOfUndef(block, operand_src, null); |
| 7950 | } |
| 7951 | if (!(try sema.enumHasInt(dest_ty, int_val))) { |
| 7952 | return sema.fail(block, src, "enum '{f}' has no tag with value '{f}'", .{ |
| 7953 | dest_ty.fmt(pt), int_val.fmtValueSema(pt, sema), |
| 7954 | }); |
| 7955 | } |
| 7956 | return Air.internedToRef((try pt.getCoerced(int_val, dest_ty)).toIntern()); |
| 7957 | } |
| 7958 | |
| 7959 | if (try dest_ty.onePossibleValue(pt)) |opv| { |
| 7960 | if (block.wantSafety()) { |
| 7961 | // The operand is runtime-known but the result is comptime-known. In |
| 7962 | // this case we still need a safety check. |
| 7963 | const expect_int = try pt.getCoerced(opv.backingInt(zcu), operand_ty); |
| 7964 | const ok = try block.addBinOp(.cmp_eq, operand, .fromValue(expect_int)); |
| 7965 | try sema.addSafetyCheck(block, src, ok, .invalid_enum_value); |
| 7966 | } |
| 7967 | return .fromValue(opv); |
| 7968 | } |
| 7969 | |
| 7970 | try sema.requireRuntimeBlock(block, src, operand_src); |
| 7971 | if (block.wantSafety()) { |
| 7972 | try sema.preparePanicId(src, .invalid_enum_value); |
| 7973 | return block.addTyOp(.int_cast_safe, dest_ty, operand); |
| 7974 | } |
| 7975 | return block.addTyOp(.int_cast, dest_ty, operand); |
| 7976 | } |
| 7977 | |
| 7978 | /// Pointer in, pointer out. |
| 7979 | fn zirOptionalPayloadPtr( |
| 7980 | sema: *Sema, |
| 7981 | block: *Block, |
| 7982 | inst: Zir.Inst.Index, |
| 7983 | safety_check: bool, |
| 7984 | ) CompileError!Air.Inst.Ref { |
| 7985 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 7986 | const optional_ptr = sema.resolveInst(inst_data.operand); |
| 7987 | const src = block.nodeOffset(inst_data.src_node); |
| 7988 | |
| 7989 | const ptr_ty = sema.typeOf(optional_ptr); |
| 7990 | assert(ptr_ty.zigTypeTag(sema.pt.zcu) == .pointer); |
| 7991 | try sema.ensureLayoutResolved(ptr_ty.childType(sema.pt.zcu), src, .ptr_access); |
| 7992 | |
| 7993 | return sema.analyzeOptionalPayloadPtr(block, src, optional_ptr, safety_check, false); |
| 7994 | } |
| 7995 | |
| 7996 | /// Asserts that the layout of the pointer child type is already resolved. |
| 7997 | fn analyzeOptionalPayloadPtr( |
| 7998 | sema: *Sema, |
| 7999 | block: *Block, |
| 8000 | src: LazySrcLoc, |
| 8001 | optional_ptr: Air.Inst.Ref, |
| 8002 | safety_check: bool, |
| 8003 | initializing: bool, |
| 8004 | ) CompileError!Air.Inst.Ref { |
| 8005 | const pt = sema.pt; |
| 8006 | const zcu = pt.zcu; |
| 8007 | const optional_ptr_ty = sema.typeOf(optional_ptr); |
| 8008 | assert(optional_ptr_ty.zigTypeTag(zcu) == .pointer); |
| 8009 | |
| 8010 | const opt_type = optional_ptr_ty.childType(zcu); |
| 8011 | opt_type.assertHasLayout(zcu); |
| 8012 | if (opt_type.zigTypeTag(zcu) != .optional) { |
| 8013 | return sema.failWithExpectedOptionalType(block, src, opt_type); |
| 8014 | } |
| 8015 | |
| 8016 | const child_type = opt_type.optionalChild(zcu); |
| 8017 | const child_pointer = try pt.ptrType(info: { |
| 8018 | var new = optional_ptr_ty.ptrInfo(zcu); |
| 8019 | new.child = child_type.toIntern(); |
| 8020 | break :info new; |
| 8021 | }); |
| 8022 | |
| 8023 | if (try sema.resolveDefinedValue(block, src, optional_ptr)) |ptr_val| { |
| 8024 | if (initializing) { |
| 8025 | if (sema.isComptimeMutablePtr(ptr_val)) { |
| 8026 | // Set the optional to non-null at comptime. |
| 8027 | // If the payload is OPV, we must use that value instead of undef. |
| 8028 | const payload_val = try child_type.onePossibleValue(pt) orelse try pt.undefValue(child_type); |
| 8029 | const opt_val = try pt.intern(.{ .opt = .{ |
| 8030 | .ty = opt_type.toIntern(), |
| 8031 | .val = payload_val.toIntern(), |
| 8032 | } }); |
| 8033 | try sema.storePtrVal(block, src, ptr_val, Value.fromInterned(opt_val), opt_type); |
| 8034 | } else { |
| 8035 | // Emit runtime instructions to set the optional non-null bit. |
| 8036 | const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr); |
| 8037 | try sema.checkKnownAllocPtr(block, optional_ptr, opt_payload_ptr); |
| 8038 | } |
| 8039 | return Air.internedToRef((try ptr_val.ptrOptPayload(pt)).toIntern()); |
| 8040 | } |
| 8041 | if (try sema.pointerDeref(block, src, ptr_val, optional_ptr_ty)) |val| { |
| 8042 | if (val.isNull(zcu)) { |
| 8043 | return sema.fail(block, src, "unable to unwrap null", .{}); |
| 8044 | } |
| 8045 | return Air.internedToRef((try ptr_val.ptrOptPayload(pt)).toIntern()); |
| 8046 | } |
| 8047 | } |
| 8048 | |
| 8049 | try sema.requireRuntimeBlock(block, src, null); |
| 8050 | if (safety_check and block.wantSafety()) { |
| 8051 | const is_non_null = try block.addUnOp(.is_non_null_ptr, optional_ptr); |
| 8052 | try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); |
| 8053 | } |
| 8054 | |
| 8055 | if (initializing) { |
| 8056 | const opt_payload_ptr = try block.addTyOp(.optional_payload_ptr_set, child_pointer, optional_ptr); |
| 8057 | try sema.checkKnownAllocPtr(block, optional_ptr, opt_payload_ptr); |
| 8058 | return opt_payload_ptr; |
| 8059 | } else { |
| 8060 | return block.addTyOp(.optional_payload_ptr, child_pointer, optional_ptr); |
| 8061 | } |
| 8062 | } |
| 8063 | |
| 8064 | /// Value in, value out. |
| 8065 | fn zirOptionalPayload( |
| 8066 | sema: *Sema, |
| 8067 | block: *Block, |
| 8068 | inst: Zir.Inst.Index, |
| 8069 | safety_check: bool, |
| 8070 | ) CompileError!Air.Inst.Ref { |
| 8071 | const pt = sema.pt; |
| 8072 | const zcu = pt.zcu; |
| 8073 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 8074 | const src = block.nodeOffset(inst_data.src_node); |
| 8075 | const operand = sema.resolveInst(inst_data.operand); |
| 8076 | const operand_ty = sema.typeOf(operand); |
| 8077 | const result_ty = switch (operand_ty.zigTypeTag(zcu)) { |
| 8078 | .optional => operand_ty.optionalChild(zcu), |
| 8079 | // TODO: https://github.com/ziglang/zig/issues/6597 will eliminate this branch so that we only need to handle optionals. |
| 8080 | .pointer => switch (operand_ty.ptrSize(zcu)) { |
| 8081 | .c => operand_ty, // if `ptr` is a `[*c]T`, then `ptr.?` is also a `[*c]T` |
| 8082 | .one, .many, .slice => return sema.failWithExpectedOptionalType(block, src, operand_ty), |
| 8083 | }, |
| 8084 | else => return sema.failWithExpectedOptionalType(block, src, operand_ty), |
| 8085 | }; |
| 8086 | |
| 8087 | ct: { |
| 8088 | if (try sema.resolveDefinedValue(block, src, operand)) |val| { |
| 8089 | if (val.optionalValue(zcu)) |payload| return .fromValue(payload); // comptime-known payload |
| 8090 | } else if (try sema.resolveIsNullFromType(block, src, operand_ty)) |is_null| { |
| 8091 | if (!is_null) break :ct; // fully runtime-known |
| 8092 | } else { |
| 8093 | break :ct; // fully runtime-known |
| 8094 | } |
| 8095 | // Comptime-known to be `null`. |
| 8096 | if (block.isComptime()) return sema.fail(block, src, "unable to unwrap null", .{}); |
| 8097 | if (safety_check and block.wantSafety()) { |
| 8098 | try sema.safetyPanic(block, src, .unwrap_null); |
| 8099 | } else { |
| 8100 | _ = try block.addNoOp(.unreach); |
| 8101 | } |
| 8102 | return .unreachable_value; |
| 8103 | } |
| 8104 | |
| 8105 | if (safety_check and block.wantSafety()) { |
| 8106 | const is_non_null = try block.addUnOp(.is_non_null, operand); |
| 8107 | try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); |
| 8108 | } |
| 8109 | |
| 8110 | // If the payload is OPV, we need the safety check but have a comptime-known result. |
| 8111 | if (try result_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 8112 | |
| 8113 | return block.addTyOp(.optional_payload, result_ty, operand); |
| 8114 | } |
| 8115 | |
| 8116 | /// Value in, value out |
| 8117 | fn zirErrUnionPayload( |
| 8118 | sema: *Sema, |
| 8119 | block: *Block, |
| 8120 | inst: Zir.Inst.Index, |
| 8121 | ) CompileError!Air.Inst.Ref { |
| 8122 | const pt = sema.pt; |
| 8123 | const zcu = pt.zcu; |
| 8124 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 8125 | const src = block.nodeOffset(inst_data.src_node); |
| 8126 | const operand = sema.resolveInst(inst_data.operand); |
| 8127 | const operand_src = src; |
| 8128 | const err_union_ty = sema.typeOf(operand); |
| 8129 | if (err_union_ty.zigTypeTag(zcu) != .error_union) { |
| 8130 | return sema.fail(block, operand_src, "expected error union type, found '{f}'", .{ |
| 8131 | err_union_ty.fmt(pt), |
| 8132 | }); |
| 8133 | } |
| 8134 | return sema.analyzeErrUnionPayload(block, src, err_union_ty, operand, operand_src, false); |
| 8135 | } |
| 8136 | |
| 8137 | fn analyzeErrUnionPayload( |
| 8138 | sema: *Sema, |
| 8139 | block: *Block, |
| 8140 | src: LazySrcLoc, |
| 8141 | err_union_ty: Type, |
| 8142 | operand: Air.Inst.Ref, |
| 8143 | operand_src: LazySrcLoc, |
| 8144 | safety_check: bool, |
| 8145 | ) CompileError!Air.Inst.Ref { |
| 8146 | const pt = sema.pt; |
| 8147 | const zcu = pt.zcu; |
| 8148 | const payload_ty = err_union_ty.errorUnionPayload(zcu); |
| 8149 | if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { |
| 8150 | if (val.getErrorName(zcu).unwrap()) |name| { |
| 8151 | return sema.failWithComptimeErrorRetTrace(block, src, name); |
| 8152 | } |
| 8153 | return Air.internedToRef(zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.payload); |
| 8154 | } |
| 8155 | |
| 8156 | try sema.requireRuntimeBlock(block, src, null); |
| 8157 | |
| 8158 | // If the error set has no fields then no safety check is needed. |
| 8159 | if (safety_check and block.wantSafety() and |
| 8160 | !err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) |
| 8161 | { |
| 8162 | try sema.addSafetyCheckUnwrapError(block, src, operand, .unwrap_errunion_err, .is_non_err); |
| 8163 | } |
| 8164 | |
| 8165 | if (try payload_ty.onePossibleValue(pt)) |payload_opv| { |
| 8166 | return .fromValue(payload_opv); |
| 8167 | } |
| 8168 | |
| 8169 | return block.addTyOp(.unwrap_errunion_payload, payload_ty, operand); |
| 8170 | } |
| 8171 | |
| 8172 | /// Pointer in, pointer out. |
| 8173 | fn zirErrUnionPayloadPtr( |
| 8174 | sema: *Sema, |
| 8175 | block: *Block, |
| 8176 | inst: Zir.Inst.Index, |
| 8177 | ) CompileError!Air.Inst.Ref { |
| 8178 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 8179 | const operand = sema.resolveInst(inst_data.operand); |
| 8180 | const src = block.nodeOffset(inst_data.src_node); |
| 8181 | |
| 8182 | const ptr_ty = sema.typeOf(operand); |
| 8183 | assert(ptr_ty.zigTypeTag(sema.pt.zcu) == .pointer); |
| 8184 | try sema.ensureLayoutResolved(ptr_ty.childType(sema.pt.zcu), src, .ptr_access); |
| 8185 | |
| 8186 | return sema.analyzeErrUnionPayloadPtr(block, src, operand, false, false); |
| 8187 | } |
| 8188 | |
| 8189 | /// Asserts that the layout of the pointer child type is already resolved. |
| 8190 | fn analyzeErrUnionPayloadPtr( |
| 8191 | sema: *Sema, |
| 8192 | block: *Block, |
| 8193 | src: LazySrcLoc, |
| 8194 | operand: Air.Inst.Ref, |
| 8195 | safety_check: bool, |
| 8196 | initializing: bool, |
| 8197 | ) CompileError!Air.Inst.Ref { |
| 8198 | const pt = sema.pt; |
| 8199 | const zcu = pt.zcu; |
| 8200 | const operand_ty = sema.typeOf(operand); |
| 8201 | assert(operand_ty.zigTypeTag(zcu) == .pointer); |
| 8202 | |
| 8203 | if (operand_ty.childType(zcu).zigTypeTag(zcu) != .error_union) { |
| 8204 | return sema.fail(block, src, "expected error union type, found '{f}'", .{ |
| 8205 | operand_ty.childType(zcu).fmt(pt), |
| 8206 | }); |
| 8207 | } |
| 8208 | |
| 8209 | const err_union_ty = operand_ty.childType(zcu); |
| 8210 | err_union_ty.assertHasLayout(zcu); |
| 8211 | const payload_ty = err_union_ty.errorUnionPayload(zcu); |
| 8212 | const operand_pointer_ty = try pt.ptrType(.{ |
| 8213 | .child = payload_ty.toIntern(), |
| 8214 | .flags = .{ |
| 8215 | .is_const = operand_ty.isConstPtr(zcu), |
| 8216 | .address_space = operand_ty.ptrAddressSpace(zcu), |
| 8217 | }, |
| 8218 | }); |
| 8219 | |
| 8220 | if (try sema.resolveDefinedValue(block, src, operand)) |ptr_val| { |
| 8221 | if (initializing) { |
| 8222 | if (sema.isComptimeMutablePtr(ptr_val)) { |
| 8223 | // Set the error union to non-error at comptime. |
| 8224 | // If the payload is OPV, we must use that value instead of undef. |
| 8225 | const payload_val = try payload_ty.onePossibleValue(pt) orelse try pt.undefValue(payload_ty); |
| 8226 | const eu_val = try pt.intern(.{ .error_union = .{ |
| 8227 | .ty = err_union_ty.toIntern(), |
| 8228 | .val = .{ .payload = payload_val.toIntern() }, |
| 8229 | } }); |
| 8230 | try sema.storePtrVal(block, src, ptr_val, Value.fromInterned(eu_val), err_union_ty); |
| 8231 | } else { |
| 8232 | // Emit runtime instructions to set the error union error code. |
| 8233 | try sema.requireRuntimeBlock(block, src, null); |
| 8234 | const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand); |
| 8235 | try sema.checkKnownAllocPtr(block, operand, eu_payload_ptr); |
| 8236 | } |
| 8237 | return Air.internedToRef((try ptr_val.ptrEuPayload(pt)).toIntern()); |
| 8238 | } |
| 8239 | if (try sema.pointerDeref(block, src, ptr_val, operand_ty)) |val| { |
| 8240 | if (val.getErrorName(zcu).unwrap()) |name| { |
| 8241 | return sema.failWithComptimeErrorRetTrace(block, src, name); |
| 8242 | } |
| 8243 | return Air.internedToRef((try ptr_val.ptrEuPayload(pt)).toIntern()); |
| 8244 | } |
| 8245 | } |
| 8246 | |
| 8247 | try sema.requireRuntimeBlock(block, src, null); |
| 8248 | |
| 8249 | // If the error set has no fields then no safety check is needed. |
| 8250 | if (safety_check and block.wantSafety() and |
| 8251 | !err_union_ty.errorUnionSet(zcu).errorSetIsEmpty(zcu)) |
| 8252 | { |
| 8253 | try sema.addSafetyCheckUnwrapError(block, src, operand, .unwrap_errunion_err_ptr, .is_non_err_ptr); |
| 8254 | } |
| 8255 | |
| 8256 | if (initializing) { |
| 8257 | const eu_payload_ptr = try block.addTyOp(.errunion_payload_ptr_set, operand_pointer_ty, operand); |
| 8258 | try sema.checkKnownAllocPtr(block, operand, eu_payload_ptr); |
| 8259 | return eu_payload_ptr; |
| 8260 | } else { |
| 8261 | return block.addTyOp(.unwrap_errunion_payload_ptr, operand_pointer_ty, operand); |
| 8262 | } |
| 8263 | } |
| 8264 | |
| 8265 | /// Value in, value out |
| 8266 | fn zirErrUnionCode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 8267 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 8268 | const src = block.nodeOffset(inst_data.src_node); |
| 8269 | const operand = sema.resolveInst(inst_data.operand); |
| 8270 | return sema.analyzeErrUnionCode(block, src, operand); |
| 8271 | } |
| 8272 | |
| 8273 | /// If `operand` is comptime-known, asserts that it is an error value rather than a payload value. |
| 8274 | fn analyzeErrUnionCode(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref { |
| 8275 | const pt = sema.pt; |
| 8276 | const zcu = pt.zcu; |
| 8277 | const operand_ty = sema.typeOf(operand); |
| 8278 | if (operand_ty.zigTypeTag(zcu) != .error_union) { |
| 8279 | return sema.fail(block, src, "expected error union type, found '{f}'", .{ |
| 8280 | operand_ty.fmt(pt), |
| 8281 | }); |
| 8282 | } |
| 8283 | |
| 8284 | const result_ty = operand_ty.errorUnionSet(zcu); |
| 8285 | |
| 8286 | if (try sema.resolveDefinedValue(block, src, operand)) |val| { |
| 8287 | if (val.getErrorName(zcu) == .none) return .unreachable_value; |
| 8288 | return Air.internedToRef((try pt.intern(.{ .err = .{ |
| 8289 | .ty = result_ty.toIntern(), |
| 8290 | .name = zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name, |
| 8291 | } }))); |
| 8292 | } |
| 8293 | |
| 8294 | try sema.requireRuntimeBlock(block, src, null); |
| 8295 | return block.addTyOp(.unwrap_errunion_err, result_ty, operand); |
| 8296 | } |
| 8297 | |
| 8298 | /// Pointer in, value out |
| 8299 | fn zirErrUnionCodePtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 8300 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 8301 | const src = block.nodeOffset(inst_data.src_node); |
| 8302 | const operand = sema.resolveInst(inst_data.operand); |
| 8303 | return sema.analyzeErrUnionCodePtr(block, src, operand); |
| 8304 | } |
| 8305 | |
| 8306 | fn analyzeErrUnionCodePtr(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref) CompileError!Air.Inst.Ref { |
| 8307 | const pt = sema.pt; |
| 8308 | const zcu = pt.zcu; |
| 8309 | const operand_ty = sema.typeOf(operand); |
| 8310 | assert(operand_ty.zigTypeTag(zcu) == .pointer); |
| 8311 | |
| 8312 | if (operand_ty.childType(zcu).zigTypeTag(zcu) != .error_union) { |
| 8313 | return sema.fail(block, src, "expected error union type, found '{f}'", .{ |
| 8314 | operand_ty.childType(zcu).fmt(pt), |
| 8315 | }); |
| 8316 | } |
| 8317 | |
| 8318 | const result_ty = operand_ty.childType(zcu).errorUnionSet(zcu); |
| 8319 | |
| 8320 | if (try sema.resolveDefinedValue(block, src, operand)) |pointer_val| { |
| 8321 | if (try sema.pointerDeref(block, src, pointer_val, operand_ty)) |val| { |
| 8322 | if (val.getErrorName(zcu) == .none) return .unreachable_value; |
| 8323 | return Air.internedToRef((try pt.intern(.{ .err = .{ |
| 8324 | .ty = result_ty.toIntern(), |
| 8325 | .name = zcu.intern_pool.indexToKey(val.toIntern()).error_union.val.err_name, |
| 8326 | } }))); |
| 8327 | } |
| 8328 | } |
| 8329 | |
| 8330 | try sema.requireRuntimeBlock(block, src, null); |
| 8331 | return block.addTyOp(.unwrap_errunion_err_ptr, result_ty, operand); |
| 8332 | } |
| 8333 | |
| 8334 | fn zirFunc( |
| 8335 | sema: *Sema, |
| 8336 | block: *Block, |
| 8337 | inst: Zir.Inst.Index, |
| 8338 | inferred_error_set: bool, |
| 8339 | ) CompileError!Air.Inst.Ref { |
| 8340 | const pt = sema.pt; |
| 8341 | const zcu = pt.zcu; |
| 8342 | const comp = zcu.comp; |
| 8343 | const gpa = comp.gpa; |
| 8344 | const io = comp.io; |
| 8345 | const ip = &zcu.intern_pool; |
| 8346 | |
| 8347 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 8348 | const extra = sema.code.extraData(Zir.Inst.Func, inst_data.payload_index); |
| 8349 | const target = zcu.getTarget(); |
| 8350 | const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = inst_data.src_node }); |
| 8351 | const src = block.nodeOffset(inst_data.src_node); |
| 8352 | |
| 8353 | var extra_index = extra.end; |
| 8354 | |
| 8355 | const ret_ty: Type = if (extra.data.ret_ty.is_generic) |
| 8356 | .generic_poison |
| 8357 | else switch (extra.data.ret_ty.body_len) { |
| 8358 | 0 => .void, |
| 8359 | 1 => blk: { |
| 8360 | const ret_ty_ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_index])); |
| 8361 | extra_index += 1; |
| 8362 | break :blk try sema.resolveType(block, ret_ty_src, ret_ty_ref); |
| 8363 | }, |
| 8364 | else => blk: { |
| 8365 | const ret_ty_body = sema.code.bodySlice(extra_index, extra.data.ret_ty.body_len); |
| 8366 | extra_index += ret_ty_body.len; |
| 8367 | |
| 8368 | const ret_ty_val = try sema.resolveGenericBody(block, ret_ty_src, ret_ty_body, inst, .type, .{ .simple = .fn_ret_ty }); |
| 8369 | break :blk ret_ty_val.toType(); |
| 8370 | }, |
| 8371 | }; |
| 8372 | |
| 8373 | var src_locs: Zir.Inst.Func.SrcLocs = undefined; |
| 8374 | const has_body = extra.data.body_len != 0; |
| 8375 | if (has_body) { |
| 8376 | extra_index += extra.data.body_len; |
| 8377 | src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; |
| 8378 | } |
| 8379 | |
| 8380 | // If this instruction has a body, then it's a function declaration, and we decide |
| 8381 | // the callconv based on whether it is exported. Otherwise, the callconv defaults |
| 8382 | // to `.auto`. |
| 8383 | const cc: std.lang.CallingConvention = if (has_body) cc: { |
| 8384 | const func_decl_nav = sema.owner.unwrap().nav_val; |
| 8385 | const fn_is_exported = exported: { |
| 8386 | const decl_ti = ip.getNav(func_decl_nav).analysis.?.zir_index; |
| 8387 | const decl_inst = decl_ti.resolve(ip) orelse { |
| 8388 | return sema.failTransitive(.{ .lost_tracking = decl_ti }); |
| 8389 | }; |
| 8390 | const zir_decl = sema.code.getDeclaration(decl_inst); |
| 8391 | break :exported zir_decl.linkage == .@"export"; |
| 8392 | }; |
| 8393 | if (fn_is_exported) { |
| 8394 | break :cc target.cCallingConvention() orelse { |
| 8395 | // This target has no default C calling convention. We sometimes trigger a similar |
| 8396 | // error by trying to evaluate `std.lang.CallingConvention.c`, so for consistency, |
| 8397 | // let's eval that now and just get the transitive error. (It's guaranteed to error |
| 8398 | // because it does the exact `cCallingConvention` call we just did.) |
| 8399 | const cc_type = try sema.getStdLangType(src, .CallingConvention); |
| 8400 | _ = try sema.namespaceLookupVal( |
| 8401 | block, |
| 8402 | LazySrcLoc.unneeded, |
| 8403 | cc_type.getNamespaceIndex(zcu), |
| 8404 | try ip.getOrPutString(gpa, io, pt.tid, "c", .no_embedded_nulls), |
| 8405 | ); |
| 8406 | // The above should have errored. |
| 8407 | @panic("std.lang is corrupt"); |
| 8408 | }; |
| 8409 | } else { |
| 8410 | break :cc .auto; |
| 8411 | } |
| 8412 | } else .auto; |
| 8413 | |
| 8414 | return sema.funcCommon( |
| 8415 | block, |
| 8416 | inst_data.src_node, |
| 8417 | inst, |
| 8418 | cc, |
| 8419 | ret_ty, |
| 8420 | false, |
| 8421 | inferred_error_set, |
| 8422 | has_body, |
| 8423 | src_locs, |
| 8424 | 0, |
| 8425 | false, |
| 8426 | ); |
| 8427 | } |
| 8428 | |
| 8429 | fn resolveGenericBody( |
| 8430 | sema: *Sema, |
| 8431 | block: *Block, |
| 8432 | src: LazySrcLoc, |
| 8433 | body: []const Zir.Inst.Index, |
| 8434 | func_inst: Zir.Inst.Index, |
| 8435 | dest_ty: Type, |
| 8436 | reason: ComptimeReason, |
| 8437 | ) !Value { |
| 8438 | assert(body.len != 0); |
| 8439 | |
| 8440 | // Make sure any nested param instructions don't clobber our work. |
| 8441 | const prev_params = block.params; |
| 8442 | block.params = .{}; |
| 8443 | defer { |
| 8444 | block.params = prev_params; |
| 8445 | } |
| 8446 | |
| 8447 | const uncasted = try sema.resolveInlineBody(block, body, func_inst); |
| 8448 | const result = try sema.coerce(block, dest_ty, uncasted, src); |
| 8449 | return sema.resolveConstDefinedValue(block, src, result, reason); |
| 8450 | } |
| 8451 | |
| 8452 | /// Given a library name, examines if the library name should end up in |
| 8453 | /// `link.File.Options.windows_libs` table (for example, libc is always |
| 8454 | /// specified via dedicated flag `link_libc` instead), |
| 8455 | /// and puts it there if it doesn't exist. |
| 8456 | /// It also dupes the library name which can then be saved as part of the |
| 8457 | /// respective `Decl` (either `ExternFn` or `Var`). |
| 8458 | /// The liveness of the duped library name is tied to liveness of `Zcu`. |
| 8459 | /// To deallocate, call `deinit` on the respective `Decl` (`ExternFn` or `Var`). |
| 8460 | pub fn handleExternLibName( |
| 8461 | sema: *Sema, |
| 8462 | block: *Block, |
| 8463 | src_loc: LazySrcLoc, |
| 8464 | lib_name: []const u8, |
| 8465 | ) CompileError!void { |
| 8466 | blk: { |
| 8467 | const pt = sema.pt; |
| 8468 | const zcu = pt.zcu; |
| 8469 | const comp = zcu.comp; |
| 8470 | const target = zcu.getTarget(); |
| 8471 | log.debug("extern fn symbol expected in lib '{s}'", .{lib_name}); |
| 8472 | if (std.zig.target.isLibCLibName(target, lib_name)) { |
| 8473 | if (!comp.config.link_libc) { |
| 8474 | return sema.fail( |
| 8475 | block, |
| 8476 | src_loc, |
| 8477 | "dependency on libc must be explicitly specified in the build command", |
| 8478 | .{}, |
| 8479 | ); |
| 8480 | } |
| 8481 | break :blk; |
| 8482 | } |
| 8483 | if (std.zig.target.isLibCxxLibName(target, lib_name)) { |
| 8484 | if (!comp.config.link_libcpp) return sema.fail( |
| 8485 | block, |
| 8486 | src_loc, |
| 8487 | "dependency on libc++ must be explicitly specified in the build command", |
| 8488 | .{}, |
| 8489 | ); |
| 8490 | break :blk; |
| 8491 | } |
| 8492 | if (mem.eql(u8, lib_name, "unwind")) { |
| 8493 | if (!comp.config.link_libunwind) return sema.fail( |
| 8494 | block, |
| 8495 | src_loc, |
| 8496 | "dependency on libunwind must be explicitly specified in the build command", |
| 8497 | .{}, |
| 8498 | ); |
| 8499 | break :blk; |
| 8500 | } |
| 8501 | if (!target_util.canDynamicLink(target)) { |
| 8502 | return sema.fail( |
| 8503 | block, |
| 8504 | src_loc, |
| 8505 | "dependency on dynamic library '{s}' cannot be satisfied because target does not support dynamic linking", |
| 8506 | .{lib_name}, |
| 8507 | ); |
| 8508 | } |
| 8509 | if (!block.ownerModule().pic and target_util.requiresPicForDynamicLink(target)) { |
| 8510 | return sema.fail( |
| 8511 | block, |
| 8512 | src_loc, |
| 8513 | "dependency on dynamic library '{s}' requires enabling Position Independent Code; fixed by '-l{s}' or '-fPIC'", |
| 8514 | .{ lib_name, lib_name }, |
| 8515 | ); |
| 8516 | } |
| 8517 | comp.addLinkLib(lib_name) catch |err| { |
| 8518 | return sema.fail(block, src_loc, "unable to add link lib '{s}': {s}", .{ |
| 8519 | lib_name, @errorName(err), |
| 8520 | }); |
| 8521 | }; |
| 8522 | } |
| 8523 | } |
| 8524 | |
| 8525 | /// These are calling conventions that are confirmed to work with variadic functions. |
| 8526 | /// Any calling conventions not included here are either not yet verified to work with variadic |
| 8527 | /// functions or there are no more other calling conventions that support variadic functions. |
| 8528 | const calling_conventions_supporting_var_args = [_]std.lang.CallingConvention.Tag{ |
| 8529 | .x86_16_cdecl, |
| 8530 | .x86_64_sysv, |
| 8531 | .x86_64_x32, |
| 8532 | .x86_64_win, |
| 8533 | .x86_sysv, |
| 8534 | .x86_win, |
| 8535 | .x86_mingw, |
| 8536 | .aarch64_aapcs, |
| 8537 | .aarch64_aapcs_darwin, |
| 8538 | .aarch64_aapcs_win, |
| 8539 | .aarch64_vfabi, |
| 8540 | .aarch64_vfabi_sve, |
| 8541 | .alpha_osf, |
| 8542 | .arm_aapcs, |
| 8543 | .arm_aapcs_vfp, |
| 8544 | .microblaze_std, |
| 8545 | .mips64_n64, |
| 8546 | .mips64_n32, |
| 8547 | .mips_o32, |
| 8548 | .riscv64_lp64, |
| 8549 | .riscv64_lp64_v, |
| 8550 | .riscv32_ilp32, |
| 8551 | .riscv32_ilp32_v, |
| 8552 | .sparc64_sysv, |
| 8553 | .sparc_sysv, |
| 8554 | .powerpc64_elf, |
| 8555 | .powerpc64_elf_altivec, |
| 8556 | .powerpc64_elf_v2, |
| 8557 | .powerpc_sysv, |
| 8558 | .powerpc_sysv_altivec, |
| 8559 | .powerpc_aix, |
| 8560 | .powerpc_aix_altivec, |
| 8561 | .wasm_mvp, |
| 8562 | .arc_sysv, |
| 8563 | .avr_gnu, |
| 8564 | .bpf_std, |
| 8565 | .csky_sysv, |
| 8566 | .hexagon_sysv, |
| 8567 | .hexagon_sysv_hvx, |
| 8568 | .hppa_elf, |
| 8569 | .hppa64_elf, |
| 8570 | .kvx_lp64, |
| 8571 | .kvx_ilp32, |
| 8572 | .lanai_sysv, |
| 8573 | .loongarch64_lp64, |
| 8574 | .loongarch32_ilp32, |
| 8575 | .m68k_sysv, |
| 8576 | .m68k_gnu, |
| 8577 | .m68k_rtd, |
| 8578 | .m88k_sysv, |
| 8579 | .msp430_eabi, |
| 8580 | .or1k_sysv, |
| 8581 | .s390x_sysv, |
| 8582 | .s390x_sysv_vx, |
| 8583 | .sh_gnu, |
| 8584 | .sh_renesas, |
| 8585 | .ve_sysv, |
| 8586 | .xcore_xs1, |
| 8587 | .xcore_xs2, |
| 8588 | .xtensa_call0, |
| 8589 | .xtensa_windowed, |
| 8590 | }; |
| 8591 | fn callConvSupportsVarArgs(cc: std.lang.CallingConvention.Tag) bool { |
| 8592 | return for (calling_conventions_supporting_var_args) |supported_cc| { |
| 8593 | if (cc == supported_cc) return true; |
| 8594 | } else false; |
| 8595 | } |
| 8596 | fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: std.lang.CallingConvention.Tag) CompileError!void { |
| 8597 | const CallingConventionsSupportingVarArgsList = struct { |
| 8598 | arch: std.Target.Cpu.Arch, |
| 8599 | pub fn format(ctx: @This(), w: *std.Io.Writer) std.Io.Writer.Error!void { |
| 8600 | var first = true; |
| 8601 | for (calling_conventions_supporting_var_args) |cc_inner| { |
| 8602 | for (std.Target.Cpu.Arch.fromCallingConvention(cc_inner)) |supported_arch| { |
| 8603 | if (supported_arch == ctx.arch) break; |
| 8604 | } else continue; // callconv not supported by this arch |
| 8605 | if (!first) { |
| 8606 | try w.writeAll(", "); |
| 8607 | } |
| 8608 | first = false; |
| 8609 | try w.print("'{s}'", .{@tagName(cc_inner)}); |
| 8610 | } |
| 8611 | } |
| 8612 | }; |
| 8613 | |
| 8614 | if (!callConvSupportsVarArgs(cc)) { |
| 8615 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 8616 | const msg = try sema.errMsg(src, "variadic function does not support '{s}' calling convention", .{@tagName(cc)}); |
| 8617 | errdefer msg.destroy(sema.gpa); |
| 8618 | const target = sema.pt.zcu.getTarget(); |
| 8619 | try sema.errNote(src, msg, "supported calling conventions: {f}", .{CallingConventionsSupportingVarArgsList{ .arch = target.cpu.arch }}); |
| 8620 | break :msg msg; |
| 8621 | }); |
| 8622 | } |
| 8623 | } |
| 8624 | |
| 8625 | fn checkParamType( |
| 8626 | sema: *Sema, |
| 8627 | block: *Block, |
| 8628 | param_idx: u32, |
| 8629 | param_ty: Type, |
| 8630 | param_is_comptime: bool, |
| 8631 | param_is_noalias: bool, |
| 8632 | param_src: LazySrcLoc, |
| 8633 | cc: std.lang.CallingConvention, |
| 8634 | ) CompileError!void { |
| 8635 | const pt = sema.pt; |
| 8636 | const zcu = pt.zcu; |
| 8637 | const target = zcu.getTarget(); |
| 8638 | |
| 8639 | if (!param_ty.isValidParamType(zcu)) { |
| 8640 | const opaque_str = if (param_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; |
| 8641 | return sema.fail(block, param_src, "parameter of {s}type '{f}' not allowed", .{ |
| 8642 | opaque_str, param_ty.fmt(pt), |
| 8643 | }); |
| 8644 | } |
| 8645 | if (!target_util.fnCallConvAllowsZigTypes(cc)) { |
| 8646 | if (param_is_comptime) { |
| 8647 | return sema.fail(block, param_src, "comptime parameters not allowed in function with calling convention '{t}'", .{cc}); |
| 8648 | } |
| 8649 | if (param_ty.isGenericPoison()) { |
| 8650 | return sema.fail(block, param_src, "generic parameters not allowed in function with calling convention '{t}'", .{cc}); |
| 8651 | } |
| 8652 | // The `validateExtern` check happens later, in `validateResolvedFuncType`. |
| 8653 | } |
| 8654 | switch (cc) { |
| 8655 | .x86_64_interrupt, .x86_interrupt => { |
| 8656 | const err_code_size = target.ptrBitWidth(); |
| 8657 | switch (param_idx) { |
| 8658 | 0 => if (param_ty.zigTypeTag(zcu) != .pointer) return sema.fail(block, param_src, "first parameter of function with '{t}' calling convention must be a pointer type", .{cc}), |
| 8659 | 1 => if (param_ty.bitSize(zcu) != err_code_size) return sema.fail(block, param_src, "second parameter of function with '{t}' calling convention must be a {d}-bit integer", .{ cc, err_code_size }), |
| 8660 | else => return sema.fail(block, param_src, "'{t}' calling convention supports up to 2 parameters, found {d}", .{ cc, param_idx + 1 }), |
| 8661 | } |
| 8662 | }, |
| 8663 | .arc_interrupt, |
| 8664 | .arm_interrupt, |
| 8665 | .microblaze_interrupt, |
| 8666 | .mips64_interrupt, |
| 8667 | .mips_interrupt, |
| 8668 | .riscv64_interrupt, |
| 8669 | .riscv32_interrupt, |
| 8670 | .sh_interrupt, |
| 8671 | .avr_interrupt, |
| 8672 | .csky_interrupt, |
| 8673 | .m68k_interrupt, |
| 8674 | .msp430_interrupt, |
| 8675 | .avr_signal, |
| 8676 | => return sema.fail(block, param_src, "parameters are not allowed with '{t}' calling convention", .{cc}), |
| 8677 | else => {}, |
| 8678 | } |
| 8679 | if (param_is_noalias and !param_ty.isGenericPoison() and !param_ty.isPtrAtRuntime(zcu) and !param_ty.isSliceAtRuntime(zcu)) { |
| 8680 | return sema.fail(block, param_src, "non-pointer parameter declared noalias", .{}); |
| 8681 | } |
| 8682 | switch (target.os.tag) { |
| 8683 | .vulkan, .opengl => if (cc != .@"inline" and param_ty.isPtrAtRuntime(zcu)) { |
| 8684 | switch (param_ty.ptrAddressSpace(zcu)) { |
| 8685 | .input, .output => |as| return sema.failWithOwnedErrorMsg(block, msg: { |
| 8686 | const msg = try sema.errMsg(param_src, "function parameter cannot be a pointer in '{s}' address space", .{@tagName(as)}); |
| 8687 | errdefer msg.destroy(sema.gpa); |
| 8688 | try sema.errNote(param_src, msg, "mark the function as 'inline' so the parameter is substituted at call sites", .{}); |
| 8689 | break :msg msg; |
| 8690 | }), |
| 8691 | else => {}, |
| 8692 | } |
| 8693 | }, |
| 8694 | else => {}, |
| 8695 | } |
| 8696 | } |
| 8697 | |
| 8698 | fn checkReturnTypeAndCallConv( |
| 8699 | sema: *Sema, |
| 8700 | block: *Block, |
| 8701 | bare_ret_ty: Type, |
| 8702 | ret_ty_src: LazySrcLoc, |
| 8703 | @"callconv": std.lang.CallingConvention, |
| 8704 | callconv_src: LazySrcLoc, |
| 8705 | /// non-`null` only if the function is varargs. |
| 8706 | opt_varargs_src: ?LazySrcLoc, |
| 8707 | inferred_error_set: bool, |
| 8708 | is_noinline: bool, |
| 8709 | ) CompileError!void { |
| 8710 | const pt = sema.pt; |
| 8711 | const zcu = pt.zcu; |
| 8712 | const target = zcu.getTarget(); |
| 8713 | if (opt_varargs_src) |varargs_src| { |
| 8714 | try sema.checkCallConvSupportsVarArgs(block, varargs_src, @"callconv"); |
| 8715 | } |
| 8716 | if (inferred_error_set and !bare_ret_ty.isGenericPoison()) { |
| 8717 | try sema.validateErrorUnionPayloadType(block, bare_ret_ty, ret_ty_src); |
| 8718 | } |
| 8719 | const ies_ret_ty_prefix: []const u8 = if (inferred_error_set) "!" else ""; |
| 8720 | if (!bare_ret_ty.isValidReturnType(zcu)) { |
| 8721 | const opaque_str = if (bare_ret_ty.zigTypeTag(zcu) == .@"opaque") "opaque " else ""; |
| 8722 | return sema.fail(block, ret_ty_src, "{s}return type '{s}{f}' not allowed", .{ |
| 8723 | opaque_str, ies_ret_ty_prefix, bare_ret_ty.fmt(pt), |
| 8724 | }); |
| 8725 | } |
| 8726 | if (!target_util.fnCallConvAllowsZigTypes(@"callconv")) { |
| 8727 | if (inferred_error_set) { |
| 8728 | return sema.fail(block, ret_ty_src, "return type '!{f}' not allowed in function with calling convention '{t}'", .{ bare_ret_ty.fmt(pt), @"callconv" }); |
| 8729 | } |
| 8730 | if (bare_ret_ty.isGenericPoison()) { |
| 8731 | return sema.fail(block, ret_ty_src, "generic return type not allowed in function with calling convention '{t}'", .{@"callconv"}); |
| 8732 | } |
| 8733 | // The `validateExtern` check happens later, in `validateResolvedFuncType`. |
| 8734 | } |
| 8735 | validate_incoming_stack_align: { |
| 8736 | const a: u64 = switch (@"callconv") { |
| 8737 | inline else => |payload| if (@TypeOf(payload) != void and @hasField(@TypeOf(payload), "incoming_stack_alignment")) |
| 8738 | payload.incoming_stack_alignment orelse break :validate_incoming_stack_align |
| 8739 | else |
| 8740 | break :validate_incoming_stack_align, |
| 8741 | }; |
| 8742 | if (!std.math.isPowerOfTwo(a)) { |
| 8743 | return sema.fail(block, callconv_src, "calling convention incoming stack alignment '{d}' is not a power of two", .{a}); |
| 8744 | } |
| 8745 | } |
| 8746 | switch (@"callconv") { |
| 8747 | .x86_64_interrupt, |
| 8748 | .x86_interrupt, |
| 8749 | .arm_interrupt, |
| 8750 | .mips64_interrupt, |
| 8751 | .mips_interrupt, |
| 8752 | .riscv64_interrupt, |
| 8753 | .riscv32_interrupt, |
| 8754 | .sh_interrupt, |
| 8755 | .arc_interrupt, |
| 8756 | .avr_interrupt, |
| 8757 | .csky_interrupt, |
| 8758 | .m68k_interrupt, |
| 8759 | .microblaze_interrupt, |
| 8760 | .msp430_interrupt, |
| 8761 | .avr_signal, |
| 8762 | => { |
| 8763 | const ret_ok = !inferred_error_set and switch (bare_ret_ty.toIntern()) { |
| 8764 | .void_type, .noreturn_type => true, |
| 8765 | else => false, |
| 8766 | }; |
| 8767 | if (!ret_ok) { |
| 8768 | return sema.fail(block, ret_ty_src, "function with calling convention '{t}' must return 'void' or 'noreturn'", .{@"callconv"}); |
| 8769 | } |
| 8770 | }, |
| 8771 | .@"inline" => if (is_noinline) { |
| 8772 | return sema.fail(block, callconv_src, "'noinline' function cannot have calling convention 'inline'", .{}); |
| 8773 | }, |
| 8774 | .spirv_fragment => |fragment| { |
| 8775 | if (fragment.pixel_centered_integer and target.os.tag != .opengl) { |
| 8776 | return sema.fail(block, callconv_src, "'pixel_centered_integer' is not supported on this target", .{}); |
| 8777 | } |
| 8778 | }, |
| 8779 | .spirv_kernel => |kernel| { |
| 8780 | if (kernel.x == 0 or kernel.y == 0 or kernel.z == 0) { |
| 8781 | return sema.fail(block, callconv_src, "kernel workgroup dimensions must be at least 1", .{}); |
| 8782 | } |
| 8783 | }, |
| 8784 | .spirv_task => |task| { |
| 8785 | if (task.x == 0 or task.y == 0 or task.z == 0) { |
| 8786 | return sema.fail(block, callconv_src, "kernel workgroup dimensions must be at least 1", .{}); |
| 8787 | } |
| 8788 | if (!target.cpu.has(.spirv, .mesh_shading_ext)) { |
| 8789 | return sema.fail(block, callconv_src, "calling convention '{t}' requires the 'mesh_shading_ext' feature", .{@"callconv"}); |
| 8790 | } |
| 8791 | }, |
| 8792 | .spirv_mesh => |mesh| { |
| 8793 | if (mesh.max_vertices == 0 or mesh.max_primitives == 0) { |
| 8794 | return sema.fail(block, callconv_src, "mesh shader 'max_vertices' and 'max_primitives' must be at least 1", .{}); |
| 8795 | } |
| 8796 | if (mesh.x == 0 or mesh.y == 0 or mesh.z == 0) { |
| 8797 | return sema.fail(block, callconv_src, "mesh shader workgroup dimensions must be at least 1", .{}); |
| 8798 | } |
| 8799 | if (!target.cpu.has(.spirv, .mesh_shading_ext)) { |
| 8800 | return sema.fail(block, callconv_src, "calling convention '{t}' requires the 'mesh_shading_ext' feature", .{@"callconv"}); |
| 8801 | } |
| 8802 | }, |
| 8803 | else => {}, |
| 8804 | } |
| 8805 | switch (zcu.callconvSupported(@"callconv")) { |
| 8806 | .ok => {}, |
| 8807 | .bad_arch => |allowed_archs| { |
| 8808 | const ArchListFormatter = struct { |
| 8809 | archs: []const std.Target.Cpu.Arch, |
| 8810 | pub fn format(formatter: @This(), w: *std.Io.Writer) std.Io.Writer.Error!void { |
| 8811 | for (formatter.archs, 0..) |arch, i| { |
| 8812 | if (i != 0) |
| 8813 | try w.writeAll(", "); |
| 8814 | try w.print("'{s}'", .{@tagName(arch)}); |
| 8815 | } |
| 8816 | } |
| 8817 | }; |
| 8818 | return sema.fail(block, callconv_src, "calling convention '{t}' only available on architectures {f}", .{ |
| 8819 | @"callconv", ArchListFormatter{ .archs = allowed_archs }, |
| 8820 | }); |
| 8821 | }, |
| 8822 | .bad_backend => |bad_backend| return sema.fail(block, callconv_src, "calling convention '{t}' not supported by compiler backend '{t}'", .{ |
| 8823 | @"callconv", bad_backend, |
| 8824 | }), |
| 8825 | } |
| 8826 | } |
| 8827 | |
| 8828 | /// To avoid forcing type layout resolution too quickly, some validation of function types cannot be |
| 8829 | /// performed when the type is first constructed, and instead must happen when either (a) a function |
| 8830 | /// with that type is declared, or (b) a function with that type is called. That validation is |
| 8831 | /// handled here. |
| 8832 | /// |
| 8833 | /// Asserts that all parameter types and return types have their layout fully resolved. |
| 8834 | fn validateResolvedFuncType( |
| 8835 | sema: *Sema, |
| 8836 | block: *Block, |
| 8837 | @"callconv": std.lang.CallingConvention, |
| 8838 | param_types: []const InternPool.Index, |
| 8839 | ret_ty: Type, |
| 8840 | src: LazySrcLoc, |
| 8841 | maybe_func_decl_inst: ?InternPool.TrackedInst.Index, |
| 8842 | ) SemaError!void { |
| 8843 | const pt = sema.pt; |
| 8844 | const zcu = pt.zcu; |
| 8845 | const gpa = zcu.comp.gpa; |
| 8846 | if (!target_util.fnCallConvAllowsZigTypes(@"callconv")) { |
| 8847 | // Check that all parameter types are extern-compatible. |
| 8848 | for (param_types, 0..) |param_ty_ip, param_index| { |
| 8849 | const param_ty: Type = .fromInterned(param_ty_ip); |
| 8850 | if (!param_ty.validateExtern(.param_ty, zcu)) { |
| 8851 | const param_src: LazySrcLoc = if (maybe_func_decl_inst) |inst| .{ |
| 8852 | .base_node_inst = inst, |
| 8853 | .offset = .{ .fn_proto_param = .{ |
| 8854 | .fn_proto_node_offset = .zero, |
| 8855 | .param_index = @intCast(param_index), |
| 8856 | } }, |
| 8857 | } else src; |
| 8858 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 8859 | const msg = try sema.errMsg(param_src, "parameter of type '{f}' not allowed in function with calling convention '{t}'", .{ |
| 8860 | param_ty.fmt(pt), @"callconv", |
| 8861 | }); |
| 8862 | errdefer msg.destroy(gpa); |
| 8863 | try sema.explainWhyTypeIsNotExtern(msg, param_src, param_ty, .param_ty); |
| 8864 | try sema.addDeclaredHereNote(msg, param_ty); |
| 8865 | break :msg msg; |
| 8866 | }); |
| 8867 | } |
| 8868 | } |
| 8869 | // Check that the return type is extern-compatible. |
| 8870 | if (!ret_ty.validateExtern(.ret_ty, zcu)) { |
| 8871 | const ret_ty_src: LazySrcLoc = if (maybe_func_decl_inst) |inst| .{ |
| 8872 | .base_node_inst = inst, |
| 8873 | .offset = .{ .node_offset_fn_type_ret_ty = .zero }, |
| 8874 | } else src; |
| 8875 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 8876 | const msg = try sema.errMsg(ret_ty_src, "return type '{f}' not allowed in function with calling convention '{t}'", .{ |
| 8877 | ret_ty.fmt(pt), @"callconv", |
| 8878 | }); |
| 8879 | errdefer msg.destroy(gpa); |
| 8880 | try sema.explainWhyTypeIsNotExtern(msg, ret_ty_src, ret_ty, .ret_ty); |
| 8881 | try sema.addDeclaredHereNote(msg, ret_ty); |
| 8882 | break :msg msg; |
| 8883 | }); |
| 8884 | } |
| 8885 | } |
| 8886 | } |
| 8887 | |
| 8888 | fn callConvIsCallable(cc: std.lang.CallingConvention.Tag) bool { |
| 8889 | return switch (cc) { |
| 8890 | .naked, |
| 8891 | |
| 8892 | .arc_interrupt, |
| 8893 | .arm_interrupt, |
| 8894 | .avr_interrupt, |
| 8895 | .avr_signal, |
| 8896 | .csky_interrupt, |
| 8897 | .m68k_interrupt, |
| 8898 | .microblaze_interrupt, |
| 8899 | .mips_interrupt, |
| 8900 | .mips64_interrupt, |
| 8901 | .msp430_interrupt, |
| 8902 | .riscv32_interrupt, |
| 8903 | .riscv64_interrupt, |
| 8904 | .sh_interrupt, |
| 8905 | .x86_interrupt, |
| 8906 | .x86_64_interrupt, |
| 8907 | |
| 8908 | .amdgcn_kernel, |
| 8909 | .nvptx_kernel, |
| 8910 | .spirv_kernel, |
| 8911 | .spirv_fragment, |
| 8912 | .spirv_vertex, |
| 8913 | .spirv_task, |
| 8914 | .spirv_mesh, |
| 8915 | => false, |
| 8916 | |
| 8917 | else => true, |
| 8918 | }; |
| 8919 | } |
| 8920 | |
| 8921 | fn checkMergeAllowed(sema: *Sema, block: *Block, src: LazySrcLoc, peer_ty: Type) !void { |
| 8922 | const pt = sema.pt; |
| 8923 | const zcu = pt.zcu; |
| 8924 | const target = zcu.getTarget(); |
| 8925 | |
| 8926 | if (!peer_ty.isPtrAtRuntime(zcu)) { |
| 8927 | return; |
| 8928 | } |
| 8929 | |
| 8930 | const as = peer_ty.ptrAddressSpace(zcu); |
| 8931 | if (!target_util.shouldBlockPointerOps(target, as)) { |
| 8932 | return; |
| 8933 | } |
| 8934 | |
| 8935 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 8936 | const msg = try sema.errMsg(src, "value with non-mergable pointer type '{f}' depends on runtime control flow", .{peer_ty.fmt(pt)}); |
| 8937 | errdefer msg.destroy(sema.gpa); |
| 8938 | |
| 8939 | const runtime_src = block.runtime_cond orelse block.runtime_loop.?; |
| 8940 | try sema.errNote(runtime_src, msg, "runtime control flow here", .{}); |
| 8941 | |
| 8942 | const backend = target_util.zigBackend(target, zcu.comp.config.use_llvm); |
| 8943 | try sema.errNote(src, msg, "pointers with address space '{s}' cannot be returned from a branch on target '{s}-{s}' by compiler backend '{s}'", .{ |
| 8944 | @tagName(as), |
| 8945 | @tagName(target.cpu.arch.family()), |
| 8946 | @tagName(target.os.tag), |
| 8947 | @tagName(backend), |
| 8948 | }); |
| 8949 | |
| 8950 | break :msg msg; |
| 8951 | }); |
| 8952 | } |
| 8953 | |
| 8954 | const Section = union(enum) { |
| 8955 | generic, |
| 8956 | default, |
| 8957 | explicit: InternPool.NullTerminatedString, |
| 8958 | }; |
| 8959 | |
| 8960 | fn funcCommon( |
| 8961 | sema: *Sema, |
| 8962 | block: *Block, |
| 8963 | src_node_offset: std.zig.Ast.Node.Offset, |
| 8964 | func_inst: Zir.Inst.Index, |
| 8965 | cc: std.lang.CallingConvention, |
| 8966 | /// this might be Type.generic_poison |
| 8967 | bare_return_type: Type, |
| 8968 | var_args: bool, |
| 8969 | inferred_error_set: bool, |
| 8970 | has_body: bool, |
| 8971 | src_locs: Zir.Inst.Func.SrcLocs, |
| 8972 | noalias_bits: u32, |
| 8973 | is_noinline: bool, |
| 8974 | ) CompileError!Air.Inst.Ref { |
| 8975 | const pt = sema.pt; |
| 8976 | const zcu = pt.zcu; |
| 8977 | const comp = zcu.comp; |
| 8978 | const gpa = comp.gpa; |
| 8979 | const io = comp.io; |
| 8980 | const ip = &zcu.intern_pool; |
| 8981 | |
| 8982 | const src = block.nodeOffset(src_node_offset); |
| 8983 | const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = src_node_offset }); |
| 8984 | const cc_src = block.src(.{ .node_offset_fn_type_cc = src_node_offset }); |
| 8985 | |
| 8986 | var comptime_bits: u32 = 0; |
| 8987 | for (block.params.items(.ty), block.params.items(.is_comptime), 0..) |param_ty_ip, param_is_comptime, i| { |
| 8988 | const param_ty: Type = .fromInterned(param_ty_ip); |
| 8989 | const is_noalias = blk: { |
| 8990 | const index = std.math.cast(u5, i) orelse break :blk false; |
| 8991 | break :blk @as(u1, @truncate(noalias_bits >> index)) != 0; |
| 8992 | }; |
| 8993 | const param_src = block.src(.{ .fn_proto_param = .{ |
| 8994 | .fn_proto_node_offset = src_node_offset, |
| 8995 | .param_index = @intCast(i), |
| 8996 | } }); |
| 8997 | if (param_is_comptime) { |
| 8998 | comptime_bits |= @as(u32, 1) << @intCast(i); // TODO: handle cast error |
| 8999 | } |
| 9000 | try sema.checkParamType( |
| 9001 | block, |
| 9002 | @intCast(i), |
| 9003 | param_ty, |
| 9004 | param_is_comptime, |
| 9005 | is_noalias, |
| 9006 | param_src, |
| 9007 | cc, |
| 9008 | ); |
| 9009 | } |
| 9010 | |
| 9011 | try sema.checkReturnTypeAndCallConv( |
| 9012 | block, |
| 9013 | bare_return_type, |
| 9014 | ret_ty_src, |
| 9015 | cc, |
| 9016 | cc_src, |
| 9017 | if (var_args) block.src(.{ .fn_proto_param = .{ |
| 9018 | .fn_proto_node_offset = src_node_offset, |
| 9019 | .param_index = @intCast(block.params.len), |
| 9020 | } }) else null, |
| 9021 | inferred_error_set, |
| 9022 | is_noinline, |
| 9023 | ); |
| 9024 | |
| 9025 | const param_types = block.params.items(.ty); |
| 9026 | |
| 9027 | if (has_body) { |
| 9028 | for (param_types, 0..) |param_ty_ip, param_index| { |
| 9029 | const param_ty: Type = .fromInterned(param_ty_ip); |
| 9030 | const param_src = block.src(.{ .fn_proto_param = .{ |
| 9031 | .fn_proto_node_offset = src_node_offset, |
| 9032 | .param_index = @intCast(param_index), |
| 9033 | } }); |
| 9034 | try sema.ensureLayoutResolved(param_ty, param_src, .parameter); |
| 9035 | } |
| 9036 | try sema.ensureLayoutResolved(bare_return_type, ret_ty_src, .return_type); |
| 9037 | try sema.validateResolvedFuncType( |
| 9038 | block, |
| 9039 | cc, |
| 9040 | param_types, |
| 9041 | bare_return_type, |
| 9042 | src, |
| 9043 | ip.getNav(sema.owner.unwrap().nav_val).srcInst(ip), |
| 9044 | ); |
| 9045 | } |
| 9046 | |
| 9047 | if (inferred_error_set) { |
| 9048 | assert(has_body); |
| 9049 | return .fromIntern(try ip.getFuncDeclIes(gpa, io, pt.tid, .{ |
| 9050 | .owner_nav = sema.owner.unwrap().nav_val, |
| 9051 | |
| 9052 | .param_types = param_types, |
| 9053 | .noalias_bits = noalias_bits, |
| 9054 | .comptime_bits = comptime_bits, |
| 9055 | .bare_return_type = bare_return_type.toIntern(), |
| 9056 | .cc = cc, |
| 9057 | .is_var_args = var_args, |
| 9058 | .is_noinline = is_noinline, |
| 9059 | |
| 9060 | .zir_body_inst = try block.trackZir(func_inst), |
| 9061 | .lbrace_line = src_locs.lbrace_line, |
| 9062 | .rbrace_line = src_locs.rbrace_line, |
| 9063 | .lbrace_column = @as(u16, @truncate(src_locs.columns)), |
| 9064 | .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)), |
| 9065 | })); |
| 9066 | } |
| 9067 | |
| 9068 | const func_ty = try ip.getFuncType(gpa, io, pt.tid, .{ |
| 9069 | .param_types = param_types, |
| 9070 | .noalias_bits = noalias_bits, |
| 9071 | .comptime_bits = comptime_bits, |
| 9072 | .return_type = bare_return_type.toIntern(), |
| 9073 | .cc = cc, |
| 9074 | .is_var_args = var_args, |
| 9075 | .is_noinline = is_noinline, |
| 9076 | }); |
| 9077 | |
| 9078 | if (has_body) { |
| 9079 | return .fromIntern(try ip.getFuncDecl(gpa, io, pt.tid, .{ |
| 9080 | .owner_nav = sema.owner.unwrap().nav_val, |
| 9081 | .ty = func_ty, |
| 9082 | .cc = cc, |
| 9083 | .is_noinline = is_noinline, |
| 9084 | .zir_body_inst = try block.trackZir(func_inst), |
| 9085 | .lbrace_line = src_locs.lbrace_line, |
| 9086 | .rbrace_line = src_locs.rbrace_line, |
| 9087 | .lbrace_column = @as(u16, @truncate(src_locs.columns)), |
| 9088 | .rbrace_column = @as(u16, @truncate(src_locs.columns >> 16)), |
| 9089 | })); |
| 9090 | } |
| 9091 | |
| 9092 | return .fromIntern(func_ty); |
| 9093 | } |
| 9094 | |
| 9095 | fn zirParam( |
| 9096 | sema: *Sema, |
| 9097 | block: *Block, |
| 9098 | inst: Zir.Inst.Index, |
| 9099 | comptime_syntax: bool, |
| 9100 | ) CompileError!void { |
| 9101 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_tok; |
| 9102 | const src = block.tokenOffset(inst_data.src_tok); |
| 9103 | const extra = sema.code.extraData(Zir.Inst.Param, inst_data.payload_index); |
| 9104 | const param_name: Zir.NullTerminatedString = extra.data.name; |
| 9105 | const body = sema.code.bodySlice(extra.end, extra.data.type.body_len); |
| 9106 | |
| 9107 | const param_ty: Type = if (extra.data.type.is_generic) .generic_poison else ty: { |
| 9108 | // Make sure any nested param instructions don't clobber our work. |
| 9109 | const prev_params = block.params; |
| 9110 | block.params = .{}; |
| 9111 | defer { |
| 9112 | block.params = prev_params; |
| 9113 | } |
| 9114 | |
| 9115 | const param_ty_inst = try sema.resolveInlineBody(block, body, inst); |
| 9116 | break :ty try sema.analyzeAsType(block, src, .fn_param_types, param_ty_inst); |
| 9117 | }; |
| 9118 | |
| 9119 | try block.params.append(sema.arena, .{ |
| 9120 | .ty = param_ty.toIntern(), |
| 9121 | .is_comptime = comptime_syntax, |
| 9122 | .name = param_name, |
| 9123 | }); |
| 9124 | } |
| 9125 | |
| 9126 | fn zirParamAnytype( |
| 9127 | sema: *Sema, |
| 9128 | block: *Block, |
| 9129 | inst: Zir.Inst.Index, |
| 9130 | comptime_syntax: bool, |
| 9131 | ) CompileError!void { |
| 9132 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].str_tok; |
| 9133 | const param_name: Zir.NullTerminatedString = inst_data.start; |
| 9134 | |
| 9135 | try block.params.append(sema.arena, .{ |
| 9136 | .ty = .generic_poison_type, |
| 9137 | .is_comptime = comptime_syntax, |
| 9138 | .name = param_name, |
| 9139 | }); |
| 9140 | } |
| 9141 | |
| 9142 | fn zirAsNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9143 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9144 | const src = block.nodeOffset(inst_data.src_node); |
| 9145 | const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data; |
| 9146 | return sema.analyzeAs(block, src, extra.dest_type, extra.operand, false); |
| 9147 | } |
| 9148 | |
| 9149 | fn zirAsShiftOperand(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9150 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9151 | const src = block.nodeOffset(inst_data.src_node); |
| 9152 | const extra = sema.code.extraData(Zir.Inst.As, inst_data.payload_index).data; |
| 9153 | return sema.analyzeAs(block, src, extra.dest_type, extra.operand, true); |
| 9154 | } |
| 9155 | |
| 9156 | fn analyzeAs( |
| 9157 | sema: *Sema, |
| 9158 | block: *Block, |
| 9159 | src: LazySrcLoc, |
| 9160 | zir_dest_type: Zir.Inst.Ref, |
| 9161 | zir_operand: Zir.Inst.Ref, |
| 9162 | no_cast_to_comptime_int: bool, |
| 9163 | ) CompileError!Air.Inst.Ref { |
| 9164 | const pt = sema.pt; |
| 9165 | const zcu = pt.zcu; |
| 9166 | const operand = sema.resolveInst(zir_operand); |
| 9167 | const dest_ty = try sema.resolveTypeOrPoison(block, src, zir_dest_type) orelse return operand; |
| 9168 | switch (dest_ty.zigTypeTag(zcu)) { |
| 9169 | .@"opaque" => return sema.fail(block, src, "cannot cast to opaque type '{f}'", .{dest_ty.fmt(pt)}), |
| 9170 | .noreturn => return sema.fail(block, src, "cannot cast to noreturn", .{}), |
| 9171 | else => {}, |
| 9172 | } |
| 9173 | |
| 9174 | const is_ret = if (zir_dest_type.toIndex()) |ptr_index| |
| 9175 | sema.code.instructions.items(.tag)[@backingInt(ptr_index)] == .ret_type |
| 9176 | else |
| 9177 | false; |
| 9178 | return sema.coerceExtra(block, dest_ty, operand, src, .{ .is_ret = is_ret, .no_cast_to_comptime_int = no_cast_to_comptime_int }) catch |err| switch (err) { |
| 9179 | error.NotCoercible => unreachable, |
| 9180 | else => |e| return e, |
| 9181 | }; |
| 9182 | } |
| 9183 | |
| 9184 | fn zirIntFromPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9185 | const pt = sema.pt; |
| 9186 | const zcu = pt.zcu; |
| 9187 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 9188 | const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 9189 | const operand = sema.resolveInst(inst_data.operand); |
| 9190 | const operand_ty = sema.typeOf(operand); |
| 9191 | const ptr_ty = operand_ty.scalarType(zcu); |
| 9192 | const is_vector = operand_ty.zigTypeTag(zcu) == .vector; |
| 9193 | if (!ptr_ty.isPtrAtRuntime(zcu)) { |
| 9194 | return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ty.fmt(pt)}); |
| 9195 | } |
| 9196 | |
| 9197 | const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined; |
| 9198 | const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .usize_type, .len = len }) else .usize; |
| 9199 | |
| 9200 | if (sema.resolveValue(operand)) |operand_val| ct: { |
| 9201 | if (!is_vector) { |
| 9202 | if (operand_val.isUndef(zcu)) { |
| 9203 | return .undef_usize; |
| 9204 | } |
| 9205 | const addr = operand_val.getUnsignedInt(zcu) orelse { |
| 9206 | // Wasn't an integer pointer. This is a runtime operation. |
| 9207 | break :ct; |
| 9208 | }; |
| 9209 | return Air.internedToRef((try pt.intValue( |
| 9210 | .usize, |
| 9211 | addr, |
| 9212 | )).toIntern()); |
| 9213 | } |
| 9214 | const new_elems = try sema.arena.alloc(InternPool.Index, len); |
| 9215 | for (new_elems, 0..) |*new_elem, i| { |
| 9216 | const ptr_val = try operand_val.elemValue(pt, i); |
| 9217 | if (ptr_val.isUndef(zcu)) { |
| 9218 | new_elem.* = .undef_usize; |
| 9219 | continue; |
| 9220 | } |
| 9221 | const addr = ptr_val.getUnsignedInt(zcu) orelse { |
| 9222 | // A vector element wasn't an integer pointer. This is a runtime operation. |
| 9223 | break :ct; |
| 9224 | }; |
| 9225 | new_elem.* = (try pt.intValue( |
| 9226 | .usize, |
| 9227 | addr, |
| 9228 | )).toIntern(); |
| 9229 | } |
| 9230 | return Air.internedToRef((try pt.aggregateValue(dest_ty, new_elems)).toIntern()); |
| 9231 | } |
| 9232 | try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), ptr_src); |
| 9233 | try sema.validateRuntimeValue(block, ptr_src, operand); |
| 9234 | try sema.checkLogicalPtrOperation(block, ptr_src, ptr_ty); |
| 9235 | return block.addTyOp(.int_from_ptr, dest_ty, operand); |
| 9236 | } |
| 9237 | |
| 9238 | fn zirFieldPtrLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9239 | const pt = sema.pt; |
| 9240 | const zcu = pt.zcu; |
| 9241 | const comp = zcu.comp; |
| 9242 | const gpa = comp.gpa; |
| 9243 | const io = comp.io; |
| 9244 | |
| 9245 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9246 | const src = block.nodeOffset(inst_data.src_node); |
| 9247 | const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node }); |
| 9248 | const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; |
| 9249 | const field_name = try zcu.intern_pool.getOrPutString( |
| 9250 | gpa, |
| 9251 | io, |
| 9252 | pt.tid, |
| 9253 | sema.code.nullTerminatedString(extra.field_name_start), |
| 9254 | .no_embedded_nulls, |
| 9255 | ); |
| 9256 | const object_ptr = sema.resolveInst(extra.lhs); |
| 9257 | return fieldPtrLoad(sema, block, src, object_ptr, field_name, field_name_src); |
| 9258 | } |
| 9259 | |
| 9260 | fn zirFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9261 | const pt = sema.pt; |
| 9262 | const zcu = pt.zcu; |
| 9263 | const comp = zcu.comp; |
| 9264 | const gpa = comp.gpa; |
| 9265 | const io = comp.io; |
| 9266 | |
| 9267 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9268 | const src = block.nodeOffset(inst_data.src_node); |
| 9269 | const field_name_src = block.src(.{ .node_offset_field_name = inst_data.src_node }); |
| 9270 | const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; |
| 9271 | const field_name = try zcu.intern_pool.getOrPutString( |
| 9272 | gpa, |
| 9273 | io, |
| 9274 | pt.tid, |
| 9275 | sema.code.nullTerminatedString(extra.field_name_start), |
| 9276 | .no_embedded_nulls, |
| 9277 | ); |
| 9278 | const object_ptr = sema.resolveInst(extra.lhs); |
| 9279 | return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false); |
| 9280 | } |
| 9281 | |
| 9282 | fn zirStructInitFieldPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9283 | const pt = sema.pt; |
| 9284 | const zcu = pt.zcu; |
| 9285 | const comp = zcu.comp; |
| 9286 | const gpa = comp.gpa; |
| 9287 | const io = comp.io; |
| 9288 | |
| 9289 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9290 | const src = block.nodeOffset(inst_data.src_node); |
| 9291 | const field_name_src = block.src(.{ .node_offset_field_name_init = inst_data.src_node }); |
| 9292 | const extra = sema.code.extraData(Zir.Inst.Field, inst_data.payload_index).data; |
| 9293 | const field_name = try zcu.intern_pool.getOrPutString( |
| 9294 | gpa, |
| 9295 | io, |
| 9296 | pt.tid, |
| 9297 | sema.code.nullTerminatedString(extra.field_name_start), |
| 9298 | .no_embedded_nulls, |
| 9299 | ); |
| 9300 | const object_ptr = sema.resolveInst(extra.lhs); |
| 9301 | const struct_ty = sema.typeOf(object_ptr).childType(zcu); |
| 9302 | switch (struct_ty.zigTypeTag(zcu)) { |
| 9303 | .@"struct", .@"union" => { |
| 9304 | return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, true); |
| 9305 | }, |
| 9306 | else => { |
| 9307 | return sema.failWithStructInitNotSupported(block, src, struct_ty); |
| 9308 | }, |
| 9309 | } |
| 9310 | } |
| 9311 | |
| 9312 | fn zirFieldPtrNamedLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9313 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9314 | const src = block.nodeOffset(inst_data.src_node); |
| 9315 | const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 9316 | const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; |
| 9317 | const object_ptr = sema.resolveInst(extra.lhs); |
| 9318 | const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name }); |
| 9319 | return fieldPtrLoad(sema, block, src, object_ptr, field_name, field_name_src); |
| 9320 | } |
| 9321 | |
| 9322 | fn zirFieldPtrNamed(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9323 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9324 | const src = block.nodeOffset(inst_data.src_node); |
| 9325 | const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 9326 | const extra = sema.code.extraData(Zir.Inst.FieldNamed, inst_data.payload_index).data; |
| 9327 | const object_ptr = sema.resolveInst(extra.lhs); |
| 9328 | const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name }); |
| 9329 | return sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false); |
| 9330 | } |
| 9331 | |
| 9332 | fn zirIntCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9333 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9334 | const src = block.nodeOffset(inst_data.src_node); |
| 9335 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 9336 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 9337 | |
| 9338 | const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intCast"); |
| 9339 | const operand = sema.resolveInst(extra.rhs); |
| 9340 | |
| 9341 | return sema.intCast(block, block.nodeOffset(inst_data.src_node), dest_ty, src, operand, operand_src); |
| 9342 | } |
| 9343 | |
| 9344 | fn intCast( |
| 9345 | sema: *Sema, |
| 9346 | block: *Block, |
| 9347 | src: LazySrcLoc, |
| 9348 | dest_ty: Type, |
| 9349 | dest_ty_src: LazySrcLoc, |
| 9350 | operand: Air.Inst.Ref, |
| 9351 | operand_src: LazySrcLoc, |
| 9352 | ) CompileError!Air.Inst.Ref { |
| 9353 | const pt = sema.pt; |
| 9354 | const zcu = pt.zcu; |
| 9355 | const operand_ty = sema.typeOf(operand); |
| 9356 | const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, dest_ty_src); |
| 9357 | const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); |
| 9358 | |
| 9359 | if (try sema.isComptimeKnown(operand)) { |
| 9360 | return sema.coerce(block, dest_ty, operand, operand_src); |
| 9361 | } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) { |
| 9362 | return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_int'", .{}); |
| 9363 | } |
| 9364 | |
| 9365 | try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, dest_ty_src, operand_src); |
| 9366 | const is_vector = dest_ty.zigTypeTag(zcu) == .vector; |
| 9367 | |
| 9368 | if (try dest_ty.onePossibleValue(pt)) |opv| { |
| 9369 | // requirement: intCast(u0, input) iff input == 0 |
| 9370 | if (block.wantSafety()) { |
| 9371 | try sema.requireRuntimeBlock(block, src, operand_src); |
| 9372 | const wanted_info = dest_scalar_ty.intInfo(zcu); |
| 9373 | const wanted_bits = wanted_info.bits; |
| 9374 | |
| 9375 | if (wanted_bits == 0) { |
| 9376 | const ok = if (is_vector) ok: { |
| 9377 | const zeros = try sema.splat(operand_ty, try pt.intValue(operand_scalar_ty, 0)); |
| 9378 | const zero_inst = Air.internedToRef(zeros.toIntern()); |
| 9379 | const is_in_range = try block.addCmpVector(operand, zero_inst, .eq); |
| 9380 | const all_in_range = try block.addReduce(is_in_range, .And); |
| 9381 | break :ok all_in_range; |
| 9382 | } else ok: { |
| 9383 | const zero_inst = Air.internedToRef((try pt.intValue(operand_ty, 0)).toIntern()); |
| 9384 | const is_in_range = try block.addBinOp(.cmp_eq, operand, zero_inst); |
| 9385 | break :ok is_in_range; |
| 9386 | }; |
| 9387 | try sema.addSafetyCheck(block, src, ok, .integer_out_of_bounds); |
| 9388 | } |
| 9389 | } |
| 9390 | |
| 9391 | return Air.internedToRef(opv.toIntern()); |
| 9392 | } |
| 9393 | |
| 9394 | try sema.requireRuntimeBlock(block, src, operand_src); |
| 9395 | if (block.wantSafety()) { |
| 9396 | try sema.preparePanicId(src, .integer_out_of_bounds); |
| 9397 | return block.addTyOp(.int_cast_safe, dest_ty, operand); |
| 9398 | } |
| 9399 | return block.addTyOp(.int_cast, dest_ty, operand); |
| 9400 | } |
| 9401 | |
| 9402 | fn zirBitcast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9403 | const pt = sema.pt; |
| 9404 | const zcu = pt.zcu; |
| 9405 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9406 | const src = block.nodeOffset(inst_data.src_node); |
| 9407 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 9408 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 9409 | |
| 9410 | const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@bitCast"); |
| 9411 | const operand = sema.resolveInst(extra.rhs); |
| 9412 | const operand_ty = sema.typeOf(operand); |
| 9413 | |
| 9414 | // Check for pointers before checking `hasBitRepresentation` so we can emit a better message for slices. |
| 9415 | switch (dest_ty.scalarType(zcu).zigTypeTag(zcu)) { |
| 9416 | .pointer, .optional => return sema.failWithOwnedErrorMsg(block, msg: { |
| 9417 | const msg = try sema.errMsg(src, "cannot @bitCast to '{f}'", .{dest_ty.fmt(pt)}); |
| 9418 | errdefer msg.destroy(sema.gpa); |
| 9419 | switch (operand_ty.zigTypeTag(zcu)) { |
| 9420 | .int, .comptime_int => try sema.errNote(src, msg, "use @ptrFromInt to cast from '{f}'", .{operand_ty.fmt(pt)}), |
| 9421 | .pointer => try sema.errNote(src, msg, "use @ptrCast to cast from '{f}'", .{operand_ty.fmt(pt)}), |
| 9422 | else => {}, |
| 9423 | } |
| 9424 | break :msg msg; |
| 9425 | }), |
| 9426 | .array => switch (dest_ty.arrayBase(zcu)[0].zigTypeTag(zcu)) { |
| 9427 | .pointer, .optional => return sema.fail(block, src, "cannot @bitCast to '{f}'", .{dest_ty.fmt(pt)}), |
| 9428 | else => {}, |
| 9429 | }, |
| 9430 | else => {}, |
| 9431 | } |
| 9432 | if (!dest_ty.hasBitRepresentation(zcu)) { |
| 9433 | return sema.fail(block, src, "cannot @bitCast to '{f}'", .{dest_ty.fmt(pt)}); |
| 9434 | } |
| 9435 | |
| 9436 | // Check for pointers before checking `hasBitRepresentation` so we can emit a better message for slices. |
| 9437 | switch (operand_ty.scalarType(zcu).zigTypeTag(zcu)) { |
| 9438 | .pointer, .optional => return sema.failWithOwnedErrorMsg(block, msg: { |
| 9439 | const msg = try sema.errMsg(operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)}); |
| 9440 | errdefer msg.destroy(sema.gpa); |
| 9441 | switch (dest_ty.zigTypeTag(zcu)) { |
| 9442 | .int, .comptime_int => try sema.errNote(operand_src, msg, "use @intFromPtr to cast to '{f}'", .{dest_ty.fmt(pt)}), |
| 9443 | .pointer => try sema.errNote(operand_src, msg, "use @ptrCast to cast to '{f}'", .{dest_ty.fmt(pt)}), |
| 9444 | else => {}, |
| 9445 | } |
| 9446 | break :msg msg; |
| 9447 | }), |
| 9448 | .array => switch (operand_ty.arrayBase(zcu)[0].zigTypeTag(zcu)) { |
| 9449 | .pointer, .optional => return sema.fail(block, operand_src, "cannot @bitCast from '{f}'", .{dest_ty.fmt(pt)}), |
| 9450 | else => {}, |
| 9451 | }, |
| 9452 | else => {}, |
| 9453 | } |
| 9454 | if (!operand_ty.hasBitRepresentation(zcu)) { |
| 9455 | return sema.fail(block, operand_src, "cannot @bitCast from '{f}'", .{operand_ty.fmt(pt)}); |
| 9456 | } |
| 9457 | |
| 9458 | operand_ty.assertHasLayout(zcu); |
| 9459 | try sema.ensureLayoutResolved(dest_ty, src, .init); |
| 9460 | |
| 9461 | const operand_bits = operand_ty.bitSize(zcu); |
| 9462 | const dest_bits = dest_ty.bitSize(zcu); |
| 9463 | if (operand_bits != dest_bits) { |
| 9464 | return sema.fail(block, src, "@bitCast size mismatch: destination type '{f}' has {d} bits but source type '{f}' has {d} bits", .{ |
| 9465 | dest_ty.fmt(pt), |
| 9466 | dest_bits, |
| 9467 | operand_ty.fmt(pt), |
| 9468 | operand_bits, |
| 9469 | }); |
| 9470 | } |
| 9471 | |
| 9472 | if (sema.resolveValue(operand)) |operand_val| { |
| 9473 | const dest_is_exhaustive_enum = dest_ty.zigTypeTag(zcu) == .@"enum" and |
| 9474 | !dest_ty.isNonexhaustiveEnum(zcu); |
| 9475 | if (dest_is_exhaustive_enum and operand_val.isUndef(zcu)) { |
| 9476 | return sema.failWithUseOfUndef(block, operand_src, null); |
| 9477 | } |
| 9478 | |
| 9479 | const result_val = try sema.bitCastVal(operand_val, dest_ty); |
| 9480 | |
| 9481 | if (dest_is_exhaustive_enum and |
| 9482 | dest_ty.enumTagFieldIndex(result_val, zcu) == null) |
| 9483 | { |
| 9484 | return sema.fail(block, src, "enum '{f}' has no tag with value '{f}'", .{ |
| 9485 | dest_ty.fmt(pt), result_val.backingInt(zcu).fmtValueSema(pt, sema), |
| 9486 | }); |
| 9487 | } |
| 9488 | |
| 9489 | return .fromValue(result_val); |
| 9490 | } |
| 9491 | |
| 9492 | try sema.validateRuntimeValue(block, src, operand); |
| 9493 | |
| 9494 | if (block.wantSafety()) { |
| 9495 | try sema.preparePanicId(src, .invalid_enum_value); |
| 9496 | return block.addTyOp(.bit_cast_safe, dest_ty, operand); |
| 9497 | } |
| 9498 | return block.addTyOp(.bit_cast, dest_ty, operand); |
| 9499 | } |
| 9500 | |
| 9501 | fn zirBackingInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9502 | const pt = sema.pt; |
| 9503 | const zcu = pt.zcu; |
| 9504 | const gpa = zcu.comp.gpa; |
| 9505 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 9506 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 9507 | |
| 9508 | const operand = sema.resolveInst(inst_data.operand); |
| 9509 | const operand_ty = sema.typeOf(operand); |
| 9510 | operand_ty.assertHasLayout(zcu); |
| 9511 | const int_backed_ref: Air.Inst.Ref = ref: switch (operand_ty.zigTypeTag(zcu)) { |
| 9512 | .@"enum" => operand, |
| 9513 | .@"union" => { |
| 9514 | const union_obj = zcu.intern_pool.loadUnionType(operand_ty.toIntern()); |
| 9515 | if (union_obj.tag_usage == .tagged) break :ref try sema.unionToTag(block, operand); |
| 9516 | if (union_obj.layout == .@"packed") break :ref operand; |
| 9517 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 9518 | const msg = try sema.errMsg(operand_src, "non-packed union '{f}' does not have a backing integer", .{operand_ty.fmt(pt)}); |
| 9519 | errdefer msg.deinit(gpa); |
| 9520 | try sema.errNote(operand_src, msg, "untagged union '{f}' does not have an enum tag with a backing integer", .{operand_ty.fmt(pt)}); |
| 9521 | try sema.addDeclaredHereNote(msg, operand_ty); |
| 9522 | break :msg msg; |
| 9523 | }); |
| 9524 | }, |
| 9525 | .@"struct" => { |
| 9526 | if (operand_ty.containerLayout(zcu) != .@"packed") { |
| 9527 | return sema.fail(block, operand_src, "non-packed struct '{f}' does not have a backing integer", .{ |
| 9528 | operand_ty.fmt(pt), |
| 9529 | }); |
| 9530 | } |
| 9531 | break :ref operand; |
| 9532 | }, |
| 9533 | else => { |
| 9534 | return sema.fail(block, operand_src, "expected enum, tagged union, packed union or packed struct, found '{f}'", .{ |
| 9535 | operand_ty.fmt(pt), |
| 9536 | }); |
| 9537 | }, |
| 9538 | }; |
| 9539 | const int_backed_ty = sema.typeOf(int_backed_ref); |
| 9540 | if (int_backed_ty.backingIntMode(zcu) != .explicit and int_backed_ty.zigTypeTag(zcu) != .@"enum") |
| 9541 | return sema.failWithAmbiguousBackingIntType(block, operand_src, int_backed_ty, "@backingInt"); |
| 9542 | const backing_int_ty = int_backed_ty.backingIntType(zcu); |
| 9543 | |
| 9544 | if (sema.resolveValue(int_backed_ref)) |int_backed_val| { |
| 9545 | if (int_backed_val.isUndef(zcu)) return pt.undefRef(backing_int_ty); |
| 9546 | return .fromValue(int_backed_val.backingInt(zcu)); |
| 9547 | } |
| 9548 | |
| 9549 | switch (backing_int_ty.classify(zcu)) { |
| 9550 | .partially_comptime, .fully_comptime => unreachable, // does not apply to integers |
| 9551 | .no_possible_value => unreachable, // enum also NPV, cannot instantiate NPV types |
| 9552 | .one_possible_value => unreachable, // enum or bitpack also OPV, should have been resolve above |
| 9553 | .runtime => {}, |
| 9554 | } |
| 9555 | |
| 9556 | return block.addTyOp(.bit_cast, backing_int_ty, int_backed_ref); |
| 9557 | } |
| 9558 | |
| 9559 | fn zirFromBackingIntArgTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9560 | const pt = sema.pt; |
| 9561 | const zcu = pt.zcu; |
| 9562 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 9563 | const src = block.nodeOffset(inst_data.src_node); |
| 9564 | |
| 9565 | const dest_ty = try sema.resolveDestType(block, src, inst_data.operand, .remove_eu_opt, "@fromBackingInt"); |
| 9566 | try sema.ensureLayoutResolved(dest_ty, src, .init); |
| 9567 | switch (dest_ty.zigTypeTag(zcu)) { |
| 9568 | .@"enum" => {}, |
| 9569 | .@"struct", .@"union" => |type_tag| if (dest_ty.containerLayout(zcu) != .@"packed") { |
| 9570 | return sema.fail(block, src, "non-packed {t} '{f}' does not have a backing integer", .{ |
| 9571 | type_tag, dest_ty.fmt(pt), |
| 9572 | }); |
| 9573 | }, |
| 9574 | else => { |
| 9575 | return sema.fail(block, src, "expected enum, packed union or packed struct, found '{f}'", .{ |
| 9576 | dest_ty.fmt(pt), |
| 9577 | }); |
| 9578 | }, |
| 9579 | } |
| 9580 | if (dest_ty.backingIntMode(zcu) != .explicit and dest_ty.zigTypeTag(zcu) != .@"enum") |
| 9581 | return sema.failWithAmbiguousBackingIntType(block, src, dest_ty, "@fromBackingInt"); |
| 9582 | return .fromType(dest_ty.backingIntType(zcu)); |
| 9583 | } |
| 9584 | |
| 9585 | fn zirFromBackingInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9586 | const pt = sema.pt; |
| 9587 | const zcu = pt.zcu; |
| 9588 | const ip = &zcu.intern_pool; |
| 9589 | |
| 9590 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9591 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 9592 | const src = block.nodeOffset(inst_data.src_node); |
| 9593 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 9594 | |
| 9595 | // Type has already been validated and layout-resolved by `zirFromBackingIntArgTy`. |
| 9596 | const dest_ty = try sema.resolveDestType(block, .unneeded, extra.lhs, .remove_eu_opt, undefined); |
| 9597 | dest_ty.assertHasLayout(zcu); |
| 9598 | |
| 9599 | const operand = sema.resolveInst(extra.rhs); |
| 9600 | const backing_int_ref = try sema.coerce(block, dest_ty.backingIntType(zcu), operand, operand_src); |
| 9601 | |
| 9602 | if (sema.resolveValue(backing_int_ref)) |backing_int_val| { |
| 9603 | if (dest_ty.zigTypeTag(zcu) != .@"enum") { |
| 9604 | // we don't do any safety checks for bitpacks |
| 9605 | if (backing_int_val.isUndef(zcu)) return pt.undefRef(dest_ty); |
| 9606 | return .fromValue(try pt.bitpackValue(dest_ty, backing_int_val)); |
| 9607 | } |
| 9608 | const enum_obj = ip.loadEnumType(dest_ty.toIntern()); |
| 9609 | if (backing_int_val.isUndef(zcu)) { |
| 9610 | if (enum_obj.nonexhaustive) return pt.undefRef(dest_ty); |
| 9611 | return sema.failWithUseOfUndef(block, operand_src, null); |
| 9612 | } |
| 9613 | if (!enum_obj.nonexhaustive and |
| 9614 | enum_obj.tagValueIndex(ip, backing_int_val.toIntern()) == null) |
| 9615 | { |
| 9616 | return sema.fail(block, src, "enum '{f}' has no tag with value '{f}'", .{ |
| 9617 | dest_ty.fmt(pt), backing_int_val.fmtValueSema(pt, sema), |
| 9618 | }); |
| 9619 | } |
| 9620 | return .fromValue(try pt.enumValue(dest_ty, backing_int_val)); |
| 9621 | } |
| 9622 | |
| 9623 | switch (dest_ty.classify(zcu)) { |
| 9624 | .partially_comptime, .fully_comptime => unreachable, // does not apply to enums or bitpacks |
| 9625 | .no_possible_value => unreachable, // backing int also NPV, cannot coerce to NPV type |
| 9626 | .one_possible_value => unreachable, // backing int also OPV, should have been resolve above |
| 9627 | .runtime => {}, |
| 9628 | } |
| 9629 | |
| 9630 | if (block.wantSafety()) { |
| 9631 | try sema.preparePanicId(src, .invalid_enum_value); |
| 9632 | return block.addTyOp(.bit_cast_safe, dest_ty, backing_int_ref); |
| 9633 | } |
| 9634 | return block.addTyOp(.bit_cast, dest_ty, backing_int_ref); |
| 9635 | } |
| 9636 | |
| 9637 | fn zirFloatCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9638 | const pt = sema.pt; |
| 9639 | const zcu = pt.zcu; |
| 9640 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9641 | const src = block.nodeOffset(inst_data.src_node); |
| 9642 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 9643 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 9644 | |
| 9645 | const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatCast"); |
| 9646 | const dest_scalar_ty = dest_ty.scalarType(zcu); |
| 9647 | |
| 9648 | const operand = sema.resolveInst(extra.rhs); |
| 9649 | const operand_ty = sema.typeOf(operand); |
| 9650 | const operand_scalar_ty = operand_ty.scalarType(zcu); |
| 9651 | |
| 9652 | try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); |
| 9653 | const is_vector = dest_ty.zigTypeTag(zcu) == .vector; |
| 9654 | |
| 9655 | const target = zcu.getTarget(); |
| 9656 | const dest_is_comptime_float = switch (dest_scalar_ty.zigTypeTag(zcu)) { |
| 9657 | .comptime_float => true, |
| 9658 | .float => false, |
| 9659 | else => return sema.fail( |
| 9660 | block, |
| 9661 | src, |
| 9662 | "expected float or vector type, found '{f}'", |
| 9663 | .{dest_ty.fmt(pt)}, |
| 9664 | ), |
| 9665 | }; |
| 9666 | |
| 9667 | switch (operand_scalar_ty.zigTypeTag(zcu)) { |
| 9668 | .comptime_float, .float, .comptime_int => {}, |
| 9669 | else => return sema.fail( |
| 9670 | block, |
| 9671 | operand_src, |
| 9672 | "expected float or vector type, found '{f}'", |
| 9673 | .{operand_ty.fmt(pt)}, |
| 9674 | ), |
| 9675 | } |
| 9676 | |
| 9677 | if (sema.resolveValue(operand)) |operand_val| { |
| 9678 | if (!is_vector) { |
| 9679 | return Air.internedToRef((try operand_val.floatCast(dest_ty, pt)).toIntern()); |
| 9680 | } |
| 9681 | const vec_len = operand_ty.vectorLen(zcu); |
| 9682 | const new_elems = try sema.arena.alloc(InternPool.Index, vec_len); |
| 9683 | for (new_elems, 0..) |*new_elem, i| { |
| 9684 | const old_elem = try operand_val.elemValue(pt, i); |
| 9685 | new_elem.* = (try old_elem.floatCast(dest_scalar_ty, pt)).toIntern(); |
| 9686 | } |
| 9687 | return Air.internedToRef((try pt.aggregateValue(dest_ty, new_elems)).toIntern()); |
| 9688 | } |
| 9689 | if (dest_is_comptime_float) { |
| 9690 | return sema.fail(block, operand_src, "unable to cast runtime value to 'comptime_float'", .{}); |
| 9691 | } |
| 9692 | try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), operand_src); |
| 9693 | |
| 9694 | const src_bits = operand_scalar_ty.floatBits(target); |
| 9695 | const dst_bits = dest_scalar_ty.floatBits(target); |
| 9696 | if (dst_bits >= src_bits) { |
| 9697 | return sema.coerce(block, dest_ty, operand, operand_src); |
| 9698 | } |
| 9699 | return block.addTyOp(.fptrunc, dest_ty, operand); |
| 9700 | } |
| 9701 | |
| 9702 | fn zirElemVal(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9703 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9704 | const src = block.nodeOffset(inst_data.src_node); |
| 9705 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 9706 | const array = sema.resolveInst(extra.lhs); |
| 9707 | const elem_index = sema.resolveInst(extra.rhs); |
| 9708 | return sema.elemVal(block, src, array, elem_index, src, false); |
| 9709 | } |
| 9710 | |
| 9711 | fn zirElemPtrLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9712 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9713 | const src = block.nodeOffset(inst_data.src_node); |
| 9714 | const elem_index_src = block.src(.{ .node_offset_array_access_index = inst_data.src_node }); |
| 9715 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 9716 | const array_ptr = sema.resolveInst(extra.lhs); |
| 9717 | const uncoerced_elem_index = sema.resolveInst(extra.rhs); |
| 9718 | if (try sema.resolveDefinedValue(block, src, array_ptr)) |array_ptr_val| { |
| 9719 | const array_ptr_ty = sema.typeOf(array_ptr); |
| 9720 | if (try sema.pointerDeref(block, src, array_ptr_val, array_ptr_ty)) |array_val| { |
| 9721 | const array: Air.Inst.Ref = .fromValue(array_val); |
| 9722 | return elemVal(sema, block, src, array, uncoerced_elem_index, elem_index_src, true); |
| 9723 | } |
| 9724 | } |
| 9725 | const elem_index = try sema.coerce(block, .usize, uncoerced_elem_index, elem_index_src); |
| 9726 | const elem_ptr = try elemPtr(sema, block, src, array_ptr, elem_index, elem_index_src, false, true); |
| 9727 | return analyzeLoad(sema, block, src, elem_ptr, elem_index_src); |
| 9728 | } |
| 9729 | |
| 9730 | fn zirElemValImm(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9731 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].elem_val_imm; |
| 9732 | const array = sema.resolveInst(inst_data.operand); |
| 9733 | const elem_index = try sema.pt.intRef(.usize, inst_data.idx); |
| 9734 | return sema.elemVal(block, LazySrcLoc.unneeded, array, elem_index, LazySrcLoc.unneeded, false); |
| 9735 | } |
| 9736 | |
| 9737 | fn zirElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9738 | const pt = sema.pt; |
| 9739 | const zcu = pt.zcu; |
| 9740 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9741 | const src = block.nodeOffset(inst_data.src_node); |
| 9742 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 9743 | const array_ptr = sema.resolveInst(extra.lhs); |
| 9744 | const elem_index = sema.resolveInst(extra.rhs); |
| 9745 | const indexable_ty = sema.typeOf(array_ptr); |
| 9746 | if (indexable_ty.zigTypeTag(zcu) != .pointer) { |
| 9747 | const capture_src = block.src(.{ .for_capture_from_input = inst_data.src_node }); |
| 9748 | const msg = msg: { |
| 9749 | const msg = try sema.errMsg(capture_src, "pointer capture of non pointer type '{f}'", .{ |
| 9750 | indexable_ty.fmt(pt), |
| 9751 | }); |
| 9752 | errdefer msg.destroy(sema.gpa); |
| 9753 | if (indexable_ty.isIndexable(zcu)) { |
| 9754 | try sema.errNote(src, msg, "consider using '&' here", .{}); |
| 9755 | } |
| 9756 | break :msg msg; |
| 9757 | }; |
| 9758 | return sema.failWithOwnedErrorMsg(block, msg); |
| 9759 | } |
| 9760 | try sema.checkIndexable(block, src, indexable_ty); |
| 9761 | try sema.ensureLayoutResolved(indexable_ty.childType(zcu), src, .ptr_access); |
| 9762 | return sema.elemPtrOneLayerOnly(block, src, array_ptr, elem_index, src, false, false); |
| 9763 | } |
| 9764 | |
| 9765 | fn zirElemPtrNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9766 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9767 | const src = block.nodeOffset(inst_data.src_node); |
| 9768 | const elem_index_src = block.src(.{ .node_offset_array_access_index = inst_data.src_node }); |
| 9769 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 9770 | const array_ptr = sema.resolveInst(extra.lhs); |
| 9771 | const uncoerced_elem_index = sema.resolveInst(extra.rhs); |
| 9772 | const elem_index = try sema.coerce(block, .usize, uncoerced_elem_index, elem_index_src); |
| 9773 | return sema.elemPtr(block, src, array_ptr, elem_index, elem_index_src, false, true); |
| 9774 | } |
| 9775 | |
| 9776 | fn zirArrayInitElemPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9777 | const pt = sema.pt; |
| 9778 | const zcu = pt.zcu; |
| 9779 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9780 | const src = block.nodeOffset(inst_data.src_node); |
| 9781 | const extra = sema.code.extraData(Zir.Inst.ElemPtrImm, inst_data.payload_index).data; |
| 9782 | const array_ptr = sema.resolveInst(extra.ptr); |
| 9783 | const elem_index = try pt.intRef(.usize, extra.index); |
| 9784 | const array_ty = sema.typeOf(array_ptr).childType(zcu); |
| 9785 | switch (array_ty.zigTypeTag(zcu)) { |
| 9786 | .array, .vector => {}, |
| 9787 | else => if (!array_ty.isTuple(zcu)) { |
| 9788 | return sema.failWithArrayInitNotSupported(block, src, array_ty); |
| 9789 | }, |
| 9790 | } |
| 9791 | return sema.elemPtr(block, src, array_ptr, elem_index, src, true, true); |
| 9792 | } |
| 9793 | |
| 9794 | fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9795 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9796 | const src = block.nodeOffset(inst_data.src_node); |
| 9797 | const extra = sema.code.extraData(Zir.Inst.SliceStart, inst_data.payload_index).data; |
| 9798 | const array_ptr = sema.resolveInst(extra.lhs); |
| 9799 | const start = sema.resolveInst(extra.start); |
| 9800 | const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); |
| 9801 | const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node }); |
| 9802 | const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node }); |
| 9803 | |
| 9804 | return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, LazySrcLoc.unneeded, ptr_src, start_src, end_src, false); |
| 9805 | } |
| 9806 | |
| 9807 | fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9808 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9809 | const src = block.nodeOffset(inst_data.src_node); |
| 9810 | const extra = sema.code.extraData(Zir.Inst.SliceEnd, inst_data.payload_index).data; |
| 9811 | const array_ptr = sema.resolveInst(extra.lhs); |
| 9812 | const start = sema.resolveInst(extra.start); |
| 9813 | const end = sema.resolveInst(extra.end); |
| 9814 | const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); |
| 9815 | const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node }); |
| 9816 | const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node }); |
| 9817 | |
| 9818 | return sema.analyzeSlice(block, src, array_ptr, start, end, .none, LazySrcLoc.unneeded, ptr_src, start_src, end_src, false); |
| 9819 | } |
| 9820 | |
| 9821 | fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9822 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9823 | const src = block.nodeOffset(inst_data.src_node); |
| 9824 | const sentinel_src = block.src(.{ .node_offset_slice_sentinel = inst_data.src_node }); |
| 9825 | const extra = sema.code.extraData(Zir.Inst.SliceSentinel, inst_data.payload_index).data; |
| 9826 | const array_ptr = sema.resolveInst(extra.lhs); |
| 9827 | const start = sema.resolveInst(extra.start); |
| 9828 | const end: Air.Inst.Ref = if (extra.end == .none) .none else sema.resolveInst(extra.end); |
| 9829 | const sentinel = sema.resolveInst(extra.sentinel); |
| 9830 | const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); |
| 9831 | const start_src = block.src(.{ .node_offset_slice_start = inst_data.src_node }); |
| 9832 | const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node }); |
| 9833 | |
| 9834 | return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src, false); |
| 9835 | } |
| 9836 | |
| 9837 | fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9838 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 9839 | const src = block.nodeOffset(inst_data.src_node); |
| 9840 | const extra = sema.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data; |
| 9841 | const array_ptr = sema.resolveInst(extra.lhs); |
| 9842 | const start = sema.resolveInst(extra.start); |
| 9843 | const len = sema.resolveInst(extra.len); |
| 9844 | const sentinel = if (extra.sentinel == .none) .none else sema.resolveInst(extra.sentinel); |
| 9845 | const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); |
| 9846 | const start_src = block.src(.{ .node_offset_slice_start = extra.start_src_node_offset }); |
| 9847 | const end_src = block.src(.{ .node_offset_slice_end = inst_data.src_node }); |
| 9848 | const sentinel_src: LazySrcLoc = if (sentinel == .none) |
| 9849 | LazySrcLoc.unneeded |
| 9850 | else |
| 9851 | block.src(.{ .node_offset_slice_sentinel = inst_data.src_node }); |
| 9852 | |
| 9853 | return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true); |
| 9854 | } |
| 9855 | |
| 9856 | fn zirSliceSentinelTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9857 | const pt = sema.pt; |
| 9858 | const zcu = pt.zcu; |
| 9859 | |
| 9860 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 9861 | |
| 9862 | const src = block.nodeOffset(inst_data.src_node); |
| 9863 | const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); |
| 9864 | const sentinel_src = block.src(.{ .node_offset_slice_sentinel = inst_data.src_node }); |
| 9865 | |
| 9866 | // This is like the logic in `analyzeSlice`; since we've evaluated the LHS as an lvalue, we will |
| 9867 | // have a double pointer if it was already a pointer. |
| 9868 | |
| 9869 | const lhs_ptr_ty = sema.typeOf(sema.resolveInst(inst_data.operand)); |
| 9870 | const lhs_ty = switch (lhs_ptr_ty.zigTypeTag(zcu)) { |
| 9871 | .pointer => lhs_ptr_ty.childType(zcu), |
| 9872 | else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{lhs_ptr_ty.fmt(pt)}), |
| 9873 | }; |
| 9874 | |
| 9875 | const sentinel_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) { |
| 9876 | .array => lhs_ty.childType(zcu), |
| 9877 | .pointer => switch (lhs_ty.ptrSize(zcu)) { |
| 9878 | .many, .c, .slice => lhs_ty.childType(zcu), |
| 9879 | .one => s: { |
| 9880 | const lhs_elem_ty = lhs_ty.childType(zcu); |
| 9881 | break :s switch (lhs_elem_ty.zigTypeTag(zcu)) { |
| 9882 | .array => lhs_elem_ty.childType(zcu), // array element type |
| 9883 | else => return sema.fail(block, sentinel_src, "slice of single-item pointer cannot have sentinel", .{}), |
| 9884 | }; |
| 9885 | }, |
| 9886 | }, |
| 9887 | else => return sema.fail(block, src, "slice of non-array type '{f}'", .{lhs_ty.fmt(pt)}), |
| 9888 | }; |
| 9889 | |
| 9890 | return Air.internedToRef(sentinel_ty.toIntern()); |
| 9891 | } |
| 9892 | |
| 9893 | fn zirSwitchBlockErrUnion(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9894 | const pt = sema.pt; |
| 9895 | const zcu = pt.zcu; |
| 9896 | const gpa = sema.gpa; |
| 9897 | |
| 9898 | const zir_switch = sema.code.getSwitchBlock(inst); |
| 9899 | const src_node_offset = zir_switch.catch_or_if_src_node_offset.unwrap().?; |
| 9900 | const src = block.src(.{ .node_offset_main_token = src_node_offset }); |
| 9901 | const operand_src = block.src(.{ .node_offset_if_cond = src_node_offset }); |
| 9902 | |
| 9903 | assert(!zir_switch.has_continue); // wrong codepath! |
| 9904 | |
| 9905 | const block_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len)); |
| 9906 | try sema.air_instructions.append(gpa, .{ |
| 9907 | .tag = .block, |
| 9908 | .data = undefined, |
| 9909 | }); |
| 9910 | var label: Block.Label = .{ |
| 9911 | .zir_block = inst, |
| 9912 | .merges = .{ |
| 9913 | .src_locs = .empty, |
| 9914 | .results = .empty, |
| 9915 | .br_list = .empty, |
| 9916 | .block_inst = block_inst, |
| 9917 | }, |
| 9918 | }; |
| 9919 | var child_block = block.makeSubBlock(); |
| 9920 | child_block.label = &label; |
| 9921 | const merges = &child_block.label.?.merges; |
| 9922 | defer child_block.instructions.deinit(gpa); |
| 9923 | defer merges.deinit(gpa); |
| 9924 | |
| 9925 | const non_err_case = zir_switch.non_err_case.?; |
| 9926 | |
| 9927 | var non_err_block: Block = child_block.makeSubBlock(); |
| 9928 | non_err_block.runtime_loop = null; |
| 9929 | non_err_block.runtime_cond = operand_src; |
| 9930 | non_err_block.runtime_index.increment(); |
| 9931 | non_err_block.need_debug_scope = null; |
| 9932 | defer non_err_block.instructions.deinit(gpa); |
| 9933 | |
| 9934 | var switch_block: Block = child_block.makeSubBlock(); |
| 9935 | switch_block.runtime_loop = null; |
| 9936 | switch_block.runtime_cond = operand_src; |
| 9937 | switch_block.runtime_index.increment(); |
| 9938 | switch_block.need_debug_scope = null; |
| 9939 | defer switch_block.instructions.deinit(gpa); |
| 9940 | |
| 9941 | // We begin with unwrapping the error union we're switching on as necessary. |
| 9942 | // Then we analyze the non-error prong if it's not comptime-unreachable. |
| 9943 | // Lastly, we analyze the error prong(s) as a regular switch. |
| 9944 | |
| 9945 | const raw_switch_operand, const non_err_cond, const non_err_hint = non_err: { |
| 9946 | const eu_maybe_ptr = sema.resolveInst(zir_switch.main_operand); |
| 9947 | const err_union_ty: Type = err_union_ty: { |
| 9948 | const raw_operand_ty = sema.typeOf(eu_maybe_ptr); |
| 9949 | if (!non_err_case.operand_is_ref) break :err_union_ty raw_operand_ty; |
| 9950 | try sema.checkPtrOperand(block, operand_src, raw_operand_ty); |
| 9951 | const child_ty = raw_operand_ty.childType(zcu); |
| 9952 | try sema.ensureLayoutResolved(child_ty, operand_src, .ptr_access); |
| 9953 | break :err_union_ty child_ty; |
| 9954 | }; |
| 9955 | if (err_union_ty.zigTypeTag(zcu) != .error_union) { |
| 9956 | return sema.fail(block, operand_src, "expected error union type, found '{f}'", .{ |
| 9957 | err_union_ty.fmt(pt), |
| 9958 | }); |
| 9959 | } |
| 9960 | |
| 9961 | const non_err_cond = if (non_err_case.operand_is_ref) |
| 9962 | try sema.analyzePtrIsNonErr(block, operand_src, eu_maybe_ptr) |
| 9963 | else |
| 9964 | try sema.analyzeIsNonErr(block, operand_src, eu_maybe_ptr); |
| 9965 | |
| 9966 | const non_err_hint: std.lang.BranchHint = hint: { |
| 9967 | // don't analyze the non-error body if it's unreachable |
| 9968 | if (non_err_cond == .bool_false) { |
| 9969 | break :hint undefined; |
| 9970 | } |
| 9971 | |
| 9972 | const eu_payload: Air.Inst.Ref = switch (non_err_case.capture) { |
| 9973 | .by_val => try sema.analyzeErrUnionPayload(&non_err_block, src, err_union_ty, eu_maybe_ptr, operand_src, false), |
| 9974 | .by_ref => try sema.analyzeErrUnionPayloadPtr(&non_err_block, src, eu_maybe_ptr, false, false), |
| 9975 | .none => undefined, |
| 9976 | }; |
| 9977 | if (non_err_case.capture != .none) sema.inst_map.putAssumeCapacity(inst, eu_payload); |
| 9978 | defer if (non_err_case.capture != .none) assert(sema.inst_map.remove(inst)); |
| 9979 | |
| 9980 | if (non_err_cond == .bool_true) { |
| 9981 | // Early return; we don't analyze the switch as it's unreachable. |
| 9982 | return sema.resolveBlockBody(block, src, &non_err_block, non_err_case.body, inst, merges); |
| 9983 | } |
| 9984 | break :hint try sema.analyzeBodyRuntimeBreak(&non_err_block, non_err_case.body); |
| 9985 | }; |
| 9986 | |
| 9987 | // Emit this into the switch block as it's our error case! |
| 9988 | const eu_code = if (non_err_case.operand_is_ref) |
| 9989 | try sema.analyzeErrUnionCodePtr(&switch_block, operand_src, eu_maybe_ptr) |
| 9990 | else |
| 9991 | try sema.analyzeErrUnionCode(&switch_block, operand_src, eu_maybe_ptr); |
| 9992 | |
| 9993 | break :non_err .{ |
| 9994 | eu_code, |
| 9995 | non_err_cond, |
| 9996 | non_err_hint, |
| 9997 | }; |
| 9998 | }; |
| 9999 | |
| 10000 | const validated_switch = try sema.validateSwitchBlock(block, raw_switch_operand, false, inst, &zir_switch); |
| 10001 | |
| 10002 | const maybe_switch_ref: ?Air.Inst.Ref = ref: { |
| 10003 | // make err capture (i.e. switch operand) available to switch prong bodies |
| 10004 | sema.inst_map.putAssumeCapacity(inst, raw_switch_operand); |
| 10005 | defer assert(sema.inst_map.remove(inst)); |
| 10006 | break :ref try sema.analyzeSwitchBlock(block, &switch_block, raw_switch_operand, false, merges, inst, &zir_switch, &validated_switch); |
| 10007 | }; |
| 10008 | |
| 10009 | if (non_err_cond == .bool_false) { |
| 10010 | return maybe_switch_ref orelse { |
| 10011 | const switch_src = block.nodeOffset(zir_switch.switch_src_node_offset); |
| 10012 | return sema.resolveAnalyzedBlock(block, switch_src, &switch_block, merges, false); |
| 10013 | }; |
| 10014 | } |
| 10015 | |
| 10016 | if (maybe_switch_ref) |switch_ref| { |
| 10017 | if (sema.typeOf(switch_ref).isNoReturn(zcu)) { |
| 10018 | _ = try switch_block.addNoOp(.unreach); |
| 10019 | } else { |
| 10020 | const br_ref = try switch_block.addBr(merges.block_inst, switch_ref); |
| 10021 | try merges.results.append(gpa, switch_ref); |
| 10022 | try merges.br_list.append(gpa, br_ref.toIndex().?); |
| 10023 | try merges.src_locs.append(gpa, null); |
| 10024 | } |
| 10025 | } |
| 10026 | |
| 10027 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".field_names.len + |
| 10028 | non_err_block.instructions.items.len + switch_block.instructions.items.len); |
| 10029 | const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{ |
| 10030 | .then_body_len = @intCast(non_err_block.instructions.items.len), |
| 10031 | .else_body_len = @intCast(switch_block.instructions.items.len), |
| 10032 | .branch_hints = .{ |
| 10033 | .true = non_err_hint, |
| 10034 | .false = .unlikely, // errors are unlikely |
| 10035 | // Code coverage is desired for error handling. |
| 10036 | .then_cov = .poi, |
| 10037 | .else_cov = .poi, |
| 10038 | }, |
| 10039 | }); |
| 10040 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(non_err_block.instructions.items)); |
| 10041 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(switch_block.instructions.items)); |
| 10042 | |
| 10043 | _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{ |
| 10044 | .operand = non_err_cond, |
| 10045 | .payload = cond_br_payload, |
| 10046 | } } }); |
| 10047 | |
| 10048 | return sema.resolveAnalyzedBlock(block, src, &child_block, merges, false); |
| 10049 | } |
| 10050 | |
| 10051 | fn zirSwitchBlock( |
| 10052 | sema: *Sema, |
| 10053 | block: *Block, |
| 10054 | inst: Zir.Inst.Index, |
| 10055 | operand_is_ref: bool, |
| 10056 | ) CompileError!Air.Inst.Ref { |
| 10057 | const zir_switch = sema.code.getSwitchBlock(inst); |
| 10058 | |
| 10059 | const block_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len)); |
| 10060 | try sema.air_instructions.append(sema.gpa, .{ |
| 10061 | .tag = .block, |
| 10062 | .data = undefined, |
| 10063 | }); |
| 10064 | var label: Block.Label = .{ |
| 10065 | .zir_block = inst, |
| 10066 | .merges = .{ |
| 10067 | .src_locs = .empty, |
| 10068 | .results = .empty, |
| 10069 | .br_list = .empty, |
| 10070 | .block_inst = block_inst, |
| 10071 | }, |
| 10072 | }; |
| 10073 | var child_block = block.makeSubBlock(); |
| 10074 | child_block.label = &label; |
| 10075 | const merges = &child_block.label.?.merges; |
| 10076 | defer child_block.instructions.deinit(sema.gpa); |
| 10077 | defer merges.deinit(sema.gpa); |
| 10078 | |
| 10079 | const raw_operand = sema.resolveInst(zir_switch.main_operand); |
| 10080 | const validated_switch = try sema.validateSwitchBlock(block, raw_operand, operand_is_ref, inst, &zir_switch); |
| 10081 | const maybe_ref = try sema.analyzeSwitchBlock(block, &child_block, raw_operand, operand_is_ref, merges, inst, &zir_switch, &validated_switch); |
| 10082 | return maybe_ref orelse { |
| 10083 | const src = block.nodeOffset(zir_switch.switch_src_node_offset); |
| 10084 | return sema.resolveAnalyzedBlock(block, src, &child_block, merges, false); |
| 10085 | }; |
| 10086 | } |
| 10087 | |
| 10088 | /// If the switch can be resolved to a value at comptime, this will return a `Ref` |
| 10089 | /// that's never `.none`. |
| 10090 | /// If not, this will return `null` and emit its instructions into `child_block`. |
| 10091 | fn analyzeSwitchBlock( |
| 10092 | sema: *Sema, |
| 10093 | block: *Block, |
| 10094 | child_block: *Block, |
| 10095 | raw_operand: Air.Inst.Ref, |
| 10096 | operand_is_ref: bool, |
| 10097 | merges: *Block.Merges, |
| 10098 | switch_inst: Zir.Inst.Index, |
| 10099 | zir_switch: *const Zir.UnwrappedSwitchBlock, |
| 10100 | validated_switch: *const ValidatedSwitchBlock, |
| 10101 | ) CompileError!?Air.Inst.Ref { |
| 10102 | const pt = sema.pt; |
| 10103 | const zcu = pt.zcu; |
| 10104 | const gpa = sema.gpa; |
| 10105 | |
| 10106 | const src_node_offset = zir_switch.switch_src_node_offset; |
| 10107 | const src = block.nodeOffset(src_node_offset); |
| 10108 | const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset }); |
| 10109 | |
| 10110 | const has_else = zir_switch.else_case != null; |
| 10111 | const else_case = validated_switch.else_case; |
| 10112 | |
| 10113 | const operand: SwitchOperand, const operand_ty: Type, const maybe_operand_opv: ?Value, const item_ty: Type = operand: { |
| 10114 | const val, const ref = if (operand_is_ref) |
| 10115 | .{ try sema.analyzeLoad(block, src, raw_operand, operand_src), raw_operand } |
| 10116 | else |
| 10117 | .{ raw_operand, .none }; |
| 10118 | |
| 10119 | const operand_ty = sema.typeOf(val); |
| 10120 | operand_ty.assertHasLayout(zcu); |
| 10121 | const maybe_operand_opv = try operand_ty.onePossibleValue(pt); |
| 10122 | const init_cond: Air.Inst.Ref, const item_ty: Type = init: { |
| 10123 | if (operand_ty.zigTypeTag(zcu) == .@"union" and |
| 10124 | operand_ty.containerLayout(zcu) != .@"packed") |
| 10125 | { |
| 10126 | const tag_val = try sema.unionToTag(block, val); |
| 10127 | break :init .{ tag_val, sema.typeOf(tag_val) }; |
| 10128 | } |
| 10129 | break :init .{ |
| 10130 | if (maybe_operand_opv) |operand_opv| .fromValue(operand_opv) else val, |
| 10131 | operand_ty, |
| 10132 | }; |
| 10133 | }; |
| 10134 | item_ty.assertHasLayout(zcu); |
| 10135 | |
| 10136 | if (zir_switch.has_continue and !block.isComptime()) { |
| 10137 | const operand_alloc: Air.Inst.Ref = if (zir_switch.any_maybe_runtime_capture and |
| 10138 | maybe_operand_opv == null) |
| 10139 | alloc: { |
| 10140 | const operand_ptr_ty = try pt.singleMutPtrType(sema.typeOf(raw_operand)); |
| 10141 | const operand_alloc = try block.addTy(.alloc, operand_ptr_ty); |
| 10142 | _ = try block.addBinOp(.store, operand_alloc, raw_operand); |
| 10143 | break :alloc operand_alloc; |
| 10144 | } else .none; |
| 10145 | break :operand .{ .{ .loop = .{ |
| 10146 | .operand_alloc = operand_alloc, |
| 10147 | .operand_is_ref = operand_is_ref, |
| 10148 | .init_cond = init_cond, |
| 10149 | } }, operand_ty, maybe_operand_opv, item_ty }; |
| 10150 | } else { |
| 10151 | // We always use `simple` in the comptime/OPV case, because as far as the |
| 10152 | // dispatching logic is concerned, it really is dispatching a single prong. |
| 10153 | break :operand .{ .{ .simple = .{ |
| 10154 | .by_val = val, |
| 10155 | .by_ref = ref, |
| 10156 | .cond = init_cond, |
| 10157 | } }, operand_ty, maybe_operand_opv, item_ty }; |
| 10158 | } |
| 10159 | }; |
| 10160 | |
| 10161 | const raw_operand_ty = sema.typeOf(raw_operand); |
| 10162 | |
| 10163 | const tagged_union_originally = operand_ty.zigTypeTag(zcu) == .@"union" and |
| 10164 | operand_ty.containerLayout(zcu) != .@"packed"; |
| 10165 | const err_set = operand_ty.zigTypeTag(zcu) == .error_set; |
| 10166 | |
| 10167 | // TODO audit the `err_set` special case (https://github.com/ziglang/zig/issues/15909) |
| 10168 | if (!err_set) assert(validated_switch.case_vals.len != 0 or has_else or zir_switch.has_under); // NPV types cannot be instantiated |
| 10169 | |
| 10170 | const cond_ref = switch (operand) { |
| 10171 | .simple => |s| s.cond, |
| 10172 | .loop => |l| l.init_cond, |
| 10173 | }; |
| 10174 | |
| 10175 | resolve_at_comptime: { |
| 10176 | // always runtime; evaluation in comptime scope uses `simple` |
| 10177 | if (operand == .loop) break :resolve_at_comptime; |
| 10178 | |
| 10179 | var cur_cond_val = try sema.resolveDefinedValue(child_block, src, cond_ref) orelse { |
| 10180 | break :resolve_at_comptime; |
| 10181 | }; |
| 10182 | var cur_operand = operand; |
| 10183 | |
| 10184 | while (true) { |
| 10185 | if (sema.resolveSwitchBlock( |
| 10186 | block, |
| 10187 | child_block, |
| 10188 | cur_operand, |
| 10189 | raw_operand_ty, |
| 10190 | cur_cond_val, |
| 10191 | merges, |
| 10192 | switch_inst, |
| 10193 | zir_switch, |
| 10194 | validated_switch, |
| 10195 | )) |result| { |
| 10196 | return result; |
| 10197 | } else |err| switch (err) { |
| 10198 | error.ComptimeBreak => { |
| 10199 | const break_inst = sema.code.instructions.get(@backingInt(sema.comptime_break_inst)); |
| 10200 | if (break_inst.tag != .switch_continue) return error.ComptimeBreak; |
| 10201 | const extra = sema.code.extraData(Zir.Inst.Break, break_inst.data.@"break".payload_index).data; |
| 10202 | if (extra.block_inst != switch_inst) return error.ComptimeBreak; |
| 10203 | // This is a `switch_continue` targeting this block. Change the operand and start over. |
| 10204 | const new_operand_src = child_block.nodeOffset(extra.operand_src_node.unwrap().?); |
| 10205 | const new_operand_uncoerced = sema.resolveInst(break_inst.data.@"break".operand); |
| 10206 | const new_operand = try sema.coerce(child_block, raw_operand_ty, new_operand_uncoerced, new_operand_src); |
| 10207 | |
| 10208 | try sema.emitBackwardBranch(child_block, src); |
| 10209 | |
| 10210 | const new_val, const new_ref = if (operand_is_ref) |
| 10211 | .{ try sema.analyzeLoad(child_block, src, new_operand, new_operand_src), new_operand } |
| 10212 | else |
| 10213 | .{ new_operand, .none }; |
| 10214 | |
| 10215 | const new_cond_ref = if (tagged_union_originally) |
| 10216 | try sema.unionToTag(child_block, new_val) |
| 10217 | else |
| 10218 | new_val; |
| 10219 | |
| 10220 | cur_cond_val = try sema.resolveConstDefinedValue(child_block, src, new_cond_ref, null); |
| 10221 | cur_operand = .{ .simple = .{ |
| 10222 | .by_val = new_val, |
| 10223 | .by_ref = new_ref, |
| 10224 | .cond = new_cond_ref, |
| 10225 | } }; |
| 10226 | }, |
| 10227 | else => |e| return e, |
| 10228 | } |
| 10229 | } |
| 10230 | } |
| 10231 | |
| 10232 | if (child_block.isComptime()) { |
| 10233 | _ = try sema.resolveConstDefinedValue(child_block, operand_src, operand.simple.cond, null); |
| 10234 | unreachable; |
| 10235 | } |
| 10236 | |
| 10237 | const switch_ref: Air.Inst.Ref, const item_has_opv = switch_ref: { |
| 10238 | const item_opv = try item_ty.onePossibleValue(pt) orelse { |
| 10239 | assert(maybe_operand_opv == null); // `operand_ty` can only be an OPV type if `item_ty` is one too! |
| 10240 | const air_ref = try sema.finishSwitchBr( |
| 10241 | block, |
| 10242 | child_block, |
| 10243 | operand, |
| 10244 | raw_operand_ty, |
| 10245 | operand_is_ref, |
| 10246 | merges, |
| 10247 | switch_inst, |
| 10248 | zir_switch, |
| 10249 | validated_switch, |
| 10250 | ); |
| 10251 | break :switch_ref .{ air_ref, false }; |
| 10252 | }; |
| 10253 | |
| 10254 | // We simplify conditions with OPV to either a `loop` or a `block` since |
| 10255 | // we cannot switch on a value which doesn't exist at runtime. |
| 10256 | |
| 10257 | assert(operand == .loop); // `simple` should have already been comptime-resolved above! |
| 10258 | |
| 10259 | var case_block = child_block.makeSubBlock(); |
| 10260 | case_block.runtime_loop = null; |
| 10261 | case_block.runtime_cond = operand_src; |
| 10262 | case_block.runtime_index.increment(); |
| 10263 | case_block.need_debug_scope = null; // this body is emitted regardless |
| 10264 | defer case_block.instructions.deinit(gpa); |
| 10265 | |
| 10266 | const case_vals = validated_switch.case_vals; |
| 10267 | |
| 10268 | const case_idx, const body, const capture, const has_tag_capture = find_prong: { |
| 10269 | var case_val_idx: usize = 0; |
| 10270 | var case_it = zir_switch.iterateCases(); |
| 10271 | var extra_index = zir_switch.end; |
| 10272 | while (case_it.next()) |case| { |
| 10273 | const prong_info = case.prong_info; |
| 10274 | const prong_body = sema.code.bodySlice(extra_index, prong_info.body_len); |
| 10275 | extra_index += prong_body.len; |
| 10276 | skip_case: { |
| 10277 | if (!err_set) break :skip_case; |
| 10278 | // This case might consist of errors which are not in the set |
| 10279 | // we're switching on. If so we have to skip it! |
| 10280 | const item_refs = case_vals[case_val_idx..][0..case.item_infos.len]; |
| 10281 | case_val_idx += item_refs.len; |
| 10282 | assert(case.range_infos.len == 0); |
| 10283 | for (case.item_infos, item_refs) |item_info, item_ref| { |
| 10284 | if (item_info.bodyLen()) |body_len| extra_index += body_len; |
| 10285 | if (sema.wantSwitchProngBodyAnalysis(item_ref, operand_ty, false, true, prong_info.is_comptime_unreach)) { |
| 10286 | break :skip_case; |
| 10287 | } |
| 10288 | } |
| 10289 | continue; |
| 10290 | } |
| 10291 | break :find_prong .{ case.index, prong_body, prong_info.capture, prong_info.has_tag_capture }; |
| 10292 | } |
| 10293 | if (has_else) { |
| 10294 | // This *has* to be checked after iterating all regular cases because |
| 10295 | // we allow simple noreturn else prongs when switching on error sets! |
| 10296 | break :find_prong .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture }; |
| 10297 | } |
| 10298 | unreachable; // malformed validated switch |
| 10299 | }; |
| 10300 | |
| 10301 | const analyze_body = sema.wantSwitchProngBodyAnalysis(.fromValue(item_opv), operand_ty, tagged_union_originally, err_set, false); |
| 10302 | if (!analyze_body) return .unreachable_value; |
| 10303 | |
| 10304 | if (!(err_set and |
| 10305 | try sema.maybeErrorUnwrap(&case_block, body, cond_ref, operand_src, true))) |
| 10306 | { |
| 10307 | const payload_inst = if (capture != .none) inst: { |
| 10308 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; |
| 10309 | const payload_ref: Air.Inst.Ref = payload_ref: { |
| 10310 | const captured_opv: Value = captured_opv: { |
| 10311 | if (!tagged_union_originally) { |
| 10312 | break :captured_opv item_opv; |
| 10313 | } |
| 10314 | if (maybe_operand_opv) |operand_opv| { |
| 10315 | break :captured_opv .fromInterned(zcu.intern_pool.indexToKey(operand_opv.toIntern()).un.val); |
| 10316 | } |
| 10317 | assert(zir_switch.any_maybe_runtime_capture); // there's a payload capture |
| 10318 | const loaded_operand = try sema.analyzeSwitchOperandLoad(&case_block, operand, operand_src, capture == .by_ref); |
| 10319 | break :payload_ref try sema.resolveSwitchPayloadCaptureTaggedUnion( |
| 10320 | &case_block, |
| 10321 | loaded_operand, |
| 10322 | operand_src, |
| 10323 | operand_ty, |
| 10324 | item_opv, |
| 10325 | capture == .by_ref, |
| 10326 | ); |
| 10327 | }; |
| 10328 | break :payload_ref switch (capture) { |
| 10329 | .by_val => .fromValue(captured_opv), |
| 10330 | .by_ref => try sema.uavRef(captured_opv), |
| 10331 | .none => unreachable, |
| 10332 | }; |
| 10333 | }; |
| 10334 | sema.inst_map.putAssumeCapacity(payload_inst, payload_ref); |
| 10335 | break :inst payload_inst; |
| 10336 | } else undefined; |
| 10337 | defer if (capture != .none) assert(sema.inst_map.remove(payload_inst)); |
| 10338 | |
| 10339 | const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: { |
| 10340 | const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst; |
| 10341 | if (!tagged_union_originally) { |
| 10342 | const tag_capture_src = block.src(.{ .switch_tag_capture = .{ |
| 10343 | .switch_node_offset = src_node_offset, |
| 10344 | .case_idx = case_idx, |
| 10345 | } }); |
| 10346 | return sema.failWithInvalidSwitchTagCapture(block, tag_capture_src, operand_ty); |
| 10347 | } |
| 10348 | sema.inst_map.putAssumeCapacity(tag_inst, .fromValue(item_opv)); |
| 10349 | break :inst tag_inst; |
| 10350 | } else undefined; |
| 10351 | defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst)); |
| 10352 | |
| 10353 | if (zir_switch.has_continue) sema.inst_map.putAssumeCapacity(switch_inst, .fromType(raw_operand_ty)); |
| 10354 | defer if (zir_switch.has_continue) assert(sema.inst_map.remove(switch_inst)); |
| 10355 | |
| 10356 | _ = try sema.analyzeBodyRuntimeBreak(&case_block, body); |
| 10357 | } |
| 10358 | |
| 10359 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".field_names.len + |
| 10360 | case_block.instructions.items.len); |
| 10361 | const payload_index = sema.addExtraAssumeCapacity(Air.Block{ |
| 10362 | .body_len = @intCast(case_block.instructions.items.len), |
| 10363 | }); |
| 10364 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); |
| 10365 | |
| 10366 | const air_tag: Air.Inst.Tag = if (merges.extra_insts.items.len > 0) |
| 10367 | .loop |
| 10368 | else |
| 10369 | .block; |
| 10370 | const air_ref = try child_block.addInst(.{ |
| 10371 | .tag = air_tag, |
| 10372 | .data = .{ .ty_pl = .{ |
| 10373 | .ty = .noreturn, |
| 10374 | .payload = payload_index, |
| 10375 | } }, |
| 10376 | }); |
| 10377 | break :switch_ref .{ air_ref, true }; |
| 10378 | }; |
| 10379 | |
| 10380 | const air_tag = sema.air_instructions.items(.tag)[@backingInt(switch_ref.toIndex().?)]; |
| 10381 | switch (air_tag) { |
| 10382 | .loop_switch_br, .switch_br => assert(!item_has_opv), |
| 10383 | .loop, .block => assert(item_has_opv), |
| 10384 | else => unreachable, |
| 10385 | } |
| 10386 | switch (air_tag) { |
| 10387 | .loop_switch_br, .loop => assert(merges.extra_insts.items.len > 0), |
| 10388 | .switch_br, .block => assert(merges.extra_insts.items.len == 0), |
| 10389 | else => unreachable, |
| 10390 | } |
| 10391 | |
| 10392 | // We're done with analyzing the switch statement! Now all we have to do is |
| 10393 | // replace the placeholder `br` insts inserted by `zirSwitchContinue`s with |
| 10394 | // their respective finalized inst pointing back at `switch_ref`. |
| 10395 | |
| 10396 | for (merges.extra_insts.items, merges.extra_src_locs.items) |placeholder_inst, dispatch_src| { |
| 10397 | var replacement_block = block.makeSubBlock(); |
| 10398 | defer replacement_block.instructions.deinit(gpa); |
| 10399 | |
| 10400 | assert(sema.air_instructions.items(.tag)[@backingInt(placeholder_inst)] == .br); |
| 10401 | const new_operand_maybe_ref = sema.air_instructions.items(.data)[@backingInt(placeholder_inst)].br.operand; |
| 10402 | |
| 10403 | if (zir_switch.any_maybe_runtime_capture and !item_has_opv) { |
| 10404 | _ = try replacement_block.addBinOp(.store, operand.loop.operand_alloc, new_operand_maybe_ref); |
| 10405 | } |
| 10406 | |
| 10407 | const new_operand_val = if (operand_is_ref) |
| 10408 | try sema.analyzeLoad(&replacement_block, dispatch_src, new_operand_maybe_ref, dispatch_src) |
| 10409 | else |
| 10410 | new_operand_maybe_ref; |
| 10411 | |
| 10412 | const new_cond = try sema.coerce(&replacement_block, item_ty, new_operand_val, dispatch_src); |
| 10413 | |
| 10414 | if (zcu.backendSupportsFeature(.is_named_enum_value) and block.wantSafety() and |
| 10415 | item_ty.zigTypeTag(zcu) == .@"enum" and !item_ty.isNonexhaustiveEnum(zcu) and |
| 10416 | !item_has_opv and !try sema.isComptimeKnown(new_cond)) |
| 10417 | { |
| 10418 | const ok = try replacement_block.addUnOp(.is_named_enum_value, new_cond); |
| 10419 | try sema.addSafetyCheck(&replacement_block, src, ok, .corrupt_switch); |
| 10420 | } |
| 10421 | |
| 10422 | if (item_has_opv) { |
| 10423 | _ = try replacement_block.addInst(.{ |
| 10424 | .tag = .repeat, |
| 10425 | .data = .{ .repeat = .{ |
| 10426 | .loop_inst = switch_ref.toIndex().?, |
| 10427 | } }, |
| 10428 | }); |
| 10429 | } else { |
| 10430 | _ = try replacement_block.addInst(.{ |
| 10431 | .tag = .switch_dispatch, |
| 10432 | .data = .{ .br = .{ |
| 10433 | .block_inst = switch_ref.toIndex().?, |
| 10434 | .operand = new_cond, |
| 10435 | } }, |
| 10436 | }); |
| 10437 | } |
| 10438 | |
| 10439 | if (replacement_block.instructions.items.len == 1) { |
| 10440 | // Optimization: we don't need a block! |
| 10441 | sema.air_instructions.set( |
| 10442 | @backingInt(placeholder_inst), |
| 10443 | sema.air_instructions.get(@backingInt(replacement_block.instructions.items[0])), |
| 10444 | ); |
| 10445 | continue; |
| 10446 | } |
| 10447 | |
| 10448 | // Replace placeholder with a block. |
| 10449 | // No `br` is needed as the block is a switch dispatch so necessarily `noreturn`. |
| 10450 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".field_names.len + |
| 10451 | replacement_block.instructions.items.len); |
| 10452 | sema.air_instructions.set(@backingInt(placeholder_inst), .{ |
| 10453 | .tag = .block, |
| 10454 | .data = .{ .ty_pl = .{ |
| 10455 | .ty = .noreturn, |
| 10456 | .payload = sema.addExtraAssumeCapacity(Air.Block{ |
| 10457 | .body_len = @intCast(replacement_block.instructions.items.len), |
| 10458 | }), |
| 10459 | } }, |
| 10460 | }); |
| 10461 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(replacement_block.instructions.items)); |
| 10462 | } |
| 10463 | |
| 10464 | return null; |
| 10465 | } |
| 10466 | |
| 10467 | fn finishSwitchBr( |
| 10468 | sema: *Sema, |
| 10469 | block: *Block, |
| 10470 | child_block: *Block, |
| 10471 | operand: SwitchOperand, |
| 10472 | raw_operand_ty: Type, |
| 10473 | operand_is_ref: bool, |
| 10474 | merges: *Block.Merges, |
| 10475 | switch_inst: Zir.Inst.Index, |
| 10476 | zir_switch: *const Zir.UnwrappedSwitchBlock, |
| 10477 | validated_switch: *const ValidatedSwitchBlock, |
| 10478 | ) CompileError!Air.Inst.Ref { |
| 10479 | const pt = sema.pt; |
| 10480 | const zcu = pt.zcu; |
| 10481 | const ip = &zcu.intern_pool; |
| 10482 | const gpa = sema.gpa; |
| 10483 | |
| 10484 | const src_node_offset = zir_switch.switch_src_node_offset; |
| 10485 | const src = block.nodeOffset(src_node_offset); |
| 10486 | const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset }); |
| 10487 | |
| 10488 | const has_else = zir_switch.else_case != null; |
| 10489 | const has_under = zir_switch.has_under; |
| 10490 | |
| 10491 | const else_case = validated_switch.else_case; |
| 10492 | |
| 10493 | const scalar_cases_len = zir_switch.scalarCasesLen(); |
| 10494 | const multi_cases_len = zir_switch.multiCasesLen(); |
| 10495 | |
| 10496 | const operand_ty = if (operand_is_ref) |
| 10497 | raw_operand_ty.childType(zcu) |
| 10498 | else |
| 10499 | raw_operand_ty; |
| 10500 | |
| 10501 | const cond_ref = switch (operand) { |
| 10502 | .simple => |s| s.cond, |
| 10503 | .loop => |l| l.init_cond, |
| 10504 | }; |
| 10505 | |
| 10506 | // AstGen guarantees that the instruction immediately preceding |
| 10507 | // switch_block[_ref]/switch_block_err_union is a dbg_stmt. |
| 10508 | const cond_dbg_node_index: Zir.Inst.Index = @fromBackingInt(@intCast(@backingInt(switch_inst) - 1)); |
| 10509 | |
| 10510 | const else_is_named_only = has_else and has_under; |
| 10511 | |
| 10512 | const tagged_union_originally = operand_ty.zigTypeTag(zcu) == .@"union" and |
| 10513 | operand_ty.containerLayout(zcu) != .@"packed"; |
| 10514 | const err_set = operand_ty.zigTypeTag(zcu) == .error_set; |
| 10515 | |
| 10516 | const item_ty = if (tagged_union_originally) |
| 10517 | operand_ty.unionTagType(zcu).? |
| 10518 | else |
| 10519 | operand_ty; |
| 10520 | |
| 10521 | const estimated_cases_len: u32 = scalar_cases_len + multi_cases_len + |
| 10522 | @intFromBool(has_else or has_under); |
| 10523 | |
| 10524 | const BranchHints = struct { |
| 10525 | bags: std.ArrayList(u32), |
| 10526 | count: u32, |
| 10527 | const hints_per_bag = 10; |
| 10528 | fn ensureUnusedCapacity(hints: *@This(), gpa_inner: Allocator, additional_count: u32) Allocator.Error!void { |
| 10529 | const unused_hints = hints.bags.capacity * hints_per_bag - hints.count; |
| 10530 | if (unused_hints >= additional_count) return; |
| 10531 | const bags_required = @divCeil(hints.count + additional_count, hints_per_bag); |
| 10532 | return hints.bags.ensureUnusedCapacity(gpa_inner, bags_required); |
| 10533 | } |
| 10534 | fn appendAssumeCapacity(hints: *@This(), hint: std.lang.BranchHint) void { |
| 10535 | const idx_in_bag = hints.count % hints_per_bag; |
| 10536 | var bag: u32 = if (idx_in_bag > 0) hints.bags.pop().? else 0; |
| 10537 | bag |= @as(u32, @backingInt(hint)) << @intCast(@bitSizeOf(std.lang.BranchHint) * idx_in_bag); |
| 10538 | hints.count += 1; |
| 10539 | return hints.bags.appendAssumeCapacity(bag); |
| 10540 | } |
| 10541 | fn append(hints: *@This(), gpa_inner: Allocator, hint: std.lang.BranchHint) Allocator.Error!void { |
| 10542 | try hints.ensureUnusedCapacity(gpa_inner, 1); |
| 10543 | return hints.appendAssumeCapacity(hint); |
| 10544 | } |
| 10545 | }; |
| 10546 | var branch_hints: BranchHints = hints: { |
| 10547 | const num_bags = @divCeil(estimated_cases_len, BranchHints.hints_per_bag); |
| 10548 | break :hints .{ .bags = try .initCapacity(gpa, num_bags), .count = 0 }; |
| 10549 | }; |
| 10550 | defer branch_hints.bags.deinit(gpa); |
| 10551 | |
| 10552 | var cases_extra: std.ArrayList(u32) = try .initCapacity(gpa, estimated_cases_len * |
| 10553 | @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len); |
| 10554 | defer cases_extra.deinit(gpa); |
| 10555 | |
| 10556 | // We will reuse this block for each case. |
| 10557 | var case_block = child_block.makeSubBlock(); |
| 10558 | case_block.runtime_loop = null; |
| 10559 | case_block.runtime_cond = operand_src; |
| 10560 | case_block.runtime_index.increment(); |
| 10561 | case_block.need_debug_scope = null; // this body is emitted regardless |
| 10562 | defer case_block.instructions.deinit(gpa); |
| 10563 | |
| 10564 | const case_vals = validated_switch.case_vals; |
| 10565 | var case_val_idx: usize = 0; |
| 10566 | var case_it = zir_switch.iterateCases(); |
| 10567 | var extra_index = zir_switch.end; |
| 10568 | |
| 10569 | var under_prong: ?struct { |
| 10570 | index: Zir.UnwrappedSwitchBlock.Case.Index, |
| 10571 | body: []const Zir.Inst.Index, |
| 10572 | capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, |
| 10573 | has_tag_capture: bool, |
| 10574 | } = null; |
| 10575 | |
| 10576 | var cases_len: u32 = 0; |
| 10577 | while (case_it.next()) |case| { |
| 10578 | const item_refs = case_vals[case_val_idx..][0..case.item_infos.len]; |
| 10579 | case_val_idx += item_refs.len; |
| 10580 | const range_refs: []const [2]Air.Inst.Ref = |
| 10581 | @ptrCast(case_vals[case_val_idx..][0 .. 2 * case.range_infos.len]); |
| 10582 | case_val_idx += 2 * range_refs.len; |
| 10583 | |
| 10584 | const prong_info = case.prong_info; |
| 10585 | const prong_body = sema.code.bodySlice(extra_index, prong_info.body_len); |
| 10586 | extra_index += prong_body.len; |
| 10587 | |
| 10588 | // Enough capacity for inlining regular items, we can't really predict |
| 10589 | // how many range items we will end up with (at least not in a safe and |
| 10590 | // cheap manner) so we allocate on demand for those. |
| 10591 | if (prong_info.is_inline) { |
| 10592 | try branch_hints.ensureUnusedCapacity(gpa, @intCast(case.item_infos.len)); |
| 10593 | } |
| 10594 | |
| 10595 | var emit_bb = false; |
| 10596 | var any_analyze_body = false; |
| 10597 | var is_under_prong = false; |
| 10598 | for (case.item_infos, item_refs, 0..) |item_info, item_ref, item_i| { |
| 10599 | if (item_ref == .none) is_under_prong = true; |
| 10600 | if (item_info.bodyLen()) |body_len| extra_index += body_len; |
| 10601 | |
| 10602 | const analyze_body = sema.wantSwitchProngBodyAnalysis(item_ref, operand_ty, tagged_union_originally, err_set, prong_info.is_comptime_unreach); |
| 10603 | if (analyze_body) any_analyze_body = true; |
| 10604 | |
| 10605 | if (prong_info.is_inline) { |
| 10606 | cases_len += 1; |
| 10607 | case_block.instructions.clearRetainingCapacity(); |
| 10608 | case_block.error_return_trace_index = child_block.error_return_trace_index; |
| 10609 | |
| 10610 | if (emit_bb) { |
| 10611 | const bb_src = block.src(.{ .switch_case_item = .{ |
| 10612 | .switch_node_offset = src_node_offset, |
| 10613 | .case_idx = case.index, |
| 10614 | .item_idx = .{ .kind = .single, .value = @intCast(item_i) }, |
| 10615 | } }); |
| 10616 | try sema.emitBackwardBranch(block, bb_src); |
| 10617 | } |
| 10618 | emit_bb = true; |
| 10619 | |
| 10620 | const prong_hint: std.lang.BranchHint = hint: { |
| 10621 | if (analyze_body) break :hint try sema.analyzeSwitchProng( |
| 10622 | &case_block, |
| 10623 | operand, |
| 10624 | operand_ty, |
| 10625 | raw_operand_ty, |
| 10626 | prong_body, |
| 10627 | block.src(.{ .switch_capture = .{ |
| 10628 | .switch_node_offset = src_node_offset, |
| 10629 | .case_idx = case.index, |
| 10630 | } }), |
| 10631 | prong_info.capture, |
| 10632 | prong_info.has_tag_capture, |
| 10633 | .{ .@"inline" = item_ref }, |
| 10634 | validated_switch.else_err_ty, |
| 10635 | switch_inst, |
| 10636 | zir_switch, |
| 10637 | ); |
| 10638 | _ = try case_block.addNoOp(.unreach); |
| 10639 | break :hint .cold; // unreachable branches are cold |
| 10640 | }; |
| 10641 | branch_hints.appendAssumeCapacity(prong_hint); |
| 10642 | |
| 10643 | try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len + |
| 10644 | 1 + // `item`, no ranges |
| 10645 | case_block.instructions.items.len); |
| 10646 | cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ |
| 10647 | .items_len = 1, |
| 10648 | .ranges_len = 0, |
| 10649 | .body_len = @intCast(case_block.instructions.items.len), |
| 10650 | })); |
| 10651 | cases_extra.appendAssumeCapacity(@backingInt(item_ref)); |
| 10652 | cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); |
| 10653 | } |
| 10654 | } |
| 10655 | for (case.range_infos, range_refs, 0..) |range_info, range_ref, range_i| { |
| 10656 | if (range_info[0].bodyLen()) |body_len| extra_index += body_len; |
| 10657 | if (range_info[1].bodyLen()) |body_len| extra_index += body_len; |
| 10658 | |
| 10659 | any_analyze_body = true; // always an integer range, always needs analysis |
| 10660 | |
| 10661 | if (prong_info.is_inline) { |
| 10662 | var item = sema.resolveValue(range_ref[0]).?; |
| 10663 | const item_last = sema.resolveValue(range_ref[1]).?; |
| 10664 | |
| 10665 | if (item.getUnsignedInt(zcu)) |first_int| { |
| 10666 | if (item_last.getUnsignedInt(zcu)) |last_int| { |
| 10667 | if (std.math.cast(u32, last_int - first_int)) |range_len| { |
| 10668 | try branch_hints.ensureUnusedCapacity(gpa, range_len); |
| 10669 | } |
| 10670 | } |
| 10671 | } |
| 10672 | |
| 10673 | var prev_result_overflowed = false; |
| 10674 | while (item.compareScalar(.lte, item_last, item_ty, zcu)) : ({ |
| 10675 | assert(!prev_result_overflowed); |
| 10676 | const result = try arith.incrementDefinedInt(sema, item_ty, item); |
| 10677 | prev_result_overflowed = result.overflow; |
| 10678 | item = result.val; |
| 10679 | }) { |
| 10680 | cases_len += 1; |
| 10681 | case_block.instructions.clearRetainingCapacity(); |
| 10682 | case_block.error_return_trace_index = child_block.error_return_trace_index; |
| 10683 | |
| 10684 | const item_ref: Air.Inst.Ref = .fromValue(item); |
| 10685 | |
| 10686 | if (emit_bb) { |
| 10687 | const bb_src = block.src(.{ .switch_case_item = .{ |
| 10688 | .switch_node_offset = src_node_offset, |
| 10689 | .case_idx = case.index, |
| 10690 | .item_idx = .{ .kind = .range, .value = @intCast(range_i) }, |
| 10691 | } }); |
| 10692 | try sema.emitBackwardBranch(block, bb_src); |
| 10693 | } |
| 10694 | emit_bb = true; |
| 10695 | |
| 10696 | const prong_hint = try sema.analyzeSwitchProng( |
| 10697 | &case_block, |
| 10698 | operand, |
| 10699 | operand_ty, |
| 10700 | raw_operand_ty, |
| 10701 | prong_body, |
| 10702 | block.src(.{ .switch_capture = .{ |
| 10703 | .switch_node_offset = src_node_offset, |
| 10704 | .case_idx = case.index, |
| 10705 | } }), |
| 10706 | prong_info.capture, |
| 10707 | prong_info.has_tag_capture, |
| 10708 | .{ .@"inline" = item_ref }, |
| 10709 | validated_switch.else_err_ty, |
| 10710 | switch_inst, |
| 10711 | zir_switch, |
| 10712 | ); |
| 10713 | try branch_hints.append(gpa, prong_hint); |
| 10714 | |
| 10715 | try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len + |
| 10716 | 1 + // `item`, no ranges |
| 10717 | case_block.instructions.items.len); |
| 10718 | cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ |
| 10719 | .items_len = 1, |
| 10720 | .ranges_len = 0, |
| 10721 | .body_len = @intCast(case_block.instructions.items.len), |
| 10722 | })); |
| 10723 | cases_extra.appendAssumeCapacity(@backingInt(item_ref)); |
| 10724 | cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); |
| 10725 | } |
| 10726 | } |
| 10727 | } |
| 10728 | |
| 10729 | if (prong_info.is_inline) continue; // handled above |
| 10730 | |
| 10731 | if (is_under_prong) { |
| 10732 | // We will handle this later. If there are any named items specified |
| 10733 | // along with the `_`, we don't have to actually emit any AIR for them |
| 10734 | // as they will be 'absorbed' by the `_` (the catch-all prong) anyway. |
| 10735 | under_prong = .{ |
| 10736 | .index = case.index, |
| 10737 | .body = prong_body, |
| 10738 | .capture = case.prong_info.capture, |
| 10739 | .has_tag_capture = case.prong_info.has_tag_capture, |
| 10740 | }; |
| 10741 | continue; |
| 10742 | } |
| 10743 | |
| 10744 | cases_len += 1; |
| 10745 | case_block.instructions.clearRetainingCapacity(); |
| 10746 | case_block.error_return_trace_index = child_block.error_return_trace_index; |
| 10747 | |
| 10748 | const prong_hint: std.lang.BranchHint = hint: { |
| 10749 | if (any_analyze_body) break :hint try sema.analyzeSwitchProng( |
| 10750 | &case_block, |
| 10751 | operand, |
| 10752 | operand_ty, |
| 10753 | raw_operand_ty, |
| 10754 | prong_body, |
| 10755 | block.src(.{ .switch_capture = .{ |
| 10756 | .switch_node_offset = src_node_offset, |
| 10757 | .case_idx = case.index, |
| 10758 | } }), |
| 10759 | prong_info.capture, |
| 10760 | prong_info.has_tag_capture, |
| 10761 | if (range_refs.len > 0) .has_ranges else .{ .item_refs = item_refs }, |
| 10762 | validated_switch.else_err_ty, |
| 10763 | switch_inst, |
| 10764 | zir_switch, |
| 10765 | ); |
| 10766 | _ = try case_block.addNoOp(.unreach); |
| 10767 | break :hint .cold; // unreachable branches are cold |
| 10768 | }; |
| 10769 | try branch_hints.append(gpa, prong_hint); |
| 10770 | |
| 10771 | try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len + |
| 10772 | item_refs.len + |
| 10773 | 2 * range_refs.len + |
| 10774 | case_block.instructions.items.len); |
| 10775 | cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ |
| 10776 | .items_len = @intCast(item_refs.len), |
| 10777 | .ranges_len = @intCast(range_refs.len), |
| 10778 | .body_len = @intCast(case_block.instructions.items.len), |
| 10779 | })); |
| 10780 | cases_extra.appendSliceAssumeCapacity(@ptrCast(item_refs)); |
| 10781 | cases_extra.appendSliceAssumeCapacity(@ptrCast(range_refs)); |
| 10782 | cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); |
| 10783 | } |
| 10784 | |
| 10785 | const catch_all_extra: []const u32 = catch_all_extra: { |
| 10786 | if (!has_else and !has_under and !case_block.wantSafety()) { |
| 10787 | try branch_hints.append(gpa, .none); |
| 10788 | break :catch_all_extra &.{}; |
| 10789 | } |
| 10790 | var emit_bb = false; |
| 10791 | if (has_else and else_case.is_inline) { |
| 10792 | const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset }); |
| 10793 | const error_names, const min_int = check_enumerable: { |
| 10794 | switch (item_ty.zigTypeTag(zcu)) { |
| 10795 | .@"enum" => if (else_is_named_only or |
| 10796 | !item_ty.isNonexhaustiveEnum(zcu) or tagged_union_originally) |
| 10797 | { |
| 10798 | try branch_hints.ensureUnusedCapacity(gpa, @intCast(validated_switch.seen.enum_fields.len)); |
| 10799 | break :check_enumerable .{ undefined, undefined }; |
| 10800 | }, |
| 10801 | .error_set => if (!operand_ty.isAnyError(zcu)) { |
| 10802 | const error_names = item_ty.errorSetNames(zcu); |
| 10803 | try branch_hints.ensureUnusedCapacity(gpa, error_names.len); |
| 10804 | break :check_enumerable .{ error_names, undefined }; |
| 10805 | }, |
| 10806 | .int => { |
| 10807 | const min_int = try item_ty.minInt(pt, item_ty); |
| 10808 | break :check_enumerable .{ undefined, min_int }; |
| 10809 | }, |
| 10810 | .@"union", .@"struct" => { |
| 10811 | const backing_int_ty = item_ty.backingIntType(zcu); |
| 10812 | const min_backing_int = try backing_int_ty.minInt(pt, backing_int_ty); |
| 10813 | break :check_enumerable .{ undefined, min_backing_int }; |
| 10814 | }, |
| 10815 | .bool, .void => break :check_enumerable .{ undefined, undefined }, |
| 10816 | else => {}, |
| 10817 | } |
| 10818 | return sema.fail(block, else_prong_src, "cannot enumerate values of type '{f}' for 'inline else'", .{ |
| 10819 | item_ty.fmt(pt), |
| 10820 | }); |
| 10821 | }; |
| 10822 | var unhandled_it = validated_switch.iterateUnhandledItems(error_names, min_int); |
| 10823 | while (try unhandled_it.next(sema, item_ty)) |item_val| { |
| 10824 | cases_len += 1; |
| 10825 | case_block.instructions.clearRetainingCapacity(); |
| 10826 | case_block.error_return_trace_index = child_block.error_return_trace_index; |
| 10827 | |
| 10828 | const item_ref: Air.Inst.Ref = .fromValue(item_val); |
| 10829 | |
| 10830 | const analyze_body = sema.wantSwitchProngBodyAnalysis(item_ref, operand_ty, tagged_union_originally, err_set, false); |
| 10831 | |
| 10832 | if (emit_bb) try sema.emitBackwardBranch(block, else_prong_src); |
| 10833 | emit_bb = true; |
| 10834 | |
| 10835 | const prong_hint: std.lang.BranchHint = hint: { |
| 10836 | if (analyze_body) break :hint try sema.analyzeSwitchProng( |
| 10837 | &case_block, |
| 10838 | operand, |
| 10839 | operand_ty, |
| 10840 | raw_operand_ty, |
| 10841 | else_case.body, |
| 10842 | block.src(.{ .switch_capture = .{ |
| 10843 | .switch_node_offset = src_node_offset, |
| 10844 | .case_idx = else_case.index, |
| 10845 | } }), |
| 10846 | else_case.capture, |
| 10847 | else_case.has_tag_capture, |
| 10848 | .{ .@"inline" = item_ref }, |
| 10849 | validated_switch.else_err_ty, |
| 10850 | switch_inst, |
| 10851 | zir_switch, |
| 10852 | ); |
| 10853 | _ = try case_block.addNoOp(.unreach); |
| 10854 | break :hint .cold; // unreachable branches are cold |
| 10855 | }; |
| 10856 | try branch_hints.append(gpa, prong_hint); |
| 10857 | |
| 10858 | try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len + |
| 10859 | 1 + // `item`, no ranges |
| 10860 | case_block.instructions.items.len); |
| 10861 | cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ |
| 10862 | .items_len = 1, |
| 10863 | .ranges_len = 0, |
| 10864 | .body_len = @intCast(case_block.instructions.items.len), |
| 10865 | })); |
| 10866 | cases_extra.appendAssumeCapacity(@backingInt(item_ref)); |
| 10867 | cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); |
| 10868 | } |
| 10869 | } |
| 10870 | |
| 10871 | case_block.instructions.clearRetainingCapacity(); |
| 10872 | case_block.error_return_trace_index = child_block.error_return_trace_index; |
| 10873 | |
| 10874 | if (zcu.backendSupportsFeature(.is_named_enum_value) and |
| 10875 | (has_else or has_under) and block.wantSafety() and |
| 10876 | item_ty.zigTypeTag(zcu) == .@"enum" and |
| 10877 | (!operand_ty.isNonexhaustiveEnum(zcu) or tagged_union_originally)) |
| 10878 | { |
| 10879 | try sema.zirDbgStmt(&case_block, cond_dbg_node_index); |
| 10880 | const ok = try case_block.addUnOp(.is_named_enum_value, cond_ref); |
| 10881 | try sema.addSafetyCheck(&case_block, src, ok, .corrupt_switch); |
| 10882 | } |
| 10883 | |
| 10884 | if (else_is_named_only and !else_case.is_inline) { |
| 10885 | // If we have both an `else` and an `_` prong, all named values go |
| 10886 | // into the `else` prong and all unnamed ones go into the `_` prong. |
| 10887 | // We will manually enumerate all named values which haven't been |
| 10888 | // encountered yet and create an extra prong for them, which will |
| 10889 | // evaulate to the `else` body. |
| 10890 | |
| 10891 | assert(operand_ty.isNonexhaustiveEnum(zcu)); |
| 10892 | |
| 10893 | cases_len += 1; |
| 10894 | |
| 10895 | const prong_hint: std.lang.BranchHint = hint: { |
| 10896 | if (!else_case.is_inline) break :hint try sema.analyzeSwitchProng( |
| 10897 | &case_block, |
| 10898 | operand, |
| 10899 | operand_ty, |
| 10900 | raw_operand_ty, |
| 10901 | else_case.body, |
| 10902 | block.src(.{ .switch_capture = .{ |
| 10903 | .switch_node_offset = src_node_offset, |
| 10904 | .case_idx = else_case.index, |
| 10905 | } }), |
| 10906 | else_case.capture, |
| 10907 | else_case.has_tag_capture, |
| 10908 | .special, |
| 10909 | validated_switch.else_err_ty, |
| 10910 | switch_inst, |
| 10911 | zir_switch, |
| 10912 | ); |
| 10913 | _ = try case_block.addNoOp(.unreach); |
| 10914 | break :hint .cold; // unreachable branches are cold |
| 10915 | }; |
| 10916 | try branch_hints.append(gpa, prong_hint); |
| 10917 | |
| 10918 | try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len + |
| 10919 | (validated_switch.seen.enum_fields.len + 1 - zir_switch.totalItemsLen()) + // +1 because totalItemsLen includes the _ |
| 10920 | case_block.instructions.items.len); |
| 10921 | const extra_case = cases_extra.addManyAsArrayAssumeCapacity( |
| 10922 | @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len, |
| 10923 | ); |
| 10924 | var items_len: u32 = 0; |
| 10925 | for (validated_switch.seen.enum_fields, 0..) |seen_field, field_i| { |
| 10926 | if (seen_field != null) continue; |
| 10927 | const item_val = try pt.enumValueFieldIndex(item_ty, @intCast(field_i)); |
| 10928 | const item_ref: Air.Inst.Ref = .fromValue(item_val); |
| 10929 | cases_extra.appendAssumeCapacity(@backingInt(item_ref)); |
| 10930 | items_len += 1; |
| 10931 | } |
| 10932 | assert(items_len > 0); // `else` must be reachable at this point |
| 10933 | extra_case.* = payloadToExtraItems(Air.SwitchBr.Case{ |
| 10934 | .items_len = items_len, |
| 10935 | .ranges_len = 0, |
| 10936 | .body_len = @intCast(case_block.instructions.items.len), |
| 10937 | }); |
| 10938 | cases_extra.appendSliceAssumeCapacity(@ptrCast(case_block.instructions.items)); |
| 10939 | |
| 10940 | // We fall through to the regular catch-all prong generation. |
| 10941 | |
| 10942 | case_block.instructions.clearRetainingCapacity(); |
| 10943 | case_block.error_return_trace_index = child_block.error_return_trace_index; |
| 10944 | } |
| 10945 | |
| 10946 | const analyze_catch_all_body = analyze_body: { |
| 10947 | if (has_under) { |
| 10948 | assert(!tagged_union_originally); |
| 10949 | assert(!err_set); |
| 10950 | break :analyze_body true; // can never be inline |
| 10951 | } else if (has_else) { |
| 10952 | if (else_case.is_inline) break :analyze_body false; // already handled above |
| 10953 | } else { |
| 10954 | break :analyze_body false; // we still may want a safety check! |
| 10955 | } |
| 10956 | if (tagged_union_originally) { |
| 10957 | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 10958 | for (validated_switch.seen.enum_fields, 0..) |seen_field, field_i| { |
| 10959 | if (seen_field != null) continue; |
| 10960 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_i]); |
| 10961 | if (!field_ty.isNoReturn(zcu)) break :analyze_body true; |
| 10962 | } |
| 10963 | break :analyze_body false; |
| 10964 | } |
| 10965 | if (err_set) { |
| 10966 | const else_err_ty = validated_switch.else_err_ty orelse { |
| 10967 | assert(else_case.is_simple_noreturn); |
| 10968 | break :analyze_body false; |
| 10969 | }; |
| 10970 | if (else_err_ty.errorSetIsEmpty(zcu)) break :analyze_body false; |
| 10971 | } |
| 10972 | break :analyze_body true; |
| 10973 | }; |
| 10974 | |
| 10975 | const catch_all_hint = hint: { |
| 10976 | if (analyze_catch_all_body) { |
| 10977 | const index, const body, const capture, const has_tag_capture = if (under_prong) |under| |
| 10978 | .{ under.index, under.body, under.capture, under.has_tag_capture } |
| 10979 | else |
| 10980 | .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture }; |
| 10981 | break :hint try sema.analyzeSwitchProng( |
| 10982 | &case_block, |
| 10983 | operand, |
| 10984 | operand_ty, |
| 10985 | raw_operand_ty, |
| 10986 | body, |
| 10987 | block.src(.{ .switch_capture = .{ |
| 10988 | .switch_node_offset = src_node_offset, |
| 10989 | .case_idx = index, |
| 10990 | } }), |
| 10991 | capture, |
| 10992 | has_tag_capture, |
| 10993 | .special, |
| 10994 | validated_switch.else_err_ty, |
| 10995 | switch_inst, |
| 10996 | zir_switch, |
| 10997 | ); |
| 10998 | } |
| 10999 | // We still need a terminator in this block, but we have proven |
| 11000 | // that it is unreachable. |
| 11001 | if (case_block.wantSafety()) { |
| 11002 | try sema.zirDbgStmt(&case_block, cond_dbg_node_index); |
| 11003 | try sema.safetyPanic(&case_block, src, .corrupt_switch); |
| 11004 | } else { |
| 11005 | _ = try case_block.addNoOp(.unreach); |
| 11006 | } |
| 11007 | break :hint .cold; // Safety check / unreachable branches are cold. |
| 11008 | }; |
| 11009 | try branch_hints.append(gpa, catch_all_hint); |
| 11010 | break :catch_all_extra @ptrCast(case_block.instructions.items); |
| 11011 | }; |
| 11012 | |
| 11013 | assert(branch_hints.count == cases_len + 1); // +1 for catch-all hint |
| 11014 | |
| 11015 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).@"struct".field_names.len + |
| 11016 | branch_hints.bags.items.len + |
| 11017 | cases_extra.items.len + |
| 11018 | catch_all_extra.len); |
| 11019 | const payload_index = sema.addExtraAssumeCapacity(Air.SwitchBr{ |
| 11020 | .cases_len = @intCast(cases_len), |
| 11021 | .else_body_len = @intCast(catch_all_extra.len), |
| 11022 | }); |
| 11023 | sema.air_extra.appendSliceAssumeCapacity(branch_hints.bags.items); |
| 11024 | sema.air_extra.appendSliceAssumeCapacity(cases_extra.items); |
| 11025 | sema.air_extra.appendSliceAssumeCapacity(catch_all_extra); |
| 11026 | |
| 11027 | const air_tag: Air.Inst.Tag = if (operand == .loop and merges.extra_insts.items.len > 0) |
| 11028 | .loop_switch_br |
| 11029 | else |
| 11030 | .switch_br; |
| 11031 | const air_ref = try child_block.addInst(.{ |
| 11032 | .tag = air_tag, |
| 11033 | .data = .{ .pl_op = .{ |
| 11034 | .operand = cond_ref, |
| 11035 | .payload = payload_index, |
| 11036 | } }, |
| 11037 | }); |
| 11038 | return air_ref; |
| 11039 | } |
| 11040 | |
| 11041 | const ValidatedSwitchBlock = struct { |
| 11042 | seen: Seen, |
| 11043 | case_vals: []const Air.Inst.Ref, |
| 11044 | else_case: Zir.UnwrappedSwitchBlock.Case.Else, |
| 11045 | else_err_ty: ?Type, |
| 11046 | |
| 11047 | const Seen = struct { |
| 11048 | enum_fields: []?LazySrcLoc, |
| 11049 | errors: std.AutoHashMapUnmanaged(InternPool.NullTerminatedString, LazySrcLoc), |
| 11050 | sparse_values: std.AutoHashMapUnmanaged(InternPool.Index, LazySrcLoc), |
| 11051 | ranges: RangeSet, |
| 11052 | true_src: ?LazySrcLoc, |
| 11053 | false_src: ?LazySrcLoc, |
| 11054 | void_src: ?LazySrcLoc, |
| 11055 | }; |
| 11056 | |
| 11057 | fn iterateUnhandledItems( |
| 11058 | validated_switch: *const ValidatedSwitchBlock, |
| 11059 | /// May be `undefined` if `item_ty` isn't an `error_set`. |
| 11060 | error_names: InternPool.NullTerminatedString.Slice, |
| 11061 | /// May be `undefined` if `item_ty` isn't an `int`. |
| 11062 | min_int: Value, |
| 11063 | ) UnhandledIterator { |
| 11064 | return .{ |
| 11065 | .error_names = error_names, |
| 11066 | .seen = &validated_switch.seen, |
| 11067 | |
| 11068 | .next_idx = 0, |
| 11069 | .next_val = min_int, |
| 11070 | .handled_true = validated_switch.seen.true_src != null, |
| 11071 | .handled_false = validated_switch.seen.false_src != null, |
| 11072 | .handled_void = validated_switch.seen.void_src != null, |
| 11073 | }; |
| 11074 | } |
| 11075 | |
| 11076 | const UnhandledIterator = struct { |
| 11077 | error_names: InternPool.NullTerminatedString.Slice, |
| 11078 | seen: *const Seen, |
| 11079 | |
| 11080 | next_idx: u32, |
| 11081 | next_val: ?Value, |
| 11082 | handled_true: bool, |
| 11083 | handled_false: bool, |
| 11084 | handled_void: bool, |
| 11085 | |
| 11086 | fn next(it: *UnhandledIterator, sema: *Sema, item_ty: Type) CompileError!?Value { |
| 11087 | const pt = sema.pt; |
| 11088 | const zcu = pt.zcu; |
| 11089 | const ip = &zcu.intern_pool; |
| 11090 | switch (item_ty.zigTypeTag(zcu)) { |
| 11091 | .@"enum" => { |
| 11092 | for (it.seen.enum_fields[it.next_idx..], it.next_idx..) |seen_field, field_i| { |
| 11093 | if (seen_field != null) continue; |
| 11094 | it.next_idx = @intCast(field_i + 1); |
| 11095 | return try pt.enumValueFieldIndex(item_ty, @intCast(field_i)); |
| 11096 | } |
| 11097 | return null; |
| 11098 | }, |
| 11099 | .error_set => { |
| 11100 | for (it.error_names.get(ip)[it.next_idx..], it.next_idx..) |err_name, name_i| { |
| 11101 | if (it.seen.errors.contains(err_name)) continue; |
| 11102 | it.next_idx = @intCast(name_i + 1); |
| 11103 | return .fromInterned(try pt.intern(.{ .err = .{ |
| 11104 | .ty = item_ty.toIntern(), |
| 11105 | .name = err_name, |
| 11106 | } })); |
| 11107 | } |
| 11108 | return null; |
| 11109 | }, |
| 11110 | .int, .@"union", .@"struct" => |type_tag| { |
| 11111 | var cur_val = it.next_val orelse return null; |
| 11112 | const int_ty = switch (type_tag) { |
| 11113 | .int => item_ty, |
| 11114 | .@"union", .@"struct" => item_ty.backingIntType(zcu), |
| 11115 | else => unreachable, |
| 11116 | }; |
| 11117 | while (it.next_idx < it.seen.ranges.list.len and |
| 11118 | cur_val.eql(it.seen.ranges.list.items(.first)[it.next_idx], int_ty, zcu)) |
| 11119 | { |
| 11120 | defer it.next_idx += 1; |
| 11121 | const incr = try arith.incrementDefinedInt( |
| 11122 | sema, |
| 11123 | int_ty, |
| 11124 | it.seen.ranges.list.items(.last)[it.next_idx], |
| 11125 | ); |
| 11126 | if (incr.overflow) { |
| 11127 | it.next_val = null; |
| 11128 | return null; |
| 11129 | } |
| 11130 | cur_val = incr.val; |
| 11131 | } |
| 11132 | const incr = try arith.incrementDefinedInt(sema, int_ty, cur_val); |
| 11133 | it.next_val = if (incr.overflow) null else incr.val; |
| 11134 | return switch (type_tag) { |
| 11135 | .int => cur_val, |
| 11136 | .@"union", .@"struct" => try pt.bitpackValue(item_ty, cur_val), |
| 11137 | else => unreachable, |
| 11138 | }; |
| 11139 | }, |
| 11140 | .bool => { |
| 11141 | if (!it.handled_true) { |
| 11142 | it.handled_true = true; |
| 11143 | return .true; |
| 11144 | } |
| 11145 | if (!it.handled_false) { |
| 11146 | it.handled_false = true; |
| 11147 | return .false; |
| 11148 | } |
| 11149 | return null; |
| 11150 | }, |
| 11151 | .void => { |
| 11152 | if (!it.handled_void) { |
| 11153 | it.handled_void = true; |
| 11154 | return .void; |
| 11155 | } |
| 11156 | return null; |
| 11157 | }, |
| 11158 | else => unreachable, // item type is not enumerable |
| 11159 | } |
| 11160 | } |
| 11161 | }; |
| 11162 | }; |
| 11163 | |
| 11164 | /// Validates operand type and `else`/`_` prong usage, resolves all prong items |
| 11165 | /// and checks them for duplicates/invalid ranges. Does not emit into `block`. |
| 11166 | /// Reserves inst map space for all placeholders associated with `zir_switch`. |
| 11167 | /// Contents of returned `ValidatedSwitchBlock` belong to `sema.arena`. |
| 11168 | fn validateSwitchBlock( |
| 11169 | sema: *Sema, |
| 11170 | block: *Block, |
| 11171 | raw_operand: Air.Inst.Ref, |
| 11172 | operand_is_ref: bool, |
| 11173 | switch_inst: Zir.Inst.Index, |
| 11174 | zir_switch: *const Zir.UnwrappedSwitchBlock, |
| 11175 | ) CompileError!ValidatedSwitchBlock { |
| 11176 | const pt = sema.pt; |
| 11177 | const zcu = pt.zcu; |
| 11178 | const ip = &zcu.intern_pool; |
| 11179 | const gpa = sema.gpa; |
| 11180 | const arena = sema.arena; |
| 11181 | |
| 11182 | const src_node_offset = zir_switch.switch_src_node_offset; |
| 11183 | const src = block.nodeOffset(src_node_offset); |
| 11184 | const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset }); |
| 11185 | const else_prong_src = block.src(.{ .node_offset_switch_else_prong = src_node_offset }); |
| 11186 | var extra_index = zir_switch.end; |
| 11187 | |
| 11188 | // We want to map values to our placeholders later on. |
| 11189 | if (zir_switch.payload_capture_placeholder.unwrap()) |payload_capture_inst| { |
| 11190 | assert(payload_capture_inst != switch_inst); // malformed zir |
| 11191 | try sema.inst_map.ensureSpaceForInstructions(gpa, &.{payload_capture_inst}); |
| 11192 | } |
| 11193 | if (zir_switch.tag_capture_placeholder.unwrap()) |tag_capture_inst| { |
| 11194 | assert(tag_capture_inst != switch_inst); // malformed zir |
| 11195 | try sema.inst_map.ensureSpaceForInstructions(gpa, &.{tag_capture_inst}); |
| 11196 | } |
| 11197 | |
| 11198 | const operand_ty = operand_ty: { |
| 11199 | const raw_operand_ty = sema.typeOf(raw_operand); |
| 11200 | if (operand_is_ref) { |
| 11201 | try sema.checkPtrType(block, operand_src, raw_operand_ty, false); |
| 11202 | const child_ty = raw_operand_ty.childType(zcu); |
| 11203 | try sema.ensureLayoutResolved(child_ty, operand_src, .ptr_access); |
| 11204 | break :operand_ty child_ty; |
| 11205 | } |
| 11206 | break :operand_ty raw_operand_ty; |
| 11207 | }; |
| 11208 | |
| 11209 | const item_ty: Type = item_ty: { |
| 11210 | switch (operand_ty.zigTypeTag(zcu)) { |
| 11211 | .@"enum", |
| 11212 | .error_set, |
| 11213 | .int, |
| 11214 | .comptime_int, |
| 11215 | .type, |
| 11216 | .enum_literal, |
| 11217 | .@"fn", |
| 11218 | .bool, |
| 11219 | .void, |
| 11220 | => break :item_ty operand_ty, |
| 11221 | |
| 11222 | .@"union" => { |
| 11223 | operand_ty.assertHasLayout(zcu); |
| 11224 | const union_obj = ip.loadUnionType(operand_ty.toIntern()); |
| 11225 | switch (union_obj.tag_usage) { |
| 11226 | .tagged => break :item_ty .fromInterned(union_obj.enum_tag_type), |
| 11227 | .none => if (union_obj.layout == .@"packed") break :item_ty operand_ty, |
| 11228 | .safety => {}, |
| 11229 | } |
| 11230 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 11231 | const msg = try sema.errMsg(operand_src, "switch on union with no attached enum", .{}); |
| 11232 | errdefer msg.destroy(sema.gpa); |
| 11233 | if (operand_ty.srcLocOrNull(zcu)) |union_src| { |
| 11234 | try sema.errNote(union_src, msg, "consider 'union(enum)' here", .{}); |
| 11235 | } |
| 11236 | break :msg msg; |
| 11237 | }); |
| 11238 | }, |
| 11239 | |
| 11240 | .@"struct" => { |
| 11241 | operand_ty.assertHasLayout(zcu); |
| 11242 | if (operand_ty.containerLayout(zcu) == .@"packed") break :item_ty operand_ty; |
| 11243 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 11244 | const msg = try sema.errMsg(operand_src, "switch on non-packed struct", .{}); |
| 11245 | errdefer msg.destroy(sema.gpa); |
| 11246 | try sema.addDeclaredHereNote(msg, operand_ty); |
| 11247 | break :msg msg; |
| 11248 | }); |
| 11249 | }, |
| 11250 | |
| 11251 | .pointer => if (!operand_ty.isSlice(zcu)) break :item_ty operand_ty, |
| 11252 | |
| 11253 | .optional => return sema.failWithOwnedErrorMsg(block, msg: { |
| 11254 | const msg = try sema.errMsg(operand_src, "switch on optional type '{f}'", .{ |
| 11255 | operand_ty.fmt(pt), |
| 11256 | }); |
| 11257 | errdefer msg.destroy(gpa); |
| 11258 | try sema.errNote(operand_src, msg, "consider using '.?', 'orelse', or 'if'", .{}); |
| 11259 | break :msg msg; |
| 11260 | }), |
| 11261 | |
| 11262 | .error_union => return sema.failWithOwnedErrorMsg(block, msg: { |
| 11263 | const msg = try sema.errMsg(operand_src, "switch on error union type '{f}'", .{ |
| 11264 | operand_ty.fmt(pt), |
| 11265 | }); |
| 11266 | errdefer msg.destroy(gpa); |
| 11267 | try sema.errNote(operand_src, msg, "consider using 'try', 'catch', or 'if'", .{}); |
| 11268 | break :msg msg; |
| 11269 | }), |
| 11270 | |
| 11271 | .noreturn, |
| 11272 | .float, |
| 11273 | .comptime_float, |
| 11274 | .array, |
| 11275 | .vector, |
| 11276 | .undefined, |
| 11277 | .null, |
| 11278 | .@"opaque", |
| 11279 | .frame, |
| 11280 | .@"anyframe", |
| 11281 | .spirv, |
| 11282 | => {}, |
| 11283 | } |
| 11284 | return sema.fail(block, operand_src, "switch on type '{f}'", .{operand_ty.fmt(pt)}); |
| 11285 | }; |
| 11286 | |
| 11287 | if (zir_switch.has_continue and !block.isComptime()) { |
| 11288 | if (operand_ty.comptimeOnly(zcu)) { |
| 11289 | // Even if the operand is comptime-known, this `switch` is runtime. |
| 11290 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 11291 | const msg = try sema.errMsg(operand_src, "operand of switch loop has comptime-only type '{f}'", .{operand_ty.fmt(pt)}); |
| 11292 | errdefer msg.destroy(gpa); |
| 11293 | try sema.errNote(operand_src, msg, "switch loops are evaluated at runtime outside of comptime scopes", .{}); |
| 11294 | try sema.explainWhyTypeIsComptime(msg, operand_src, operand_ty); |
| 11295 | break :msg msg; |
| 11296 | }); |
| 11297 | } |
| 11298 | try sema.validateRuntimeValue(block, operand_src, raw_operand); |
| 11299 | } |
| 11300 | |
| 11301 | const has_else = zir_switch.else_case != null; |
| 11302 | const has_under = zir_switch.has_under; |
| 11303 | |
| 11304 | var case_vals: std.ArrayList(Air.Inst.Ref) = try .initCapacity(arena, zir_switch.item_infos.len); |
| 11305 | |
| 11306 | // Duplicate checking variables later also used for `inline else`. |
| 11307 | var seen: ValidatedSwitchBlock.Seen = .{ |
| 11308 | .enum_fields = &.{}, |
| 11309 | .errors = .empty, |
| 11310 | .sparse_values = .empty, |
| 11311 | .ranges = .empty, |
| 11312 | .true_src = null, |
| 11313 | .false_src = null, |
| 11314 | .void_src = null, |
| 11315 | }; |
| 11316 | |
| 11317 | var else_err_ty: ?Type = null; |
| 11318 | |
| 11319 | const else_case = zir_switch.else_case orelse undefined; |
| 11320 | |
| 11321 | switch (item_ty.zigTypeTag(zcu)) { |
| 11322 | .@"enum" => { |
| 11323 | seen.enum_fields = try arena.alloc(?LazySrcLoc, item_ty.enumFieldCount(zcu)); |
| 11324 | @memset(seen.enum_fields, null); |
| 11325 | // `seen.ranges` is used for non-exhaustive enum values that do not |
| 11326 | // correspond to any tags. Since this is rare, we only allocate on |
| 11327 | // demand in `validateSwitchItem`. |
| 11328 | }, |
| 11329 | .error_set => { |
| 11330 | try seen.errors.ensureUnusedCapacity(arena, zir_switch.totalItemsLen()); |
| 11331 | }, |
| 11332 | .int, .comptime_int, .@"union", .@"struct" => { |
| 11333 | try seen.ranges.ensureUnusedCapacity(arena, zir_switch.totalItemsLen()); |
| 11334 | }, |
| 11335 | .enum_literal, .@"fn", .pointer, .type => { |
| 11336 | try seen.sparse_values.ensureUnusedCapacity(arena, zir_switch.totalItemsLen()); |
| 11337 | }, |
| 11338 | .bool, .void => {}, |
| 11339 | |
| 11340 | else => unreachable, |
| 11341 | } |
| 11342 | |
| 11343 | // Validate for duplicate items and invalid ranges. |
| 11344 | var case_it = zir_switch.iterateCases(); |
| 11345 | while (case_it.next()) |case| { |
| 11346 | const prong_info = case.prong_info; |
| 11347 | extra_index += prong_info.body_len; |
| 11348 | for (case.item_infos, 0..) |item_info, item_i| { |
| 11349 | const item_src = block.src(.{ .switch_case_item = .{ |
| 11350 | .switch_node_offset = src_node_offset, |
| 11351 | .case_idx = case.index, |
| 11352 | .item_idx = .{ .kind = .single, .value = @intCast(item_i) }, |
| 11353 | } }); |
| 11354 | if (item_info.unwrap() == .under) { |
| 11355 | if (!operand_ty.isNonexhaustiveEnum(zcu)) return sema.failWithOwnedErrorMsg(block, msg: { |
| 11356 | const msg = try sema.errMsg( |
| 11357 | src, |
| 11358 | "'_' prong only allowed when switching on non-exhaustive enums", |
| 11359 | .{}, |
| 11360 | ); |
| 11361 | errdefer msg.destroy(gpa); |
| 11362 | try sema.errNote( |
| 11363 | item_src, |
| 11364 | msg, |
| 11365 | "'_' prong here", |
| 11366 | .{}, |
| 11367 | ); |
| 11368 | try sema.errNote( |
| 11369 | src, |
| 11370 | msg, |
| 11371 | "consider using 'else'", |
| 11372 | .{}, |
| 11373 | ); |
| 11374 | break :msg msg; |
| 11375 | }); |
| 11376 | case_vals.appendAssumeCapacity(.none); |
| 11377 | } else { |
| 11378 | const item, extra_index = try sema.resolveSwitchItem(block, item_src, item_ty, item_info, extra_index, switch_inst, prong_info.is_comptime_unreach); |
| 11379 | try sema.validateSwitchItemOrRange(block, item_src, item.val, null, item_ty, &seen); |
| 11380 | case_vals.appendAssumeCapacity(item.ref); |
| 11381 | } |
| 11382 | } |
| 11383 | for (case.range_infos, 0..) |range_info, range_i| { |
| 11384 | const range_offset: LazySrcLoc.Offset.SwitchItem = .{ |
| 11385 | .switch_node_offset = src_node_offset, |
| 11386 | .case_idx = case.index, |
| 11387 | .item_idx = .{ .kind = .range, .value = @intCast(range_i) }, |
| 11388 | }; |
| 11389 | const range_src = block.src(.{ .switch_case_item = range_offset }); |
| 11390 | const first_src = block.src(.{ .switch_case_item_range_first = range_offset }); |
| 11391 | const last_src = block.src(.{ .switch_case_item_range_last = range_offset }); |
| 11392 | const first_item, extra_index = try sema.resolveSwitchItem(block, first_src, item_ty, range_info[0], extra_index, switch_inst, prong_info.is_comptime_unreach); |
| 11393 | const last_item, extra_index = try sema.resolveSwitchItem(block, last_src, item_ty, range_info[1], extra_index, switch_inst, prong_info.is_comptime_unreach); |
| 11394 | try sema.validateSwitchItemOrRange(block, range_src, first_item.val, last_item.val, item_ty, &seen); |
| 11395 | case_vals.appendSliceAssumeCapacity(&.{ first_item.ref, last_item.ref }); |
| 11396 | } |
| 11397 | } |
| 11398 | |
| 11399 | switch (item_ty.zigTypeTag(zcu)) { |
| 11400 | .int, .comptime_int => {}, |
| 11401 | else => if (zir_switch.anyRanges()) { |
| 11402 | const range_src = block.src(.{ .node_offset_switch_range = src_node_offset }); |
| 11403 | const msg = msg: { |
| 11404 | const msg = try sema.errMsg( |
| 11405 | operand_src, |
| 11406 | "ranges not allowed when switching on type '{f}'", |
| 11407 | .{operand_ty.fmt(pt)}, |
| 11408 | ); |
| 11409 | errdefer msg.destroy(gpa); |
| 11410 | try sema.errNote( |
| 11411 | range_src, |
| 11412 | msg, |
| 11413 | "range here", |
| 11414 | .{}, |
| 11415 | ); |
| 11416 | break :msg msg; |
| 11417 | }; |
| 11418 | return sema.failWithOwnedErrorMsg(block, msg); |
| 11419 | }, |
| 11420 | } |
| 11421 | |
| 11422 | // Validate for missing special prongs. |
| 11423 | switch (item_ty.zigTypeTag(zcu)) { |
| 11424 | .@"enum" => { |
| 11425 | const all_tags_handled = for (seen.enum_fields) |seen_src| { |
| 11426 | if (seen_src == null) break false; |
| 11427 | } else true; |
| 11428 | |
| 11429 | if (has_else) { |
| 11430 | if (all_tags_handled) { |
| 11431 | if (operand_ty.isNonexhaustiveEnum(zcu)) { |
| 11432 | if (has_under) return sema.fail( |
| 11433 | block, |
| 11434 | else_prong_src, |
| 11435 | "unreachable else prong; all explicit cases already handled", |
| 11436 | .{}, |
| 11437 | ); |
| 11438 | } else return sema.fail( |
| 11439 | block, |
| 11440 | else_prong_src, |
| 11441 | "unreachable else prong; all cases already handled", |
| 11442 | .{}, |
| 11443 | ); |
| 11444 | } |
| 11445 | } else if (!all_tags_handled) { |
| 11446 | const msg = msg: { |
| 11447 | const msg = try sema.errMsg( |
| 11448 | src, |
| 11449 | "switch must handle all possibilities", |
| 11450 | .{}, |
| 11451 | ); |
| 11452 | errdefer msg.destroy(sema.gpa); |
| 11453 | for (seen.enum_fields, 0..) |seen_src, i| { |
| 11454 | if (seen_src != null) continue; |
| 11455 | |
| 11456 | const field_name = item_ty.enumFieldName(i, zcu); |
| 11457 | try sema.addFieldErrNote( |
| 11458 | item_ty, |
| 11459 | i, |
| 11460 | msg, |
| 11461 | "unhandled enumeration value: '{f}'", |
| 11462 | .{field_name.fmt(ip)}, |
| 11463 | ); |
| 11464 | } |
| 11465 | try sema.errNote( |
| 11466 | item_ty.srcLoc(zcu), |
| 11467 | msg, |
| 11468 | "enum '{f}' declared here", |
| 11469 | .{item_ty.fmt(pt)}, |
| 11470 | ); |
| 11471 | break :msg msg; |
| 11472 | }; |
| 11473 | return sema.failWithOwnedErrorMsg(block, msg); |
| 11474 | } else if (!has_else and !has_under and |
| 11475 | item_ty.isNonexhaustiveEnum(zcu) and operand_ty.zigTypeTag(zcu) != .@"union") |
| 11476 | { |
| 11477 | return sema.fail( |
| 11478 | block, |
| 11479 | src, |
| 11480 | "switch on non-exhaustive enum must include 'else' or '_' prong or both", |
| 11481 | .{}, |
| 11482 | ); |
| 11483 | } |
| 11484 | }, |
| 11485 | .error_set => { |
| 11486 | else_err_ty = ty: switch (try sema.resolveInferredErrorSetTy(block, src, item_ty.toIntern())) { |
| 11487 | .anyerror_type => { |
| 11488 | if (!has_else) { |
| 11489 | return sema.fail( |
| 11490 | block, |
| 11491 | src, |
| 11492 | "else prong required when switching on type 'anyerror'", |
| 11493 | .{}, |
| 11494 | ); |
| 11495 | } |
| 11496 | break :ty .anyerror; |
| 11497 | }, |
| 11498 | else => |err_set_ty_index| { |
| 11499 | const error_names = ip.indexToKey(err_set_ty_index).error_set_type.names; |
| 11500 | var maybe_msg: ?*Zcu.ErrorMsg = null; |
| 11501 | errdefer if (maybe_msg) |msg| msg.destroy(sema.gpa); |
| 11502 | |
| 11503 | var seen_errors_from_set: u32 = 0; |
| 11504 | for (error_names.get(ip)) |error_name| { |
| 11505 | if (seen.errors.contains(error_name)) { |
| 11506 | seen_errors_from_set += 1; |
| 11507 | } else if (!has_else) { |
| 11508 | const msg = maybe_msg orelse blk: { |
| 11509 | maybe_msg = try sema.errMsg( |
| 11510 | src, |
| 11511 | "switch must handle all possibilities", |
| 11512 | .{}, |
| 11513 | ); |
| 11514 | break :blk maybe_msg.?; |
| 11515 | }; |
| 11516 | |
| 11517 | try sema.errNote( |
| 11518 | src, |
| 11519 | msg, |
| 11520 | "unhandled error value: 'error.{f}'", |
| 11521 | .{error_name.fmt(ip)}, |
| 11522 | ); |
| 11523 | } |
| 11524 | } |
| 11525 | |
| 11526 | if (maybe_msg) |msg| { |
| 11527 | maybe_msg = null; |
| 11528 | try sema.addDeclaredHereNote(msg, operand_ty); |
| 11529 | return sema.failWithOwnedErrorMsg(block, msg); |
| 11530 | } |
| 11531 | |
| 11532 | if (has_else and seen_errors_from_set == error_names.len) { |
| 11533 | // This prong is unreachable anyway so we don't need its |
| 11534 | // error set type, but we still allow it to exist. |
| 11535 | if (else_case.is_simple_noreturn) break :ty null; |
| 11536 | return sema.fail( |
| 11537 | block, |
| 11538 | else_prong_src, |
| 11539 | "unreachable else prong; all cases already handled", |
| 11540 | .{}, |
| 11541 | ); |
| 11542 | } |
| 11543 | |
| 11544 | var names: InferredErrorSet.NameMap = .{}; |
| 11545 | try names.ensureUnusedCapacity(sema.arena, error_names.len); |
| 11546 | for (error_names.get(ip)) |error_name| { |
| 11547 | if (seen.errors.contains(error_name)) continue; |
| 11548 | names.putAssumeCapacityNoClobber(error_name, {}); |
| 11549 | } |
| 11550 | // No need to keep the hash map metadata correct; here we |
| 11551 | // extract the (sorted) keys only. |
| 11552 | break :ty try pt.errorSetFromUnsortedNames(names.keys()); |
| 11553 | }, |
| 11554 | }; |
| 11555 | }, |
| 11556 | .int, .@"union", .@"struct" => |type_tag| { |
| 11557 | check_range: { |
| 11558 | const int_ty = switch (type_tag) { |
| 11559 | .int => item_ty, |
| 11560 | .@"union", .@"struct" => item_ty.backingIntType(zcu), |
| 11561 | else => unreachable, |
| 11562 | }; |
| 11563 | const min_int = try int_ty.minInt(pt, int_ty); |
| 11564 | const max_int = try int_ty.maxInt(pt, int_ty); |
| 11565 | if (try seen.ranges.spans(arena, min_int, max_int, int_ty, zcu)) { |
| 11566 | if (has_else) { |
| 11567 | return sema.fail( |
| 11568 | block, |
| 11569 | else_prong_src, |
| 11570 | "unreachable else prong; all cases already handled", |
| 11571 | .{}, |
| 11572 | ); |
| 11573 | } |
| 11574 | break :check_range; |
| 11575 | } |
| 11576 | if (!has_else) { |
| 11577 | return sema.fail( |
| 11578 | block, |
| 11579 | src, |
| 11580 | "switch must handle all possibilities", |
| 11581 | .{}, |
| 11582 | ); |
| 11583 | } |
| 11584 | } |
| 11585 | }, |
| 11586 | .comptime_int, .enum_literal, .@"fn", .pointer, .type => { |
| 11587 | if (!has_else) { |
| 11588 | return sema.fail( |
| 11589 | block, |
| 11590 | src, |
| 11591 | "else prong required when switching on type '{f}'", |
| 11592 | .{item_ty.fmt(pt)}, |
| 11593 | ); |
| 11594 | } |
| 11595 | }, |
| 11596 | .bool, .void => |type_tag| { |
| 11597 | const all_values_handled = switch (type_tag) { |
| 11598 | .bool => seen.true_src != null and seen.false_src != null, |
| 11599 | .void => seen.void_src != null, |
| 11600 | else => unreachable, |
| 11601 | }; |
| 11602 | if (has_else) { |
| 11603 | if (all_values_handled) { |
| 11604 | return sema.fail( |
| 11605 | block, |
| 11606 | else_prong_src, |
| 11607 | "unreachable else prong; all cases already handled", |
| 11608 | .{}, |
| 11609 | ); |
| 11610 | } |
| 11611 | } else { |
| 11612 | if (!all_values_handled) { |
| 11613 | return sema.fail( |
| 11614 | block, |
| 11615 | src, |
| 11616 | "switch must handle all possibilities", |
| 11617 | .{}, |
| 11618 | ); |
| 11619 | } |
| 11620 | } |
| 11621 | }, |
| 11622 | else => unreachable, |
| 11623 | } |
| 11624 | |
| 11625 | return .{ |
| 11626 | .seen = seen, |
| 11627 | .case_vals = case_vals.items, |
| 11628 | .else_case = else_case, |
| 11629 | .else_err_ty = else_err_ty, |
| 11630 | }; |
| 11631 | } |
| 11632 | |
| 11633 | fn resolveSwitchBlock( |
| 11634 | sema: *Sema, |
| 11635 | block: *Block, |
| 11636 | child_block: *Block, |
| 11637 | operand: SwitchOperand, |
| 11638 | raw_operand_ty: Type, |
| 11639 | cond_val: Value, |
| 11640 | merges: *Block.Merges, |
| 11641 | switch_inst: Zir.Inst.Index, |
| 11642 | zir_switch: *const Zir.UnwrappedSwitchBlock, |
| 11643 | validated_switch: *const ValidatedSwitchBlock, |
| 11644 | ) CompileError!Air.Inst.Ref { |
| 11645 | const pt = sema.pt; |
| 11646 | const zcu = pt.zcu; |
| 11647 | |
| 11648 | const switch_node_offset = zir_switch.switch_src_node_offset; |
| 11649 | |
| 11650 | const operand_ty = sema.typeOf(operand.simple.by_val); |
| 11651 | |
| 11652 | const tagged_union_originally = operand_ty.zigTypeTag(zcu) == .@"union" and |
| 11653 | operand_ty.containerLayout(zcu) != .@"packed"; |
| 11654 | const err_set = operand_ty.zigTypeTag(zcu) == .error_set; |
| 11655 | |
| 11656 | const item_ty = if (tagged_union_originally) |
| 11657 | operand_ty.unionTagType(zcu).? |
| 11658 | else |
| 11659 | operand_ty; |
| 11660 | |
| 11661 | const cond_ref = operand.simple.cond; |
| 11662 | |
| 11663 | const case_vals = validated_switch.case_vals; |
| 11664 | var case_val_idx: usize = 0; |
| 11665 | var extra_index = zir_switch.end; |
| 11666 | var case_it = zir_switch.iterateCases(); |
| 11667 | var under_prong: ?struct { |
| 11668 | index: Zir.UnwrappedSwitchBlock.Case.Index, |
| 11669 | body: []const Zir.Inst.Index, |
| 11670 | capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, |
| 11671 | has_tag_capture: bool, |
| 11672 | } = null; |
| 11673 | while (case_it.next()) |case| { |
| 11674 | const prong_info = case.prong_info; |
| 11675 | const prong_body = sema.code.bodySlice(extra_index, prong_info.body_len); |
| 11676 | extra_index += prong_body.len; |
| 11677 | for (case.item_infos) |item_info| { |
| 11678 | if (item_info.bodyLen()) |body_len| extra_index += body_len; |
| 11679 | } |
| 11680 | for (case.range_infos) |range_info| { |
| 11681 | if (range_info[0].bodyLen()) |body_len| extra_index += body_len; |
| 11682 | if (range_info[1].bodyLen()) |body_len| extra_index += body_len; |
| 11683 | } |
| 11684 | |
| 11685 | const item_refs = case_vals[case_val_idx..][0..case.item_infos.len]; |
| 11686 | case_val_idx += item_refs.len; |
| 11687 | const range_refs: []const [2]Air.Inst.Ref = @ptrCast(case_vals[case_val_idx..][0 .. 2 * case.range_infos.len]); |
| 11688 | case_val_idx += 2 * range_refs.len; |
| 11689 | for (item_refs) |item_ref| { |
| 11690 | if (item_ref == .none) { |
| 11691 | under_prong = .{ |
| 11692 | .index = case.index, |
| 11693 | .body = prong_body, |
| 11694 | .capture = case.prong_info.capture, |
| 11695 | .has_tag_capture = case.prong_info.has_tag_capture, |
| 11696 | }; |
| 11697 | continue; |
| 11698 | } |
| 11699 | const item_val = sema.resolveValue(item_ref).?; |
| 11700 | if (cond_val.eql(item_val, item_ty, zcu)) { |
| 11701 | if (err_set) try sema.maybeErrorUnwrapComptime(child_block, prong_body, cond_ref); |
| 11702 | if (tagged_union_originally and operand_ty.unionFieldType(item_val, zcu).?.isNoReturn(zcu)) { |
| 11703 | // This prong should be unreachable! |
| 11704 | return .unreachable_value; |
| 11705 | } |
| 11706 | const prong_items: SwitchProngItems = prong_items: { |
| 11707 | if (prong_info.is_inline) break :prong_items .{ .@"inline" = cond_ref }; |
| 11708 | if (range_refs.len > 0) break :prong_items .has_ranges; |
| 11709 | break :prong_items .{ .item_refs = item_refs }; |
| 11710 | }; |
| 11711 | return sema.resolveSwitchProng( |
| 11712 | block, |
| 11713 | child_block, |
| 11714 | operand, |
| 11715 | raw_operand_ty, |
| 11716 | prong_body, |
| 11717 | block.src(.{ .switch_capture = .{ |
| 11718 | .switch_node_offset = switch_node_offset, |
| 11719 | .case_idx = case.index, |
| 11720 | } }), |
| 11721 | prong_info.capture, |
| 11722 | prong_info.has_tag_capture, |
| 11723 | prong_items, |
| 11724 | validated_switch.else_err_ty, |
| 11725 | merges, |
| 11726 | switch_inst, |
| 11727 | zir_switch, |
| 11728 | ); |
| 11729 | } |
| 11730 | } |
| 11731 | for (range_refs) |range_ref| { |
| 11732 | const first_val = sema.resolveValue(range_ref[0]).?; |
| 11733 | const last_val = sema.resolveValue(range_ref[1]).?; |
| 11734 | if ((try sema.compareAll(cond_val, .gte, first_val, item_ty)) and |
| 11735 | (try sema.compareAll(cond_val, .lte, last_val, item_ty))) |
| 11736 | { |
| 11737 | const prong_items: SwitchProngItems = if (prong_info.is_inline) |
| 11738 | .{ .@"inline" = cond_ref } |
| 11739 | else |
| 11740 | .has_ranges; |
| 11741 | return sema.resolveSwitchProng( |
| 11742 | block, |
| 11743 | child_block, |
| 11744 | operand, |
| 11745 | raw_operand_ty, |
| 11746 | prong_body, |
| 11747 | block.src(.{ .switch_capture = .{ |
| 11748 | .switch_node_offset = switch_node_offset, |
| 11749 | .case_idx = case.index, |
| 11750 | } }), |
| 11751 | prong_info.capture, |
| 11752 | prong_info.has_tag_capture, |
| 11753 | prong_items, |
| 11754 | validated_switch.else_err_ty, |
| 11755 | merges, |
| 11756 | switch_inst, |
| 11757 | zir_switch, |
| 11758 | ); |
| 11759 | } |
| 11760 | } |
| 11761 | } |
| 11762 | |
| 11763 | assert(zir_switch.else_case != null or under_prong != null); // switch exhaustion check wrong |
| 11764 | |
| 11765 | const else_case = validated_switch.else_case; |
| 11766 | const else_is_named_only = zir_switch.else_case != null and under_prong != null; |
| 11767 | |
| 11768 | // named-only prong |
| 11769 | |
| 11770 | if (else_is_named_only and item_ty.enumTagFieldIndex(cond_val, zcu) != null) { |
| 11771 | assert(item_ty.isNonexhaustiveEnum(zcu)); |
| 11772 | const prong_items: SwitchProngItems = if (else_case.is_inline) |
| 11773 | .{ .@"inline" = cond_ref } |
| 11774 | else |
| 11775 | .special; |
| 11776 | return sema.resolveSwitchProng( |
| 11777 | block, |
| 11778 | child_block, |
| 11779 | operand, |
| 11780 | raw_operand_ty, |
| 11781 | else_case.body, |
| 11782 | block.src(.{ .switch_capture = .{ |
| 11783 | .switch_node_offset = switch_node_offset, |
| 11784 | .case_idx = else_case.index, |
| 11785 | } }), |
| 11786 | else_case.capture, |
| 11787 | else_case.has_tag_capture, |
| 11788 | prong_items, |
| 11789 | validated_switch.else_err_ty, |
| 11790 | merges, |
| 11791 | switch_inst, |
| 11792 | zir_switch, |
| 11793 | ); |
| 11794 | } |
| 11795 | |
| 11796 | // catch-all prong |
| 11797 | |
| 11798 | const index, const body, const capture, const has_tag_capture, const is_inline = if (under_prong) |under| |
| 11799 | .{ under.index, under.body, under.capture, under.has_tag_capture, false } |
| 11800 | else |
| 11801 | .{ else_case.index, else_case.body, else_case.capture, else_case.has_tag_capture, else_case.is_inline }; |
| 11802 | if (err_set) try sema.maybeErrorUnwrapComptime(child_block, body, cond_ref); |
| 11803 | if (tagged_union_originally) { |
| 11804 | for (validated_switch.seen.enum_fields, 0..) |maybe_seen, field_i| { |
| 11805 | if (maybe_seen != null) continue; |
| 11806 | if (!operand_ty.unionFieldTypeByIndex(field_i, zcu).isNoReturn(zcu)) break; |
| 11807 | } else { |
| 11808 | // This prong should be unreachable! |
| 11809 | return .unreachable_value; |
| 11810 | } |
| 11811 | } |
| 11812 | const prong_items: SwitchProngItems = if (is_inline) |
| 11813 | .{ .@"inline" = cond_ref } |
| 11814 | else |
| 11815 | .special; |
| 11816 | return sema.resolveSwitchProng( |
| 11817 | block, |
| 11818 | child_block, |
| 11819 | operand, |
| 11820 | raw_operand_ty, |
| 11821 | body, |
| 11822 | block.src(.{ .switch_capture = .{ |
| 11823 | .switch_node_offset = switch_node_offset, |
| 11824 | .case_idx = index, |
| 11825 | } }), |
| 11826 | capture, |
| 11827 | has_tag_capture, |
| 11828 | prong_items, |
| 11829 | validated_switch.else_err_ty, |
| 11830 | merges, |
| 11831 | switch_inst, |
| 11832 | zir_switch, |
| 11833 | ); |
| 11834 | } |
| 11835 | |
| 11836 | const SwitchOperand = union(enum) { |
| 11837 | /// This switch will be dispatched only once, with the given operand. |
| 11838 | simple: struct { |
| 11839 | /// The raw switch operand value. Always defined. |
| 11840 | by_val: Air.Inst.Ref, |
| 11841 | /// The switch operand *pointer*. `none` if there are no prongs with a |
| 11842 | /// by-ref capture. |
| 11843 | by_ref: Air.Inst.Ref, |
| 11844 | /// The switch condition value. For unions, `operand` is the union |
| 11845 | /// and `cond` is its enum tag value. |
| 11846 | cond: Air.Inst.Ref, |
| 11847 | }, |
| 11848 | /// This switch may be dispatched multiple times with `continue` syntax. |
| 11849 | /// As such, the operand is stored in an alloc if needed. |
| 11850 | loop: struct { |
| 11851 | /// The `alloc` containing the `switch` operand for the active dispatch. |
| 11852 | /// Each prong must load from this `alloc` to get captures. |
| 11853 | /// If there are no captures, this may be `none`. |
| 11854 | operand_alloc: Air.Inst.Ref, |
| 11855 | /// Whether `operand_alloc` contains a by-val operand or a by-ref |
| 11856 | /// operand. |
| 11857 | operand_is_ref: bool, |
| 11858 | /// The switch condition value for the *initial* dispatch. For |
| 11859 | /// unions, this is the enum tag value. |
| 11860 | init_cond: Air.Inst.Ref, |
| 11861 | }, |
| 11862 | }; |
| 11863 | |
| 11864 | fn analyzeSwitchOperandLoad( |
| 11865 | sema: *Sema, |
| 11866 | block: *Block, |
| 11867 | operand: SwitchOperand, |
| 11868 | operand_src: LazySrcLoc, |
| 11869 | by_ref: bool, |
| 11870 | ) CompileError!Air.Inst.Ref { |
| 11871 | switch (operand) { |
| 11872 | .simple => |s| { |
| 11873 | if (by_ref) { |
| 11874 | assert(s.by_ref != .none); |
| 11875 | return s.by_ref; |
| 11876 | } else { |
| 11877 | return s.by_val; |
| 11878 | } |
| 11879 | }, |
| 11880 | .loop => |l| { |
| 11881 | const loaded = try sema.analyzeLoad(block, operand_src, l.operand_alloc, operand_src); |
| 11882 | assert(loaded != .none); // there are no captures, so no need to load the switch operand |
| 11883 | if (by_ref) { |
| 11884 | assert(l.operand_is_ref); |
| 11885 | return loaded; |
| 11886 | } |
| 11887 | return if (l.operand_is_ref) |
| 11888 | try sema.analyzeLoad(block, operand_src, loaded, operand_src) |
| 11889 | else |
| 11890 | loaded; |
| 11891 | }, |
| 11892 | } |
| 11893 | } |
| 11894 | |
| 11895 | const SwitchProngItems = union(enum) { |
| 11896 | @"inline": Air.Inst.Ref, |
| 11897 | item_refs: []const Air.Inst.Ref, |
| 11898 | has_ranges, |
| 11899 | special, |
| 11900 | }; |
| 11901 | |
| 11902 | /// A switch capture is comptime-known if it is `inline` and/or it is a by-value |
| 11903 | /// capture of a prong with a single item. |
| 11904 | fn resolveSwitchCaptureFromProngItems( |
| 11905 | sema: *Sema, |
| 11906 | prong_items: SwitchProngItems, |
| 11907 | by_ref: bool, |
| 11908 | ) ?Value { |
| 11909 | const ref: Air.Inst.Ref = switch (prong_items) { |
| 11910 | .@"inline" => |ref| ref, |
| 11911 | .item_refs => |refs| if (refs.len == 1 and !by_ref) refs[0] else return null, |
| 11912 | .has_ranges, .special => return null, |
| 11913 | }; |
| 11914 | return sema.resolveValue(ref).?; |
| 11915 | } |
| 11916 | |
| 11917 | /// Resolve a switch prong which is determined at comptime to have no peers. |
| 11918 | /// Sets up captures as needed. Uses `analyzeBodyRuntimeBreak`. |
| 11919 | fn resolveSwitchProng( |
| 11920 | sema: *Sema, |
| 11921 | block: *Block, |
| 11922 | child_block: *Block, |
| 11923 | operand: SwitchOperand, |
| 11924 | raw_operand_ty: Type, |
| 11925 | prong_body: []const Zir.Inst.Index, |
| 11926 | /// Must use the `switch_capture` field in `offset`. |
| 11927 | capture_src: LazySrcLoc, |
| 11928 | capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, |
| 11929 | has_tag_capture: bool, |
| 11930 | prong_items: SwitchProngItems, |
| 11931 | else_err_ty: ?Type, |
| 11932 | merges: *Block.Merges, |
| 11933 | switch_inst: Zir.Inst.Index, |
| 11934 | zir_switch: *const Zir.UnwrappedSwitchBlock, |
| 11935 | ) CompileError!Air.Inst.Ref { |
| 11936 | const src_node_offset = zir_switch.switch_src_node_offset; |
| 11937 | const src = block.nodeOffset(src_node_offset); |
| 11938 | const operand_src = block.src(.{ .node_offset_switch_operand = src_node_offset }); |
| 11939 | |
| 11940 | // We can propagate `.cold` hints from this branch since it's comptime-known |
| 11941 | // to be taken from the parent branch. |
| 11942 | const parent_hint = sema.branch_hint; |
| 11943 | defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null; |
| 11944 | |
| 11945 | const analyzed_captures = try sema.analyzeSwitchCaptures( |
| 11946 | child_block, |
| 11947 | operand, |
| 11948 | operand_src, |
| 11949 | sema.typeOf(operand.simple.by_val), |
| 11950 | capture_src, |
| 11951 | capture, |
| 11952 | has_tag_capture, |
| 11953 | prong_items, |
| 11954 | else_err_ty, |
| 11955 | ); |
| 11956 | |
| 11957 | const payload_inst = if (capture != .none) inst: { |
| 11958 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; |
| 11959 | sema.inst_map.putAssumeCapacity(payload_inst, analyzed_captures.payload_ref); |
| 11960 | break :inst payload_inst; |
| 11961 | } else undefined; |
| 11962 | defer if (capture != .none) assert(sema.inst_map.remove(payload_inst)); |
| 11963 | |
| 11964 | const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: { |
| 11965 | const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst; |
| 11966 | sema.inst_map.putAssumeCapacity(tag_inst, analyzed_captures.tag_ref); |
| 11967 | break :inst tag_inst; |
| 11968 | } else undefined; |
| 11969 | defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst)); |
| 11970 | |
| 11971 | if (zir_switch.has_continue) sema.inst_map.putAssumeCapacity(switch_inst, .fromType(raw_operand_ty)); |
| 11972 | defer if (zir_switch.has_continue) assert(sema.inst_map.remove(switch_inst)); |
| 11973 | |
| 11974 | return sema.resolveBlockBody(block, src, child_block, prong_body, switch_inst, merges); |
| 11975 | } |
| 11976 | |
| 11977 | fn wantSwitchProngBodyAnalysis( |
| 11978 | sema: *Sema, |
| 11979 | item_ref: Air.Inst.Ref, |
| 11980 | operand_ty: Type, |
| 11981 | tagged_union_originally: bool, |
| 11982 | err_set: bool, |
| 11983 | prong_is_comptime_unreach: bool, |
| 11984 | ) bool { |
| 11985 | const zcu = sema.pt.zcu; |
| 11986 | if (tagged_union_originally) { |
| 11987 | const item_val = sema.resolveValue(item_ref).?; |
| 11988 | const field_ty = operand_ty.unionFieldType(item_val, zcu).?; |
| 11989 | if (field_ty.isNoReturn(zcu)) return false; |
| 11990 | } |
| 11991 | if (err_set and prong_is_comptime_unreach) { |
| 11992 | const item_val = sema.resolveValue(item_ref).?; |
| 11993 | const err_name = item_val.getErrorName(zcu).unwrap().?; |
| 11994 | if (!operand_ty.errorSetHasField(err_name, zcu)) return false; |
| 11995 | } |
| 11996 | return true; |
| 11997 | } |
| 11998 | |
| 11999 | /// Assumes that `operand_ty` has more than one possible value. |
| 12000 | /// Sets up captures as needed. Uses `analyzeBodyRuntimeBreak`. |
| 12001 | fn analyzeSwitchProng( |
| 12002 | sema: *Sema, |
| 12003 | case_block: *Block, |
| 12004 | operand: SwitchOperand, |
| 12005 | operand_ty: Type, |
| 12006 | raw_operand_ty: Type, |
| 12007 | prong_body: []const Zir.Inst.Index, |
| 12008 | /// Must use the `switch_capture` field in `offset`. |
| 12009 | capture_src: LazySrcLoc, |
| 12010 | capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, |
| 12011 | has_tag_capture: bool, |
| 12012 | prong_items: SwitchProngItems, |
| 12013 | else_err_ty: ?Type, |
| 12014 | switch_inst: Zir.Inst.Index, |
| 12015 | zir_switch: *const Zir.UnwrappedSwitchBlock, |
| 12016 | ) CompileError!std.lang.BranchHint { |
| 12017 | const pt = sema.pt; |
| 12018 | const zcu = pt.zcu; |
| 12019 | |
| 12020 | const operand_src = case_block.src(.{ .node_offset_switch_operand = zir_switch.switch_src_node_offset }); |
| 12021 | |
| 12022 | if (operand_ty.zigTypeTag(zcu) == .error_set) { |
| 12023 | const cond_ref = switch (operand) { |
| 12024 | .simple => |s| s.cond, |
| 12025 | .loop => |l| l.init_cond, |
| 12026 | }; |
| 12027 | if (try sema.maybeErrorUnwrap(case_block, prong_body, cond_ref, operand_src, true)) { |
| 12028 | // nothing to do here. weight against error branch |
| 12029 | return .unlikely; |
| 12030 | } |
| 12031 | } |
| 12032 | |
| 12033 | const analyzed_captures = try sema.analyzeSwitchCaptures( |
| 12034 | case_block, |
| 12035 | operand, |
| 12036 | operand_src, |
| 12037 | operand_ty, |
| 12038 | capture_src, |
| 12039 | capture, |
| 12040 | has_tag_capture, |
| 12041 | prong_items, |
| 12042 | else_err_ty, |
| 12043 | ); |
| 12044 | |
| 12045 | const payload_inst = if (capture != .none) inst: { |
| 12046 | const payload_inst = zir_switch.payload_capture_placeholder.unwrap() orelse switch_inst; |
| 12047 | sema.inst_map.putAssumeCapacity(payload_inst, analyzed_captures.payload_ref); |
| 12048 | break :inst payload_inst; |
| 12049 | } else undefined; |
| 12050 | defer if (capture != .none) assert(sema.inst_map.remove(payload_inst)); |
| 12051 | |
| 12052 | const tag_inst: Zir.Inst.Index = if (has_tag_capture) inst: { |
| 12053 | const tag_inst = zir_switch.tag_capture_placeholder.unwrap() orelse switch_inst; |
| 12054 | sema.inst_map.putAssumeCapacity(tag_inst, analyzed_captures.tag_ref); |
| 12055 | break :inst tag_inst; |
| 12056 | } else undefined; |
| 12057 | defer if (has_tag_capture) assert(sema.inst_map.remove(tag_inst)); |
| 12058 | |
| 12059 | if (zir_switch.has_continue) sema.inst_map.putAssumeCapacity(switch_inst, .fromType(raw_operand_ty)); |
| 12060 | defer if (zir_switch.has_continue) assert(sema.inst_map.remove(switch_inst)); |
| 12061 | |
| 12062 | return sema.analyzeBodyRuntimeBreak(case_block, prong_body); |
| 12063 | } |
| 12064 | |
| 12065 | fn analyzeSwitchCaptures( |
| 12066 | sema: *Sema, |
| 12067 | case_block: *Block, |
| 12068 | operand: SwitchOperand, |
| 12069 | operand_src: LazySrcLoc, |
| 12070 | operand_ty: Type, |
| 12071 | capture_src: LazySrcLoc, |
| 12072 | capture: Zir.Inst.SwitchBlock.ProngInfo.Capture, |
| 12073 | has_tag_capture: bool, |
| 12074 | prong_items: SwitchProngItems, |
| 12075 | else_err_ty: ?Type, |
| 12076 | ) CompileError!struct { |
| 12077 | payload_ref: Air.Inst.Ref, |
| 12078 | tag_ref: Air.Inst.Ref, |
| 12079 | } { |
| 12080 | const pt = sema.pt; |
| 12081 | const zcu = pt.zcu; |
| 12082 | |
| 12083 | if (operand_ty.zigTypeTag(zcu) == .@"union" and |
| 12084 | operand_ty.containerLayout(zcu) != .@"packed") |
| 12085 | { |
| 12086 | if (capture == .none) { |
| 12087 | const tag_ref: Air.Inst.Ref = tag_ref: { |
| 12088 | if (!has_tag_capture) break :tag_ref .none; |
| 12089 | if (sema.resolveSwitchCaptureFromProngItems(prong_items, false)) |tag_val| { |
| 12090 | break :tag_ref .fromValue(tag_val); |
| 12091 | } |
| 12092 | const loaded_operand = try sema.analyzeSwitchOperandLoad(case_block, operand, operand_src, false); |
| 12093 | break :tag_ref try sema.unionToTag(case_block, loaded_operand); |
| 12094 | }; |
| 12095 | return .{ .payload_ref = .none, .tag_ref = tag_ref }; |
| 12096 | } |
| 12097 | |
| 12098 | // We always have to load the operand for tagged union payload captures |
| 12099 | // since we can't derive the payload value from the tag (except for OPV |
| 12100 | // types, for which the load is always basically a noop anyway). |
| 12101 | |
| 12102 | const loaded_operand = try sema.analyzeSwitchOperandLoad(case_block, operand, operand_src, capture == .by_ref); |
| 12103 | |
| 12104 | if (sema.resolveSwitchCaptureFromProngItems(prong_items, capture == .by_ref)) |tag_val| { |
| 12105 | const payload_ref = try sema.resolveSwitchPayloadCaptureTaggedUnion( |
| 12106 | case_block, |
| 12107 | loaded_operand, |
| 12108 | operand_src, |
| 12109 | operand_ty, |
| 12110 | tag_val, |
| 12111 | capture == .by_ref, |
| 12112 | ); |
| 12113 | const tag_ref: Air.Inst.Ref = if (has_tag_capture) .fromValue(tag_val) else .none; |
| 12114 | return .{ .payload_ref = payload_ref, .tag_ref = tag_ref }; |
| 12115 | } |
| 12116 | |
| 12117 | const payload_ref = try sema.analyzeSwitchPayloadCaptureTaggedUnion( |
| 12118 | case_block, |
| 12119 | operand, |
| 12120 | loaded_operand, |
| 12121 | operand_src, |
| 12122 | operand_ty, |
| 12123 | capture == .by_ref, |
| 12124 | capture_src, |
| 12125 | prong_items, |
| 12126 | ); |
| 12127 | |
| 12128 | const tag_ref: Air.Inst.Ref = tag_ref: { |
| 12129 | if (!has_tag_capture) break :tag_ref .none; |
| 12130 | const operand_val = switch (capture) { |
| 12131 | .none => unreachable, // handled above |
| 12132 | .by_val => loaded_operand, |
| 12133 | .by_ref => try sema.analyzeLoad(case_block, operand_src, loaded_operand, operand_src), |
| 12134 | }; |
| 12135 | break :tag_ref try sema.unionToTag(case_block, operand_val); |
| 12136 | }; |
| 12137 | |
| 12138 | assert(!sema.typeOf(payload_ref).isNoReturn(zcu)); |
| 12139 | return .{ .payload_ref = payload_ref, .tag_ref = tag_ref }; |
| 12140 | } |
| 12141 | |
| 12142 | const payload_ref: Air.Inst.Ref = payload_ref: { |
| 12143 | if (capture == .none) break :payload_ref .none; |
| 12144 | |
| 12145 | if (operand_ty.zigTypeTag(zcu) == .error_set) { |
| 12146 | // Error captures need to have their type narrowed! |
| 12147 | |
| 12148 | if (capture == .by_ref) { |
| 12149 | return sema.fail( |
| 12150 | case_block, |
| 12151 | capture_src, |
| 12152 | "error set cannot be captured by reference", |
| 12153 | .{}, |
| 12154 | ); |
| 12155 | } |
| 12156 | assert(capture == .by_val); |
| 12157 | |
| 12158 | if (sema.resolveSwitchCaptureFromProngItems(prong_items, false)) |err_val| { |
| 12159 | const err_name = err_val.getErrorName(zcu).unwrap().?; |
| 12160 | break :payload_ref .fromIntern((try pt.intern(.{ .err = .{ |
| 12161 | .ty = (try pt.singleErrorSetType(err_name)).toIntern(), |
| 12162 | .name = err_name, |
| 12163 | } }))); |
| 12164 | } |
| 12165 | |
| 12166 | const loaded_operand = try sema.analyzeSwitchOperandLoad(case_block, operand, operand_src, false); |
| 12167 | |
| 12168 | switch (prong_items) { |
| 12169 | .@"inline" => unreachable, // handled above |
| 12170 | .has_ranges => unreachable, // not possible for error set |
| 12171 | .special => { |
| 12172 | const capture_err_ty = else_err_ty orelse { |
| 12173 | try sema.analyzeUnreachable(case_block, operand_src, false); |
| 12174 | break :payload_ref .unreachable_value; |
| 12175 | }; |
| 12176 | break :payload_ref try sema.errorCastUnchecked(case_block, capture_err_ty, loaded_operand); |
| 12177 | }, |
| 12178 | .item_refs => |item_refs| { |
| 12179 | var names: InferredErrorSet.NameMap = .{}; |
| 12180 | try names.ensureUnusedCapacity(sema.arena, item_refs.len); |
| 12181 | for (item_refs) |item_ref| { |
| 12182 | const item_val = sema.resolveValue(item_ref).?; |
| 12183 | names.putAssumeCapacityNoClobber(item_val.getErrorName(zcu).unwrap().?, {}); |
| 12184 | } |
| 12185 | const capture_err_ty = try pt.errorSetFromUnsortedNames(names.keys()); |
| 12186 | break :payload_ref try sema.errorCastUnchecked(case_block, capture_err_ty, loaded_operand); |
| 12187 | }, |
| 12188 | } |
| 12189 | } |
| 12190 | |
| 12191 | // We try to make the capture comptime-known based on `prong_items` first: |
| 12192 | |
| 12193 | if (sema.resolveSwitchCaptureFromProngItems(prong_items, capture == .by_ref)) |item_val| { |
| 12194 | break :payload_ref switch (capture) { |
| 12195 | .none => unreachable, // handled above |
| 12196 | .by_val => .fromValue(item_val), |
| 12197 | .by_ref => try sema.uavRef(item_val), |
| 12198 | }; |
| 12199 | } |
| 12200 | |
| 12201 | // Otherwise the capture value is just the passed-through value of the |
| 12202 | // switch condition (which we might have to load first). |
| 12203 | |
| 12204 | break :payload_ref try sema.analyzeSwitchOperandLoad(case_block, operand, operand_src, capture == .by_ref); |
| 12205 | }; |
| 12206 | |
| 12207 | if (has_tag_capture) { |
| 12208 | const tag_capture_src: LazySrcLoc = .{ |
| 12209 | .base_node_inst = capture_src.base_node_inst, |
| 12210 | .offset = .{ .switch_tag_capture = capture_src.offset.switch_capture }, |
| 12211 | }; |
| 12212 | return sema.failWithInvalidSwitchTagCapture(case_block, tag_capture_src, operand_ty); |
| 12213 | } |
| 12214 | |
| 12215 | return .{ .payload_ref = payload_ref, .tag_ref = .none }; |
| 12216 | } |
| 12217 | |
| 12218 | fn resolveSwitchPayloadCaptureTaggedUnion( |
| 12219 | sema: *Sema, |
| 12220 | case_block: *Block, |
| 12221 | loaded_operand: Air.Inst.Ref, |
| 12222 | operand_src: LazySrcLoc, |
| 12223 | operand_ty: Type, |
| 12224 | tag_val: Value, |
| 12225 | capture_by_ref: bool, |
| 12226 | ) CompileError!Air.Inst.Ref { |
| 12227 | const pt = sema.pt; |
| 12228 | const zcu = pt.zcu; |
| 12229 | const ip = &zcu.intern_pool; |
| 12230 | |
| 12231 | const field_index: u32 = @intCast(operand_ty.unionTagFieldIndex(tag_val, zcu).?); |
| 12232 | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 12233 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); |
| 12234 | const payload_ref: Air.Inst.Ref = payload_ref: { |
| 12235 | if (capture_by_ref) { |
| 12236 | const ptr_field_ty = try sema.typeOf(loaded_operand).fieldPtrType(field_index, pt); |
| 12237 | if (try sema.resolveDefinedValue(case_block, operand_src, loaded_operand)) |op_ptr_val| { |
| 12238 | if (op_ptr_val.isUndef(zcu)) break :payload_ref try pt.undefRef(ptr_field_ty); |
| 12239 | const field_ptr_val = try op_ptr_val.ptrField(field_index, pt); |
| 12240 | break :payload_ref .fromValue(try pt.getCoerced(field_ptr_val, ptr_field_ty)); |
| 12241 | } |
| 12242 | break :payload_ref try case_block.addStructFieldPtr(loaded_operand, field_index, ptr_field_ty); |
| 12243 | } |
| 12244 | if (try sema.resolveDefinedValue(case_block, operand_src, loaded_operand)) |union_val| { |
| 12245 | const tag_and_val = ip.indexToKey(union_val.toIntern()).un; |
| 12246 | break :payload_ref .fromIntern(tag_and_val.val); |
| 12247 | } |
| 12248 | if (try field_ty.onePossibleValue(pt)) |opv| break :payload_ref .fromValue(opv); |
| 12249 | break :payload_ref try case_block.addStructFieldVal(loaded_operand, field_index, field_ty); |
| 12250 | }; |
| 12251 | assert(!sema.typeOf(payload_ref).isNoReturn(zcu)); |
| 12252 | return payload_ref; |
| 12253 | } |
| 12254 | |
| 12255 | fn analyzeSwitchPayloadCaptureTaggedUnion( |
| 12256 | sema: *Sema, |
| 12257 | case_block: *Block, |
| 12258 | operand: SwitchOperand, |
| 12259 | loaded_operand: Air.Inst.Ref, |
| 12260 | operand_src: LazySrcLoc, |
| 12261 | operand_ty: Type, |
| 12262 | capture_by_ref: bool, |
| 12263 | capture_src: LazySrcLoc, |
| 12264 | prong_items: SwitchProngItems, |
| 12265 | ) CompileError!Air.Inst.Ref { |
| 12266 | const pt = sema.pt; |
| 12267 | const zcu = pt.zcu; |
| 12268 | const ip = &zcu.intern_pool; |
| 12269 | const gpa = sema.gpa; |
| 12270 | |
| 12271 | const item_refs: []const Air.Inst.Ref = switch (prong_items) { |
| 12272 | .@"inline" => unreachable, // handled above |
| 12273 | .has_ranges => unreachable, // not possible for tagged union |
| 12274 | .special => return loaded_operand, |
| 12275 | .item_refs => |item_refs| item_refs, |
| 12276 | }; |
| 12277 | |
| 12278 | const switch_node_offset = operand_src.offset.node_offset_switch_operand; |
| 12279 | |
| 12280 | const union_obj = zcu.typeToUnion(operand_ty).?; |
| 12281 | |
| 12282 | const first_item_val = sema.resolveValue(item_refs[0]).?; |
| 12283 | const first_field_index: u32 = zcu.unionTagFieldIndex(union_obj, first_item_val).?; |
| 12284 | const first_field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[first_field_index]); |
| 12285 | |
| 12286 | const field_indices = try sema.arena.alloc(u32, item_refs.len); |
| 12287 | for (item_refs, field_indices) |item_ref, *field_idx| { |
| 12288 | const item_val = sema.resolveValue(item_ref).?; |
| 12289 | field_idx.* = zcu.unionTagFieldIndex(union_obj, item_val).?; |
| 12290 | } |
| 12291 | |
| 12292 | // Fast path: if all the operands are the same type already, we don't need to hit |
| 12293 | // PTR! This will also allow us to emit simpler code. |
| 12294 | const same_types = for (field_indices[1..]) |field_idx| { |
| 12295 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12296 | if (!field_ty.eql(first_field_ty)) break false; |
| 12297 | } else true; |
| 12298 | |
| 12299 | const capture_ty: Type = capture_ty: { |
| 12300 | if (same_types) break :capture_ty first_field_ty; |
| 12301 | // We need values to run PTR on, so make a bunch of undef constants. |
| 12302 | const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, item_refs.len); |
| 12303 | for (dummy_captures, field_indices) |*dummy, field_idx| { |
| 12304 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_idx]); |
| 12305 | dummy.* = try pt.undefRef(field_ty); |
| 12306 | } |
| 12307 | |
| 12308 | const item_srcs = try sema.arena.alloc(?LazySrcLoc, item_refs.len); |
| 12309 | for (item_srcs, 0..) |*item_src, item_i| { |
| 12310 | item_src.* = .{ |
| 12311 | .base_node_inst = capture_src.base_node_inst, |
| 12312 | .offset = .{ .switch_case_item = .{ |
| 12313 | .switch_node_offset = switch_node_offset, |
| 12314 | .case_idx = capture_src.offset.switch_capture.case_idx, |
| 12315 | .item_idx = .{ .kind = .single, .value = @intCast(item_i) }, |
| 12316 | } }, |
| 12317 | }; |
| 12318 | } |
| 12319 | |
| 12320 | break :capture_ty sema.resolvePeerTypes( |
| 12321 | case_block, |
| 12322 | capture_src, |
| 12323 | dummy_captures, |
| 12324 | .{ .override = item_srcs }, |
| 12325 | ) catch |err| switch (err) { |
| 12326 | error.AlreadyReported => |e| { |
| 12327 | if (sema.err) |msg| { |
| 12328 | try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); |
| 12329 | } |
| 12330 | return e; |
| 12331 | }, |
| 12332 | else => |e| return e, |
| 12333 | }; |
| 12334 | }; |
| 12335 | |
| 12336 | // By-reference captures have some further restrictions which make them easier to emit |
| 12337 | if (capture_by_ref) { |
| 12338 | const operand_ptr_ty = sema.typeOf(loaded_operand); |
| 12339 | const capture_ptr_ty = resolve: { |
| 12340 | // By-ref captures of hetereogeneous types are only allowed if all field |
| 12341 | // pointer types are peer resolvable to each other. |
| 12342 | // We need values to run PTR on, so make a bunch of undef constants. |
| 12343 | const dummy_captures = try sema.arena.alloc(Air.Inst.Ref, item_refs.len); |
| 12344 | for (field_indices, dummy_captures) |field_index, *dummy| { |
| 12345 | const field_ptr_ty = try operand_ptr_ty.fieldPtrType(field_index, pt); |
| 12346 | dummy.* = try pt.undefRef(field_ptr_ty); |
| 12347 | } |
| 12348 | const item_srcs = try sema.arena.alloc(?LazySrcLoc, item_refs.len); |
| 12349 | for (item_srcs, 0..) |*item_src, item_i| { |
| 12350 | item_src.* = .{ |
| 12351 | .base_node_inst = capture_src.base_node_inst, |
| 12352 | .offset = .{ .switch_case_item = .{ |
| 12353 | .switch_node_offset = switch_node_offset, |
| 12354 | .case_idx = capture_src.offset.switch_capture.case_idx, |
| 12355 | .item_idx = .{ .kind = .single, .value = @intCast(item_i) }, |
| 12356 | } }, |
| 12357 | }; |
| 12358 | } |
| 12359 | |
| 12360 | break :resolve sema.resolvePeerTypes( |
| 12361 | case_block, |
| 12362 | capture_src, |
| 12363 | dummy_captures, |
| 12364 | .{ .override = item_srcs }, |
| 12365 | ) catch |err| switch (err) { |
| 12366 | error.AlreadyReported => |e| { |
| 12367 | if (sema.err) |msg| { |
| 12368 | try sema.errNote(capture_src, msg, "this coercion is only possible when capturing by value", .{}); |
| 12369 | try sema.reparentOwnedErrorMsg(capture_src, msg, "capture group with incompatible types", .{}); |
| 12370 | } |
| 12371 | return e; |
| 12372 | }, |
| 12373 | else => |e| return e, |
| 12374 | }; |
| 12375 | }; |
| 12376 | |
| 12377 | if (try sema.resolveDefinedValue(case_block, operand_src, loaded_operand)) |op_ptr_val| { |
| 12378 | if (op_ptr_val.isUndef(zcu)) return pt.undefRef(capture_ptr_ty); |
| 12379 | const field_ptr_val = try op_ptr_val.ptrField(first_field_index, pt); |
| 12380 | return .fromValue(try pt.getCoerced(field_ptr_val, capture_ptr_ty)); |
| 12381 | } |
| 12382 | |
| 12383 | try sema.requireRuntimeBlock(case_block, operand_src, null); |
| 12384 | return case_block.addStructFieldPtr(loaded_operand, first_field_index, capture_ptr_ty); |
| 12385 | } |
| 12386 | |
| 12387 | if (try capture_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 12388 | |
| 12389 | if (try sema.resolveDefinedValue(case_block, operand_src, loaded_operand)) |operand_val| { |
| 12390 | if (operand_val.isUndef(zcu)) return pt.undefRef(capture_ty); |
| 12391 | const union_val = ip.indexToKey(operand_val.toIntern()).un; |
| 12392 | if (Value.fromInterned(union_val.tag).isUndef(zcu)) return pt.undefRef(capture_ty); |
| 12393 | const uncoerced: Air.Inst.Ref = .fromIntern(union_val.val); |
| 12394 | return sema.coerce(case_block, capture_ty, uncoerced, operand_src); |
| 12395 | } |
| 12396 | |
| 12397 | try sema.requireRuntimeBlock(case_block, operand_src, null); |
| 12398 | |
| 12399 | if (same_types) { |
| 12400 | return case_block.addStructFieldVal(loaded_operand, first_field_index, capture_ty); |
| 12401 | } |
| 12402 | |
| 12403 | // By-val capture with heterogeneous types which are not all in-memory coercible to |
| 12404 | // the resolved capture type. We finally have to fall back to the ugly method. |
| 12405 | |
| 12406 | const capture_block_inst = try case_block.addInstAsIndex(.{ |
| 12407 | .tag = .block, |
| 12408 | .data = .{ |
| 12409 | .ty_pl = .{ |
| 12410 | .ty = capture_ty, |
| 12411 | .payload = undefined, // updated below |
| 12412 | }, |
| 12413 | }, |
| 12414 | }); |
| 12415 | |
| 12416 | const estimated_extra = field_indices.len * 6 + (field_indices.len / 10); // 2 for Case, 1 item, probably 3 insts; plus hints |
| 12417 | var cases_extra = try std.ArrayList(u32).initCapacity(gpa, estimated_extra); |
| 12418 | defer cases_extra.deinit(gpa); |
| 12419 | |
| 12420 | { |
| 12421 | // All branch hints are `.none`, so just add zero elems. |
| 12422 | comptime assert(@backingInt(std.lang.BranchHint.none) == 0); |
| 12423 | const need_elems = @divCeil(field_indices.len + 1, 10); |
| 12424 | try cases_extra.appendNTimes(gpa, 0, need_elems); |
| 12425 | } |
| 12426 | |
| 12427 | { |
| 12428 | for (field_indices, item_refs, 0..) |field_index, item, item_index| { |
| 12429 | var coerce_block = case_block.makeSubBlock(); |
| 12430 | defer coerce_block.instructions.deinit(sema.gpa); |
| 12431 | |
| 12432 | const case_src: LazySrcLoc = .{ |
| 12433 | .base_node_inst = capture_src.base_node_inst, |
| 12434 | .offset = .{ .switch_case_item = .{ |
| 12435 | .switch_node_offset = switch_node_offset, |
| 12436 | .case_idx = capture_src.offset.switch_capture.case_idx, |
| 12437 | .item_idx = .{ .kind = .single, .value = @intCast(item_index) }, |
| 12438 | } }, |
| 12439 | }; |
| 12440 | |
| 12441 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); |
| 12442 | const uncoerced = try coerce_block.addStructFieldVal(loaded_operand, field_index, field_ty); |
| 12443 | const coerced = try sema.coerce(&coerce_block, capture_ty, uncoerced, case_src); |
| 12444 | _ = try coerce_block.addBr(capture_block_inst, coerced); |
| 12445 | |
| 12446 | try cases_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr.Case).@"struct".field_names.len + |
| 12447 | 1 + // `item`, no ranges |
| 12448 | coerce_block.instructions.items.len); |
| 12449 | cases_extra.appendSliceAssumeCapacity(&payloadToExtraItems(Air.SwitchBr.Case{ |
| 12450 | .items_len = 1, |
| 12451 | .ranges_len = 0, |
| 12452 | .body_len = @intCast(coerce_block.instructions.items.len), |
| 12453 | })); |
| 12454 | cases_extra.appendAssumeCapacity(@backingInt(item)); // item |
| 12455 | cases_extra.appendSliceAssumeCapacity(@ptrCast(coerce_block.instructions.items)); // body |
| 12456 | } |
| 12457 | } |
| 12458 | const else_body_len = len: { |
| 12459 | // 'else' prong is unreachable |
| 12460 | const result_index: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len)); |
| 12461 | try sema.air_instructions.append(gpa, .{ .tag = .unreach, .data = .{ .no_op = {} } }); |
| 12462 | try cases_extra.append(gpa, @backingInt(result_index)); |
| 12463 | break :len 1; |
| 12464 | }; |
| 12465 | |
| 12466 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.SwitchBr).@"struct".field_names.len + |
| 12467 | cases_extra.items.len + |
| 12468 | @typeInfo(Air.Block).@"struct".field_names.len + |
| 12469 | 1); |
| 12470 | |
| 12471 | const switch_br_inst: u32 = @intCast(sema.air_instructions.len); |
| 12472 | try sema.air_instructions.append(gpa, .{ |
| 12473 | .tag = .switch_br, |
| 12474 | .data = .{ |
| 12475 | .pl_op = .{ |
| 12476 | .operand = undefined, // set by switch below |
| 12477 | .payload = sema.addExtraAssumeCapacity(Air.SwitchBr{ |
| 12478 | .cases_len = @intCast(field_indices.len), |
| 12479 | .else_body_len = @intCast(else_body_len), |
| 12480 | }), |
| 12481 | }, |
| 12482 | }, |
| 12483 | }); |
| 12484 | sema.air_extra.appendSliceAssumeCapacity(cases_extra.items); |
| 12485 | |
| 12486 | // Set up block body |
| 12487 | switch (operand) { |
| 12488 | .simple => |s| { |
| 12489 | const air_datas = sema.air_instructions.items(.data); |
| 12490 | air_datas[switch_br_inst].pl_op.operand = s.cond; |
| 12491 | air_datas[@backingInt(capture_block_inst)].ty_pl.payload = |
| 12492 | sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 }); |
| 12493 | sema.air_extra.appendAssumeCapacity(switch_br_inst); |
| 12494 | }, |
| 12495 | .loop => { |
| 12496 | // The block must first extract the tag from the loaded union. |
| 12497 | const tag_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len)); |
| 12498 | try sema.air_instructions.append(sema.gpa, .{ |
| 12499 | .tag = .get_union_tag, |
| 12500 | .data = .{ .ty_op = .{ |
| 12501 | .ty = .fromInterned(union_obj.enum_tag_type), |
| 12502 | .operand = loaded_operand, |
| 12503 | } }, |
| 12504 | }); |
| 12505 | const air_datas = sema.air_instructions.items(.data); |
| 12506 | air_datas[switch_br_inst].pl_op.operand = tag_inst.toRef(); |
| 12507 | air_datas[@backingInt(capture_block_inst)].ty_pl.payload = |
| 12508 | sema.addExtraAssumeCapacity(Air.Block{ .body_len = 2 }); |
| 12509 | sema.air_extra.appendAssumeCapacity(@backingInt(tag_inst)); |
| 12510 | sema.air_extra.appendAssumeCapacity(switch_br_inst); |
| 12511 | }, |
| 12512 | } |
| 12513 | |
| 12514 | return capture_block_inst.toRef(); |
| 12515 | } |
| 12516 | |
| 12517 | const ResolvedSwitchItem = struct { |
| 12518 | ref: Air.Inst.Ref, |
| 12519 | val: Value, |
| 12520 | }; |
| 12521 | const ResolvedSwitchItemAndExtraIndex = struct { ResolvedSwitchItem, usize }; |
| 12522 | |
| 12523 | fn resolveSwitchItem( |
| 12524 | sema: *Sema, |
| 12525 | block: *Block, |
| 12526 | item_src: LazySrcLoc, |
| 12527 | item_ty: Type, |
| 12528 | item_info: Zir.Inst.SwitchBlock.ItemInfo, |
| 12529 | extra_index: usize, |
| 12530 | switch_inst: Zir.Inst.Index, |
| 12531 | prong_is_comptime_unreach: bool, |
| 12532 | ) CompileError!ResolvedSwitchItemAndExtraIndex { |
| 12533 | const pt = sema.pt; |
| 12534 | const zcu = pt.zcu; |
| 12535 | const ip = &zcu.intern_pool; |
| 12536 | const comp = zcu.comp; |
| 12537 | const gpa = comp.gpa; |
| 12538 | const io = comp.io; |
| 12539 | |
| 12540 | var end = extra_index; |
| 12541 | const uncoerced: Air.Inst.Ref, const uncoerced_ty: Type = uncoerced: switch (item_info.unwrap()) { |
| 12542 | .under => unreachable, // caller must check this before calling us |
| 12543 | .enum_literal => |str_index| { |
| 12544 | const zir_str = sema.code.nullTerminatedString(str_index); |
| 12545 | const name = try ip.getOrPutString(gpa, io, pt.tid, zir_str, .no_embedded_nulls); |
| 12546 | const uncoerced = try sema.analyzeDeclLiteral(block, item_src, name, item_ty, false); |
| 12547 | break :uncoerced .{ uncoerced, .enum_literal }; |
| 12548 | }, |
| 12549 | .error_value => |str_index| { |
| 12550 | const zir_str = sema.code.nullTerminatedString(str_index); |
| 12551 | const name = try ip.getOrPutString(gpa, io, pt.tid, zir_str, .no_embedded_nulls); |
| 12552 | // Make sure there's an error integer value associated with `name`. |
| 12553 | _ = try pt.getErrorValue(name); |
| 12554 | const err_set_ty = try pt.singleErrorSetType(name); |
| 12555 | const uncoerced = Air.internedToRef(try pt.intern(.{ .err = .{ |
| 12556 | .ty = err_set_ty.toIntern(), |
| 12557 | .name = name, |
| 12558 | } })); |
| 12559 | break :uncoerced .{ uncoerced, err_set_ty }; |
| 12560 | }, |
| 12561 | .body_len => |body_len| { |
| 12562 | const body = sema.code.bodySlice(extra_index, body_len); |
| 12563 | end += body.len; |
| 12564 | |
| 12565 | const uncoerced = ref: { |
| 12566 | // The result location of item bodies is `.{ .coerce_ty = switch_inst }`. |
| 12567 | sema.inst_map.putAssumeCapacity(switch_inst, .fromType(item_ty)); |
| 12568 | defer assert(sema.inst_map.remove(switch_inst)); |
| 12569 | const old_comptime_reason = block.comptime_reason; |
| 12570 | defer block.comptime_reason = old_comptime_reason; |
| 12571 | block.comptime_reason = .{ .reason = .{ |
| 12572 | .src = item_src, |
| 12573 | .r = .{ .simple = .switch_item }, |
| 12574 | } }; |
| 12575 | break :ref try sema.resolveInlineBody(block, body, switch_inst); |
| 12576 | }; |
| 12577 | break :uncoerced .{ uncoerced, sema.typeOf(uncoerced) }; |
| 12578 | }, |
| 12579 | }; |
| 12580 | const item_ref: Air.Inst.Ref = item_ref: { |
| 12581 | if (item_ty.zigTypeTag(zcu) == .error_set and |
| 12582 | uncoerced_ty.zigTypeTag(zcu) == .error_set) |
| 12583 | { |
| 12584 | // We allow prongs with errors which are not part of the error set |
| 12585 | // being switched on if their prong body is `=> comptime unreachable,`. |
| 12586 | switch (try sema.coerceInMemoryAllowedErrorSets(block, item_ty, uncoerced_ty, item_src, item_src)) { |
| 12587 | .ok => if (sema.resolveValue(uncoerced)) |uncoerced_val| { |
| 12588 | break :item_ref .fromValue(try pt.getCoerced(uncoerced_val, item_ty)); |
| 12589 | }, |
| 12590 | .missing_error => if (prong_is_comptime_unreach) { |
| 12591 | break :item_ref uncoerced; |
| 12592 | }, |
| 12593 | .from_anyerror => {}, |
| 12594 | else => unreachable, |
| 12595 | } |
| 12596 | } |
| 12597 | break :item_ref try sema.coerce(block, item_ty, uncoerced, item_src); |
| 12598 | }; |
| 12599 | const val = try sema.resolveConstDefinedValue(block, item_src, item_ref, .{ .simple = .switch_item }); |
| 12600 | return .{ .{ .ref = item_ref, .val = val }, end }; |
| 12601 | } |
| 12602 | |
| 12603 | fn validateSwitchItemOrRange( |
| 12604 | sema: *Sema, |
| 12605 | block: *Block, |
| 12606 | item_src: LazySrcLoc, |
| 12607 | /// If `opt_last_val` is not `null`, this refers to the first val of a range. |
| 12608 | item_val: Value, |
| 12609 | opt_last_val: ?Value, |
| 12610 | item_ty: Type, |
| 12611 | seen: *ValidatedSwitchBlock.Seen, |
| 12612 | ) CompileError!void { |
| 12613 | const pt = sema.pt; |
| 12614 | const zcu = pt.zcu; |
| 12615 | const ip = &zcu.intern_pool; |
| 12616 | const maybe_prev_src: ?LazySrcLoc = maybe_prev_src: switch (item_ty.zigTypeTag(zcu)) { |
| 12617 | .@"enum" => { |
| 12618 | const int = ip.indexToKey(item_val.toIntern()).enum_tag.int; |
| 12619 | if (ip.loadEnumType(item_ty.toIntern()).tagValueIndex(ip, int)) |field_index| { |
| 12620 | const maybe_prev_src = seen.enum_fields[field_index]; |
| 12621 | seen.enum_fields[field_index] = item_src; |
| 12622 | break :maybe_prev_src maybe_prev_src; |
| 12623 | } else { |
| 12624 | try seen.ranges.ensureUnusedCapacity(sema.arena, 1); |
| 12625 | break :maybe_prev_src if (seen.ranges.addAssumeCapacity(.{ |
| 12626 | .first = .fromInterned(int), |
| 12627 | .last = .fromInterned(int), |
| 12628 | .src = item_src, |
| 12629 | }, .fromInterned(ip.typeOf(int)), zcu)) |prev| prev.src else null; |
| 12630 | } |
| 12631 | }, |
| 12632 | .error_set => { |
| 12633 | const error_name = ip.indexToKey(item_val.toIntern()).err.name; |
| 12634 | break :maybe_prev_src if (seen.errors.fetchPutAssumeCapacity(error_name, item_src)) |prev| |
| 12635 | prev.value |
| 12636 | else |
| 12637 | null; |
| 12638 | }, |
| 12639 | .int, .comptime_int => { |
| 12640 | const first_val = item_val; |
| 12641 | const last_val: Value = last_val: { |
| 12642 | const last_val = opt_last_val orelse break :last_val item_val; |
| 12643 | if (try first_val.compareAll(.gt, last_val, item_ty, pt)) { |
| 12644 | return sema.fail(block, item_src, "range start value is greater than the end value", .{}); |
| 12645 | } |
| 12646 | break :last_val last_val; |
| 12647 | }; |
| 12648 | if (seen.ranges.addAssumeCapacity(.{ |
| 12649 | .first = first_val, |
| 12650 | .last = last_val, |
| 12651 | .src = item_src, |
| 12652 | }, item_ty, zcu)) |prev_range| { |
| 12653 | const overlap_start = first_val.numberMax(prev_range.first, zcu); |
| 12654 | const overlap_end = last_val.numberMin(prev_range.last, zcu); |
| 12655 | if (overlap_start.eql(overlap_end, item_ty, zcu)) { |
| 12656 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 12657 | const msg = try sema.errMsg(item_src, "duplicate switch value '{f}'", .{ |
| 12658 | overlap_start.fmtValueSema(pt, sema), |
| 12659 | }); |
| 12660 | errdefer msg.destroy(sema.gpa); |
| 12661 | if (prev_range.first.eql(prev_range.last, item_ty, zcu)) { |
| 12662 | try sema.errNote(prev_range.src, msg, "previous value here", .{}); |
| 12663 | } else { |
| 12664 | try sema.errNote(prev_range.src, msg, "previous value inside range here", .{}); |
| 12665 | } |
| 12666 | break :msg msg; |
| 12667 | }); |
| 12668 | } |
| 12669 | assert(!prev_range.first.eql(prev_range.last, item_ty, zcu)); |
| 12670 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 12671 | const msg = try sema.errMsg(item_src, "duplicate switch ranges", .{}); |
| 12672 | errdefer msg.destroy(sema.gpa); |
| 12673 | if (first_val.eql(prev_range.first, item_ty, zcu) and |
| 12674 | last_val.eql(prev_range.last, item_ty, zcu)) |
| 12675 | { |
| 12676 | try sema.errNote(prev_range.src, msg, "previous range here", .{}); |
| 12677 | } else { |
| 12678 | try sema.errNote(prev_range.src, msg, "overlaps with previous range here", .{}); |
| 12679 | try sema.errNote(prev_range.src, msg, "ranges overlap from '{f}' to '{f}'", .{ |
| 12680 | overlap_start.fmtValueSema(pt, sema), overlap_end.fmtValueSema(pt, sema), |
| 12681 | }); |
| 12682 | } |
| 12683 | break :msg msg; |
| 12684 | }); |
| 12685 | } |
| 12686 | break :maybe_prev_src null; |
| 12687 | }, |
| 12688 | .@"union", .@"struct" => { |
| 12689 | const backing_int_val = ip.indexToKey(item_val.toIntern()).bitpack.backing_int_val; |
| 12690 | break :maybe_prev_src if (seen.ranges.addAssumeCapacity(.{ |
| 12691 | .first = .fromInterned(backing_int_val), |
| 12692 | .last = .fromInterned(backing_int_val), |
| 12693 | .src = item_src, |
| 12694 | }, item_ty.backingIntType(zcu), zcu)) |prev| prev.src else null; |
| 12695 | }, |
| 12696 | .enum_literal, .@"fn", .pointer, .type => { |
| 12697 | break :maybe_prev_src if (seen.sparse_values.fetchPutAssumeCapacity(item_val.toIntern(), item_src)) |prev| |
| 12698 | prev.value |
| 12699 | else |
| 12700 | null; |
| 12701 | }, |
| 12702 | .bool => { |
| 12703 | if (item_val.toBool()) { |
| 12704 | if (seen.true_src) |prev_src| break :maybe_prev_src prev_src; |
| 12705 | seen.true_src = item_src; |
| 12706 | } else { |
| 12707 | if (seen.false_src) |prev_src| break :maybe_prev_src prev_src; |
| 12708 | seen.false_src = item_src; |
| 12709 | } |
| 12710 | break :maybe_prev_src null; |
| 12711 | }, |
| 12712 | .void => { |
| 12713 | if (seen.void_src) |prev_src| break :maybe_prev_src prev_src; |
| 12714 | seen.void_src = item_src; |
| 12715 | break :maybe_prev_src null; |
| 12716 | }, |
| 12717 | else => unreachable, // should have already checked for invalid types |
| 12718 | }; |
| 12719 | if (maybe_prev_src) |prev_src| { |
| 12720 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 12721 | const msg = try sema.errMsg(item_src, "duplicate switch value '{f}'", .{ |
| 12722 | item_val.fmtValueSema(pt, sema), |
| 12723 | }); |
| 12724 | errdefer msg.destroy(sema.gpa); |
| 12725 | try sema.errNote(prev_src, msg, "previous value here", .{}); |
| 12726 | if (item_ty.zigTypeTag(zcu) == .type) { |
| 12727 | try sema.addDeclaredHereNote(msg, item_val.toType()); |
| 12728 | } else { |
| 12729 | try sema.addDeclaredHereNote(msg, item_ty); |
| 12730 | } |
| 12731 | break :msg msg; |
| 12732 | }); |
| 12733 | } |
| 12734 | } |
| 12735 | |
| 12736 | fn maybeErrorUnwrap( |
| 12737 | sema: *Sema, |
| 12738 | block: *Block, |
| 12739 | body: []const Zir.Inst.Index, |
| 12740 | operand: Air.Inst.Ref, |
| 12741 | operand_src: LazySrcLoc, |
| 12742 | allow_err_code_inst: bool, |
| 12743 | ) !bool { |
| 12744 | const pt = sema.pt; |
| 12745 | const zcu = pt.zcu; |
| 12746 | |
| 12747 | const tags = sema.code.instructions.items(.tag); |
| 12748 | for (body) |inst| { |
| 12749 | switch (tags[@backingInt(inst)]) { |
| 12750 | .@"unreachable" => if (!block.wantSafety()) return false, |
| 12751 | .err_union_code => if (!allow_err_code_inst) return false, |
| 12752 | .save_err_ret_index, |
| 12753 | .dbg_stmt, |
| 12754 | .str, |
| 12755 | .as_node, |
| 12756 | .panic, |
| 12757 | => {}, |
| 12758 | else => return false, |
| 12759 | } |
| 12760 | } |
| 12761 | |
| 12762 | for (body) |inst| { |
| 12763 | const air_inst = switch (tags[@backingInt(inst)]) { |
| 12764 | .err_union_code => continue, |
| 12765 | .dbg_stmt => { |
| 12766 | try sema.zirDbgStmt(block, inst); |
| 12767 | continue; |
| 12768 | }, |
| 12769 | .save_err_ret_index => { |
| 12770 | try sema.zirSaveErrRetIndex(block, inst); |
| 12771 | continue; |
| 12772 | }, |
| 12773 | .str => try sema.zirStr(inst), |
| 12774 | .as_node => try sema.zirAsNode(block, inst), |
| 12775 | .@"unreachable" => { |
| 12776 | try safetyPanicUnwrapError(sema, block, operand_src, operand); |
| 12777 | return true; |
| 12778 | }, |
| 12779 | .panic => { |
| 12780 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 12781 | const msg_inst = sema.resolveInst(inst_data.operand); |
| 12782 | |
| 12783 | const panic_fn = try getStdLangValue(sema, operand_src, .@"panic.call"); |
| 12784 | const args: [2]Air.Inst.Ref = .{ msg_inst, .null_value }; |
| 12785 | try sema.callBuiltin(block, operand_src, Air.internedToRef(panic_fn), .auto, &args, .@"safety check"); |
| 12786 | return true; |
| 12787 | }, |
| 12788 | else => unreachable, |
| 12789 | }; |
| 12790 | if (sema.typeOf(air_inst).isNoReturn(zcu)) |
| 12791 | return true; |
| 12792 | sema.inst_map.putAssumeCapacity(inst, air_inst); |
| 12793 | } |
| 12794 | unreachable; |
| 12795 | } |
| 12796 | |
| 12797 | fn maybeErrorUnwrapCondbr(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, cond: Zir.Inst.Ref, cond_src: LazySrcLoc) !void { |
| 12798 | const pt = sema.pt; |
| 12799 | const zcu = pt.zcu; |
| 12800 | const index = cond.toIndex() orelse return; |
| 12801 | if (sema.code.instructions.items(.tag)[@backingInt(index)] != .is_non_err) return; |
| 12802 | |
| 12803 | const err_inst_data = sema.code.instructions.items(.data)[@backingInt(index)].un_node; |
| 12804 | const err_operand = sema.resolveInst(err_inst_data.operand); |
| 12805 | const operand_ty = sema.typeOf(err_operand); |
| 12806 | if (operand_ty.zigTypeTag(zcu) == .error_set) { |
| 12807 | try sema.maybeErrorUnwrapComptime(block, body, err_operand); |
| 12808 | return; |
| 12809 | } |
| 12810 | if (try sema.resolveDefinedValue(block, cond_src, err_operand)) |val| { |
| 12811 | if (!operand_ty.isError(zcu)) return; |
| 12812 | if (val.getErrorName(zcu) == .none) return; |
| 12813 | try sema.maybeErrorUnwrapComptime(block, body, err_operand); |
| 12814 | } |
| 12815 | } |
| 12816 | |
| 12817 | fn maybeErrorUnwrapComptime(sema: *Sema, block: *Block, body: []const Zir.Inst.Index, operand: Air.Inst.Ref) !void { |
| 12818 | const tags = sema.code.instructions.items(.tag); |
| 12819 | const inst = for (body) |inst| { |
| 12820 | switch (tags[@backingInt(inst)]) { |
| 12821 | .dbg_stmt, |
| 12822 | .save_err_ret_index, |
| 12823 | => {}, |
| 12824 | .@"unreachable" => break inst, |
| 12825 | else => return, |
| 12826 | } |
| 12827 | } else return; |
| 12828 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].@"unreachable"; |
| 12829 | const src = block.nodeOffset(inst_data.src_node); |
| 12830 | |
| 12831 | if (try sema.resolveDefinedValue(block, src, operand)) |val| { |
| 12832 | if (val.getErrorName(sema.pt.zcu).unwrap()) |name| { |
| 12833 | return sema.failWithComptimeErrorRetTrace(block, src, name); |
| 12834 | } |
| 12835 | } |
| 12836 | } |
| 12837 | |
| 12838 | fn zirHasField(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 12839 | const pt = sema.pt; |
| 12840 | const zcu = pt.zcu; |
| 12841 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 12842 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 12843 | const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 12844 | const name_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 12845 | const ty = try sema.resolveType(block, ty_src, extra.lhs); |
| 12846 | const field_name = try sema.resolveConstStringIntern(block, name_src, extra.rhs, .{ .simple = .field_name }); |
| 12847 | try sema.ensureLayoutResolved(ty, ty_src, .field_queried); |
| 12848 | const ip = &zcu.intern_pool; |
| 12849 | |
| 12850 | const has_field = hf: { |
| 12851 | switch (ip.indexToKey(ty.toIntern())) { |
| 12852 | .ptr_type => |ptr_type| switch (ptr_type.flags.size) { |
| 12853 | .slice => { |
| 12854 | if (field_name.eqlSlice("ptr", ip)) break :hf true; |
| 12855 | if (field_name.eqlSlice("len", ip)) break :hf true; |
| 12856 | break :hf false; |
| 12857 | }, |
| 12858 | else => {}, |
| 12859 | }, |
| 12860 | .tuple_type => |tuple| { |
| 12861 | const field_index = field_name.toUnsigned(ip) orelse break :hf false; |
| 12862 | break :hf field_index < tuple.types.len; |
| 12863 | }, |
| 12864 | .struct_type => { |
| 12865 | break :hf ip.loadStructType(ty.toIntern()).nameIndex(ip, field_name) != null; |
| 12866 | }, |
| 12867 | .union_type => { |
| 12868 | const union_type = ip.loadUnionType(ty.toIntern()); |
| 12869 | const enum_type = ip.loadEnumType(union_type.enum_tag_type); |
| 12870 | break :hf enum_type.nameIndex(ip, field_name) != null; |
| 12871 | }, |
| 12872 | .enum_type => { |
| 12873 | break :hf ip.loadEnumType(ty.toIntern()).nameIndex(ip, field_name) != null; |
| 12874 | }, |
| 12875 | .array_type => break :hf field_name.eqlSlice("len", ip), |
| 12876 | else => {}, |
| 12877 | } |
| 12878 | return sema.fail(block, ty_src, "type '{f}' does not support '@hasField'", .{ |
| 12879 | ty.fmt(pt), |
| 12880 | }); |
| 12881 | }; |
| 12882 | return if (has_field) .bool_true else .bool_false; |
| 12883 | } |
| 12884 | |
| 12885 | fn zirHasDecl(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 12886 | const pt = sema.pt; |
| 12887 | const zcu = pt.zcu; |
| 12888 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 12889 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 12890 | const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 12891 | const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 12892 | const container_type = try sema.resolveType(block, lhs_src, extra.lhs); |
| 12893 | const decl_name = try sema.resolveConstStringIntern(block, rhs_src, extra.rhs, .{ .simple = .decl_name }); |
| 12894 | |
| 12895 | try sema.checkNamespaceType(block, lhs_src, container_type); |
| 12896 | |
| 12897 | const namespace = container_type.getNamespace(zcu).unwrap() orelse return .bool_false; |
| 12898 | if (try sema.lookupInNamespace(block, namespace, decl_name)) |lookup| { |
| 12899 | if (lookup.accessible == .public) { |
| 12900 | return .bool_true; |
| 12901 | } |
| 12902 | } |
| 12903 | return .bool_false; |
| 12904 | } |
| 12905 | |
| 12906 | fn zirImport(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 12907 | const pt = sema.pt; |
| 12908 | const zcu = pt.zcu; |
| 12909 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_tok; |
| 12910 | const extra = sema.code.extraData(Zir.Inst.Import, inst_data.payload_index).data; |
| 12911 | const operand_src = block.tokenOffset(inst_data.src_tok); |
| 12912 | const operand = sema.code.nullTerminatedString(extra.path); |
| 12913 | |
| 12914 | const result = pt.doImport(block.getFileScope(zcu), operand) catch |err| switch (err) { |
| 12915 | error.ModuleNotFound => return sema.fail(block, operand_src, "no module named '{s}' available within module '{s}'", .{ |
| 12916 | operand, block.getFileScope(zcu).mod.?.fully_qualified_name, |
| 12917 | }), |
| 12918 | error.IllegalZigImport => unreachable, // caught before semantic analysis |
| 12919 | error.OutOfMemory => |e| return e, |
| 12920 | }; |
| 12921 | const file_index = result.file; |
| 12922 | const file = zcu.fileByIndex(file_index); |
| 12923 | try sema.declareDependency(.{ .source_file = file_index }); |
| 12924 | switch (file.getMode()) { |
| 12925 | .zig => { |
| 12926 | try pt.ensureFilePopulated(file_index); |
| 12927 | const ty: Type = .fromInterned(zcu.fileRootType(file_index)); |
| 12928 | try sema.addTypeReferenceEntry(operand_src, ty); |
| 12929 | // No need for `ensureNamespaceUpToDate`, because `Zcu.PerThread.updateFileNamespace` |
| 12930 | // already made sure that all root file structs have up-to-date namespaces. |
| 12931 | return .fromType(ty); |
| 12932 | }, |
| 12933 | .zon => { |
| 12934 | const res_ty: InternPool.Index = b: { |
| 12935 | if (extra.res_ty == .none) break :b .none; |
| 12936 | const res_ty_inst = sema.resolveInst(extra.res_ty); |
| 12937 | const res_ty = try sema.analyzeAsType(block, operand_src, .type, res_ty_inst); |
| 12938 | if (res_ty.isGenericPoison()) break :b .none; |
| 12939 | break :b res_ty.toIntern(); |
| 12940 | }; |
| 12941 | const interned = try LowerZon.run( |
| 12942 | sema, |
| 12943 | file, |
| 12944 | file_index, |
| 12945 | res_ty, |
| 12946 | operand_src, |
| 12947 | block, |
| 12948 | ); |
| 12949 | return Air.internedToRef(interned); |
| 12950 | }, |
| 12951 | } |
| 12952 | } |
| 12953 | |
| 12954 | fn zirEmbedFile(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 12955 | const pt = sema.pt; |
| 12956 | const zcu = pt.zcu; |
| 12957 | |
| 12958 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 12959 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 12960 | const name = try sema.resolveConstString(block, operand_src, inst_data.operand, .{ .simple = .operand_embedFile }); |
| 12961 | |
| 12962 | if (name.len == 0) { |
| 12963 | return sema.fail(block, operand_src, "file path name cannot be empty", .{}); |
| 12964 | } |
| 12965 | |
| 12966 | const ef_idx = pt.embedFile(block.getFileScope(zcu), name) catch |err| switch (err) { |
| 12967 | error.ImportOutsideModulePath => { |
| 12968 | return sema.fail(block, operand_src, "embed of file outside package path: '{s}'", .{name}); |
| 12969 | }, |
| 12970 | error.OutOfMemory => |e| return e, |
| 12971 | error.Canceled => |e| return e, |
| 12972 | }; |
| 12973 | try sema.declareDependency(.{ .embed_file = ef_idx }); |
| 12974 | |
| 12975 | const result = ef_idx.get(zcu); |
| 12976 | if (result.val == .none) { |
| 12977 | return sema.fail(block, operand_src, "unable to open '{s}': {s}", .{ name, @errorName(result.err.?) }); |
| 12978 | } |
| 12979 | |
| 12980 | return Air.internedToRef(result.val); |
| 12981 | } |
| 12982 | |
| 12983 | fn zirShl( |
| 12984 | sema: *Sema, |
| 12985 | block: *Block, |
| 12986 | inst: Zir.Inst.Index, |
| 12987 | air_tag: Air.Inst.Tag, |
| 12988 | ) CompileError!Air.Inst.Ref { |
| 12989 | const pt = sema.pt; |
| 12990 | const zcu = pt.zcu; |
| 12991 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 12992 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 12993 | const lhs = sema.resolveInst(extra.lhs); |
| 12994 | const rhs = sema.resolveInst(extra.rhs); |
| 12995 | const lhs_ty = sema.typeOf(lhs); |
| 12996 | const rhs_ty = sema.typeOf(rhs); |
| 12997 | |
| 12998 | const src = block.nodeOffset(inst_data.src_node); |
| 12999 | const lhs_src, const rhs_src = switch (air_tag) { |
| 13000 | .shl, .shl_sat => .{ |
| 13001 | block.src(.{ .node_offset_bin_lhs = inst_data.src_node }), |
| 13002 | block.src(.{ .node_offset_bin_rhs = inst_data.src_node }), |
| 13003 | }, |
| 13004 | .shl_exact => .{ |
| 13005 | block.builtinCallArgSrc(inst_data.src_node, 0), |
| 13006 | block.builtinCallArgSrc(inst_data.src_node, 1), |
| 13007 | }, |
| 13008 | else => unreachable, |
| 13009 | }; |
| 13010 | |
| 13011 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 13012 | |
| 13013 | const scalar_ty = lhs_ty.scalarType(zcu); |
| 13014 | const scalar_rhs_ty = rhs_ty.scalarType(zcu); |
| 13015 | |
| 13016 | // AstGen currently forces the rhs of `<<` to coerce to the correct type before the `.shl` instruction, so |
| 13017 | // we already know `scalar_rhs_ty` is valid for `.shl`; likewise the lhs is validated when its |
| 13018 | // `typeof_log2_int_type` is evaluated. `.shl_sat` gets neither coercion, so validate both operands here. |
| 13019 | if (air_tag == .shl_sat) { |
| 13020 | _ = try sema.log2IntType(block, lhs_ty, lhs_src); |
| 13021 | _ = try sema.checkIntType(block, rhs_src, scalar_rhs_ty); |
| 13022 | } |
| 13023 | |
| 13024 | const maybe_lhs_val = sema.resolveValue(lhs); |
| 13025 | const maybe_rhs_val = sema.resolveValue(rhs); |
| 13026 | |
| 13027 | const runtime_src = rs: { |
| 13028 | if (maybe_rhs_val) |rhs_val| { |
| 13029 | if (maybe_lhs_val) |lhs_val| { |
| 13030 | return .fromValue(try arith.shl(sema, block, lhs_ty, lhs_val, rhs_val, src, lhs_src, rhs_src, switch (air_tag) { |
| 13031 | .shl => .shl, |
| 13032 | .shl_sat => .shl_sat, |
| 13033 | .shl_exact => .shl_exact, |
| 13034 | else => unreachable, |
| 13035 | })); |
| 13036 | } |
| 13037 | if (rhs_val.isUndef(zcu)) switch (air_tag) { |
| 13038 | .shl_sat => return pt.undefRef(lhs_ty), |
| 13039 | .shl, .shl_exact => return sema.failWithUseOfUndef(block, rhs_src, null), |
| 13040 | else => unreachable, |
| 13041 | }; |
| 13042 | const bits = scalar_ty.intInfo(zcu).bits; |
| 13043 | switch (rhs_ty.zigTypeTag(zcu)) { |
| 13044 | .int, .comptime_int => { |
| 13045 | switch (Value.order(rhs_val, .zero_comptime_int, zcu)) { |
| 13046 | .gt => { |
| 13047 | if (air_tag != .shl_sat) { |
| 13048 | var rhs_space: Value.BigIntSpace = undefined; |
| 13049 | const rhs_bigint = rhs_val.toBigInt(&rhs_space, zcu); |
| 13050 | if (rhs_bigint.orderAgainstScalar(bits) != .lt) { |
| 13051 | return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_val, rhs_src, null); |
| 13052 | } |
| 13053 | } |
| 13054 | }, |
| 13055 | .eq => return lhs, |
| 13056 | .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, null), |
| 13057 | } |
| 13058 | }, |
| 13059 | .vector => { |
| 13060 | var any_positive: bool = false; |
| 13061 | for (0..rhs_ty.vectorLen(zcu)) |elem_idx| { |
| 13062 | const rhs_elem = try rhs_val.elemValue(pt, elem_idx); |
| 13063 | if (rhs_elem.isUndef(zcu)) switch (air_tag) { |
| 13064 | .shl_sat => continue, |
| 13065 | .shl, .shl_exact => return sema.failWithUseOfUndef(block, rhs_src, elem_idx), |
| 13066 | else => unreachable, |
| 13067 | }; |
| 13068 | switch (Value.order(rhs_elem, .zero_comptime_int, zcu)) { |
| 13069 | .gt => { |
| 13070 | if (air_tag != .shl_sat) { |
| 13071 | var rhs_elem_space: Value.BigIntSpace = undefined; |
| 13072 | const rhs_elem_bigint = rhs_elem.toBigInt(&rhs_elem_space, zcu); |
| 13073 | if (rhs_elem_bigint.orderAgainstScalar(bits) != .lt) { |
| 13074 | return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_elem, rhs_src, elem_idx); |
| 13075 | } |
| 13076 | } |
| 13077 | any_positive = true; |
| 13078 | }, |
| 13079 | .eq => {}, |
| 13080 | .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_elem, elem_idx), |
| 13081 | } |
| 13082 | } |
| 13083 | if (!any_positive) return lhs; |
| 13084 | }, |
| 13085 | else => unreachable, |
| 13086 | } |
| 13087 | break :rs lhs_src; |
| 13088 | } else { |
| 13089 | if (air_tag == .shl_sat and scalar_rhs_ty.isSignedInt(zcu)) { |
| 13090 | return sema.fail(block, rhs_src, "shift by signed type '{f}'", .{rhs_ty.fmt(pt)}); |
| 13091 | } |
| 13092 | if (scalar_ty.toIntern() == .comptime_int_type) { |
| 13093 | return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{}); |
| 13094 | } |
| 13095 | if (maybe_lhs_val) |lhs_val| { |
| 13096 | switch (air_tag) { |
| 13097 | .shl_sat => if (lhs_val.isUndef(zcu)) return pt.undefRef(lhs_ty), |
| 13098 | .shl, .shl_exact => try sema.checkAllScalarsDefined(block, lhs_src, lhs_val), |
| 13099 | else => unreachable, |
| 13100 | } |
| 13101 | if (lhs_val.compareAllWithZero(.eq, zcu)) return lhs; |
| 13102 | } |
| 13103 | } |
| 13104 | break :rs rhs_src; |
| 13105 | }; |
| 13106 | const rt_rhs: Air.Inst.Ref = switch (air_tag) { |
| 13107 | else => unreachable, |
| 13108 | .shl, .shl_exact => rhs, |
| 13109 | // The backend can handle a large runtime rhs better than we can, but |
| 13110 | // we can limit a large comptime rhs better here. This also has the |
| 13111 | // necessary side effect of preventing rhs from being a `comptime_int`. |
| 13112 | .shl_sat => if (maybe_rhs_val) |rhs_val| .fromValue(rt_rhs: { |
| 13113 | const bit_count = scalar_ty.intInfo(zcu).bits; |
| 13114 | const rt_rhs_scalar_ty = try pt.smallestUnsignedInt(bit_count); |
| 13115 | if (!rhs_ty.isVector(zcu)) break :rt_rhs try pt.intValue( |
| 13116 | rt_rhs_scalar_ty, |
| 13117 | @min(rhs_val.getUnsignedInt(zcu) orelse bit_count, bit_count), |
| 13118 | ); |
| 13119 | const rhs_len = rhs_ty.vectorLen(zcu); |
| 13120 | const rhs_elems = try sema.arena.alloc(InternPool.Index, rhs_len); |
| 13121 | for (rhs_elems, 0..) |*rhs_elem, i| rhs_elem.* = (try pt.intValue( |
| 13122 | rt_rhs_scalar_ty, |
| 13123 | @min((try rhs_val.elemValue(pt, i)).getUnsignedInt(zcu) orelse bit_count, bit_count), |
| 13124 | )).toIntern(); |
| 13125 | break :rt_rhs try pt.aggregateValue(try pt.vectorType(.{ |
| 13126 | .len = rhs_len, |
| 13127 | .child = rt_rhs_scalar_ty.toIntern(), |
| 13128 | }), rhs_elems); |
| 13129 | }) else rhs, |
| 13130 | }; |
| 13131 | |
| 13132 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 13133 | if (block.wantSafety()) { |
| 13134 | const bit_count = scalar_ty.intInfo(zcu).bits; |
| 13135 | if (air_tag != .shl_sat and !std.math.isPowerOfTwo(bit_count)) { |
| 13136 | const bit_count_val = try pt.intValue(scalar_rhs_ty, bit_count); |
| 13137 | const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: { |
| 13138 | const bit_count_inst = Air.internedToRef((try sema.splat(rhs_ty, bit_count_val)).toIntern()); |
| 13139 | const lt = try block.addCmpVector(rhs, bit_count_inst, .lt); |
| 13140 | break :ok try block.addReduce(lt, .And); |
| 13141 | } else ok: { |
| 13142 | const bit_count_inst = Air.internedToRef(bit_count_val.toIntern()); |
| 13143 | break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst); |
| 13144 | }; |
| 13145 | try sema.addSafetyCheck(block, src, ok, .shift_rhs_too_big); |
| 13146 | } |
| 13147 | |
| 13148 | if (air_tag == .shl_exact) { |
| 13149 | const op_ov_tuple_ty = try pt.overflowArithmeticTupleType(lhs_ty); |
| 13150 | const op_ov = try block.addInst(.{ |
| 13151 | .tag = .shl_with_overflow, |
| 13152 | .data = .{ .ty_pl = .{ |
| 13153 | .ty = op_ov_tuple_ty, |
| 13154 | .payload = try sema.addExtra(Air.Bin{ |
| 13155 | .lhs = lhs, |
| 13156 | .rhs = rhs, |
| 13157 | }), |
| 13158 | } }, |
| 13159 | }); |
| 13160 | const ov_bit = try sema.tupleFieldValByIndex(block, op_ov, 1, op_ov_tuple_ty); |
| 13161 | const any_ov_bit = if (lhs_ty.zigTypeTag(zcu) == .vector) |
| 13162 | try block.addReduce(ov_bit, .Or) |
| 13163 | else |
| 13164 | ov_bit; |
| 13165 | const no_ov = try block.addBinOp(.cmp_eq, any_ov_bit, .zero_u1); |
| 13166 | |
| 13167 | try sema.addSafetyCheck(block, src, no_ov, .shl_overflow); |
| 13168 | return sema.tupleFieldValByIndex(block, op_ov, 0, op_ov_tuple_ty); |
| 13169 | } |
| 13170 | } |
| 13171 | return block.addBinOp(air_tag, lhs, rt_rhs); |
| 13172 | } |
| 13173 | |
| 13174 | fn zirShr( |
| 13175 | sema: *Sema, |
| 13176 | block: *Block, |
| 13177 | inst: Zir.Inst.Index, |
| 13178 | air_tag: Air.Inst.Tag, |
| 13179 | ) CompileError!Air.Inst.Ref { |
| 13180 | const pt = sema.pt; |
| 13181 | const zcu = pt.zcu; |
| 13182 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 13183 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 13184 | const lhs = sema.resolveInst(extra.lhs); |
| 13185 | const rhs = sema.resolveInst(extra.rhs); |
| 13186 | const lhs_ty = sema.typeOf(lhs); |
| 13187 | const rhs_ty = sema.typeOf(rhs); |
| 13188 | |
| 13189 | const src = block.nodeOffset(inst_data.src_node); |
| 13190 | const lhs_src = switch (air_tag) { |
| 13191 | .shr => block.src(.{ .node_offset_bin_lhs = inst_data.src_node }), |
| 13192 | .shr_exact => block.builtinCallArgSrc(inst_data.src_node, 0), |
| 13193 | else => unreachable, |
| 13194 | }; |
| 13195 | const rhs_src = switch (air_tag) { |
| 13196 | .shr => block.src(.{ .node_offset_bin_rhs = inst_data.src_node }), |
| 13197 | .shr_exact => block.builtinCallArgSrc(inst_data.src_node, 1), |
| 13198 | else => unreachable, |
| 13199 | }; |
| 13200 | |
| 13201 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 13202 | const scalar_ty = lhs_ty.scalarType(zcu); |
| 13203 | |
| 13204 | const maybe_lhs_val = sema.resolveValue(lhs); |
| 13205 | const maybe_rhs_val = sema.resolveValue(rhs); |
| 13206 | |
| 13207 | const runtime_src = rs: { |
| 13208 | if (maybe_rhs_val) |rhs_val| { |
| 13209 | if (maybe_lhs_val) |lhs_val| { |
| 13210 | return .fromValue(try arith.shr(sema, block, lhs_ty, rhs_ty, lhs_val, rhs_val, src, lhs_src, rhs_src, switch (air_tag) { |
| 13211 | .shr => .shr, |
| 13212 | .shr_exact => .shr_exact, |
| 13213 | else => unreachable, |
| 13214 | })); |
| 13215 | } |
| 13216 | if (rhs_val.isUndef(zcu)) { |
| 13217 | return sema.failWithUseOfUndef(block, rhs_src, null); |
| 13218 | } |
| 13219 | const bits = scalar_ty.intInfo(zcu).bits; |
| 13220 | switch (rhs_ty.zigTypeTag(zcu)) { |
| 13221 | .int, .comptime_int => { |
| 13222 | switch (Value.order(rhs_val, .zero_comptime_int, zcu)) { |
| 13223 | .gt => { |
| 13224 | var rhs_space: Value.BigIntSpace = undefined; |
| 13225 | const rhs_bigint = rhs_val.toBigInt(&rhs_space, zcu); |
| 13226 | if (rhs_bigint.orderAgainstScalar(bits) != .lt) { |
| 13227 | return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_val, rhs_src, null); |
| 13228 | } |
| 13229 | }, |
| 13230 | .eq => return lhs, |
| 13231 | .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, null), |
| 13232 | } |
| 13233 | }, |
| 13234 | .vector => { |
| 13235 | var any_positive: bool = false; |
| 13236 | for (0..rhs_ty.vectorLen(zcu)) |elem_idx| { |
| 13237 | const rhs_elem = try rhs_val.elemValue(pt, elem_idx); |
| 13238 | if (rhs_elem.isUndef(zcu)) { |
| 13239 | return sema.failWithUseOfUndef(block, rhs_src, elem_idx); |
| 13240 | } |
| 13241 | switch (Value.order(rhs_elem, .zero_comptime_int, zcu)) { |
| 13242 | .gt => { |
| 13243 | var rhs_elem_space: Value.BigIntSpace = undefined; |
| 13244 | const rhs_elem_bigint = rhs_elem.toBigInt(&rhs_elem_space, zcu); |
| 13245 | if (rhs_elem_bigint.orderAgainstScalar(bits) != .lt) { |
| 13246 | return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_elem, rhs_src, elem_idx); |
| 13247 | } |
| 13248 | any_positive = true; |
| 13249 | }, |
| 13250 | .eq => {}, |
| 13251 | .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_elem, elem_idx), |
| 13252 | } |
| 13253 | } |
| 13254 | if (!any_positive) return lhs; |
| 13255 | }, |
| 13256 | else => unreachable, |
| 13257 | } |
| 13258 | break :rs lhs_src; |
| 13259 | } else { |
| 13260 | if (scalar_ty.toIntern() == .comptime_int_type) { |
| 13261 | return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{}); |
| 13262 | } |
| 13263 | if (maybe_lhs_val) |lhs_val| { |
| 13264 | try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); |
| 13265 | if (lhs_val.compareAllWithZero(.eq, zcu)) return lhs; |
| 13266 | } |
| 13267 | } |
| 13268 | break :rs rhs_src; |
| 13269 | }; |
| 13270 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 13271 | const result = try block.addBinOp(air_tag, lhs, rhs); |
| 13272 | if (block.wantSafety()) { |
| 13273 | const bit_count = scalar_ty.intInfo(zcu).bits; |
| 13274 | if (!std.math.isPowerOfTwo(bit_count)) { |
| 13275 | const bit_count_val = try pt.intValue(rhs_ty.scalarType(zcu), bit_count); |
| 13276 | |
| 13277 | const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: { |
| 13278 | const bit_count_inst = Air.internedToRef((try sema.splat(rhs_ty, bit_count_val)).toIntern()); |
| 13279 | const lt = try block.addCmpVector(rhs, bit_count_inst, .lt); |
| 13280 | break :ok try block.addReduce(lt, .And); |
| 13281 | } else ok: { |
| 13282 | const bit_count_inst = Air.internedToRef(bit_count_val.toIntern()); |
| 13283 | break :ok try block.addBinOp(.cmp_lt, rhs, bit_count_inst); |
| 13284 | }; |
| 13285 | try sema.addSafetyCheck(block, src, ok, .shift_rhs_too_big); |
| 13286 | } |
| 13287 | |
| 13288 | if (air_tag == .shr_exact) { |
| 13289 | const back = try block.addBinOp(.shl, result, rhs); |
| 13290 | |
| 13291 | const ok = if (rhs_ty.zigTypeTag(zcu) == .vector) ok: { |
| 13292 | const eql = try block.addCmpVector(lhs, back, .eq); |
| 13293 | break :ok try block.addReduce(eql, .And); |
| 13294 | } else try block.addBinOp(.cmp_eq, lhs, back); |
| 13295 | try sema.addSafetyCheck(block, src, ok, .shr_overflow); |
| 13296 | } |
| 13297 | } |
| 13298 | return result; |
| 13299 | } |
| 13300 | |
| 13301 | fn zirBitwise( |
| 13302 | sema: *Sema, |
| 13303 | block: *Block, |
| 13304 | inst: Zir.Inst.Index, |
| 13305 | air_tag: Air.Inst.Tag, |
| 13306 | ) CompileError!Air.Inst.Ref { |
| 13307 | const pt = sema.pt; |
| 13308 | const zcu = pt.zcu; |
| 13309 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 13310 | const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); |
| 13311 | const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); |
| 13312 | const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); |
| 13313 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 13314 | const lhs = sema.resolveInst(extra.lhs); |
| 13315 | const rhs = sema.resolveInst(extra.rhs); |
| 13316 | const lhs_ty = sema.typeOf(lhs); |
| 13317 | const rhs_ty = sema.typeOf(rhs); |
| 13318 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 13319 | |
| 13320 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 13321 | const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } }); |
| 13322 | const scalar_type = resolved_type.scalarType(zcu); |
| 13323 | const scalar_tag = scalar_type.zigTypeTag(zcu); |
| 13324 | |
| 13325 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 13326 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 13327 | |
| 13328 | const is_int_or_bool = scalar_tag == .int or scalar_tag == .comptime_int or scalar_tag == .bool; |
| 13329 | |
| 13330 | if (!is_int_or_bool) { |
| 13331 | return sema.fail(block, src, "invalid operands to binary bitwise expression: '{s}' and '{s}'", .{ @tagName(lhs_ty.zigTypeTag(zcu)), @tagName(rhs_ty.zigTypeTag(zcu)) }); |
| 13332 | } |
| 13333 | |
| 13334 | const runtime_src = runtime: { |
| 13335 | // TODO: ask the linker what kind of relocations are available, and |
| 13336 | // in some cases emit a Value that means "this decl's address AND'd with this operand". |
| 13337 | if (sema.resolveValue(casted_lhs)) |lhs_val| { |
| 13338 | if (sema.resolveValue(casted_rhs)) |rhs_val| { |
| 13339 | const result_val = switch (air_tag) { |
| 13340 | // zig fmt: off |
| 13341 | .bit_and => try arith.bitwiseBin(sema, resolved_type, lhs_val, rhs_val, .@"and"), |
| 13342 | .bit_or => try arith.bitwiseBin(sema, resolved_type, lhs_val, rhs_val, .@"or"), |
| 13343 | .xor => try arith.bitwiseBin(sema, resolved_type, lhs_val, rhs_val, .xor), |
| 13344 | else => unreachable, |
| 13345 | // zig fmt: on |
| 13346 | }; |
| 13347 | return Air.internedToRef(result_val.toIntern()); |
| 13348 | } else { |
| 13349 | break :runtime rhs_src; |
| 13350 | } |
| 13351 | } else { |
| 13352 | break :runtime lhs_src; |
| 13353 | } |
| 13354 | }; |
| 13355 | |
| 13356 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 13357 | return block.addBinOp(air_tag, casted_lhs, casted_rhs); |
| 13358 | } |
| 13359 | |
| 13360 | fn zirBitNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 13361 | const pt = sema.pt; |
| 13362 | const zcu = pt.zcu; |
| 13363 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 13364 | const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); |
| 13365 | const src = block.nodeOffset(inst_data.src_node); |
| 13366 | const operand = sema.resolveInst(inst_data.operand); |
| 13367 | const operand_ty = sema.typeOf(operand); |
| 13368 | const scalar_ty = operand_ty.scalarType(zcu); |
| 13369 | const scalar_tag = scalar_ty.zigTypeTag(zcu); |
| 13370 | |
| 13371 | if (scalar_tag != .int and scalar_tag != .bool) |
| 13372 | return sema.fail(block, operand_src, "bitwise not operation on type '{f}'", .{operand_ty.fmt(pt)}); |
| 13373 | |
| 13374 | return analyzeBitNot(sema, block, operand, src); |
| 13375 | } |
| 13376 | |
| 13377 | fn analyzeBitNot( |
| 13378 | sema: *Sema, |
| 13379 | block: *Block, |
| 13380 | operand: Air.Inst.Ref, |
| 13381 | src: LazySrcLoc, |
| 13382 | ) CompileError!Air.Inst.Ref { |
| 13383 | const operand_ty = sema.typeOf(operand); |
| 13384 | if (sema.resolveValue(operand)) |operand_val| { |
| 13385 | const result_val = try arith.bitwiseNot(sema, operand_ty, operand_val); |
| 13386 | return Air.internedToRef(result_val.toIntern()); |
| 13387 | } |
| 13388 | try sema.requireRuntimeBlock(block, src, null); |
| 13389 | return block.addTyOp(.not, operand_ty, operand); |
| 13390 | } |
| 13391 | |
| 13392 | fn analyzeTupleCat( |
| 13393 | sema: *Sema, |
| 13394 | block: *Block, |
| 13395 | src_node: std.zig.Ast.Node.Offset, |
| 13396 | lhs: Air.Inst.Ref, |
| 13397 | rhs: Air.Inst.Ref, |
| 13398 | ) CompileError!Air.Inst.Ref { |
| 13399 | const pt = sema.pt; |
| 13400 | const zcu = pt.zcu; |
| 13401 | const comp = zcu.comp; |
| 13402 | const gpa = comp.gpa; |
| 13403 | const io = comp.io; |
| 13404 | |
| 13405 | const lhs_ty = sema.typeOf(lhs); |
| 13406 | const rhs_ty = sema.typeOf(rhs); |
| 13407 | const src = block.nodeOffset(src_node); |
| 13408 | |
| 13409 | const lhs_len = lhs_ty.structFieldCount(zcu); |
| 13410 | const rhs_len = rhs_ty.structFieldCount(zcu); |
| 13411 | const dest_fields = lhs_len + rhs_len; |
| 13412 | |
| 13413 | if (dest_fields == 0) { |
| 13414 | return .empty_tuple; |
| 13415 | } |
| 13416 | if (lhs_len == 0) { |
| 13417 | return rhs; |
| 13418 | } |
| 13419 | if (rhs_len == 0) { |
| 13420 | return lhs; |
| 13421 | } |
| 13422 | const final_len = try sema.usizeCast(block, src, dest_fields); |
| 13423 | |
| 13424 | const types = try sema.arena.alloc(InternPool.Index, final_len); |
| 13425 | const values = try sema.arena.alloc(InternPool.Index, final_len); |
| 13426 | |
| 13427 | const opt_runtime_src = rs: { |
| 13428 | var runtime_src: ?LazySrcLoc = null; |
| 13429 | var i: u32 = 0; |
| 13430 | while (i < lhs_len) : (i += 1) { |
| 13431 | types[i] = lhs_ty.fieldType(i, zcu).toIntern(); |
| 13432 | const operand_src = block.src(.{ .array_cat_lhs = .{ |
| 13433 | .array_cat_offset = src_node, |
| 13434 | .elem_index = i, |
| 13435 | } }); |
| 13436 | if (lhs_ty.structFieldDefaultValue(i, zcu)) |default_val| { |
| 13437 | values[i] = default_val.toIntern(); |
| 13438 | } else { |
| 13439 | runtime_src = operand_src; |
| 13440 | values[i] = .none; |
| 13441 | } |
| 13442 | } |
| 13443 | i = 0; |
| 13444 | while (i < rhs_len) : (i += 1) { |
| 13445 | types[i + lhs_len] = rhs_ty.fieldType(i, zcu).toIntern(); |
| 13446 | const operand_src = block.src(.{ .array_cat_rhs = .{ |
| 13447 | .array_cat_offset = src_node, |
| 13448 | .elem_index = i, |
| 13449 | } }); |
| 13450 | if (rhs_ty.structFieldDefaultValue(i, zcu)) |default_val| { |
| 13451 | values[i + lhs_len] = default_val.toIntern(); |
| 13452 | } else { |
| 13453 | runtime_src = operand_src; |
| 13454 | values[i + lhs_len] = .none; |
| 13455 | } |
| 13456 | } |
| 13457 | break :rs runtime_src; |
| 13458 | }; |
| 13459 | |
| 13460 | const tuple_ty: Type = .fromInterned(try zcu.intern_pool.getTupleType(gpa, io, pt.tid, .{ |
| 13461 | .types = types, |
| 13462 | .values = values, |
| 13463 | })); |
| 13464 | |
| 13465 | const runtime_src = opt_runtime_src orelse { |
| 13466 | const tuple_val = try pt.aggregateValue(tuple_ty, values); |
| 13467 | return Air.internedToRef(tuple_val.toIntern()); |
| 13468 | }; |
| 13469 | |
| 13470 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 13471 | |
| 13472 | const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len); |
| 13473 | var i: u32 = 0; |
| 13474 | while (i < lhs_len) : (i += 1) { |
| 13475 | element_refs[i] = try sema.tupleFieldValByIndex(block, lhs, i, lhs_ty); |
| 13476 | } |
| 13477 | i = 0; |
| 13478 | while (i < rhs_len) : (i += 1) { |
| 13479 | element_refs[i + lhs_len] = |
| 13480 | try sema.tupleFieldValByIndex(block, rhs, i, rhs_ty); |
| 13481 | } |
| 13482 | |
| 13483 | return block.addAggregateInit(tuple_ty, element_refs); |
| 13484 | } |
| 13485 | |
| 13486 | fn zirArrayCat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 13487 | const pt = sema.pt; |
| 13488 | const zcu = pt.zcu; |
| 13489 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 13490 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 13491 | const lhs = sema.resolveInst(extra.lhs); |
| 13492 | const rhs = sema.resolveInst(extra.rhs); |
| 13493 | const lhs_ty = sema.typeOf(lhs); |
| 13494 | const rhs_ty = sema.typeOf(rhs); |
| 13495 | const src = block.nodeOffset(inst_data.src_node); |
| 13496 | |
| 13497 | const lhs_is_tuple = lhs_ty.isTuple(zcu); |
| 13498 | const rhs_is_tuple = rhs_ty.isTuple(zcu); |
| 13499 | if (lhs_is_tuple and rhs_is_tuple) { |
| 13500 | return sema.analyzeTupleCat(block, inst_data.src_node, lhs, rhs); |
| 13501 | } |
| 13502 | |
| 13503 | const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); |
| 13504 | const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); |
| 13505 | |
| 13506 | const lhs_info = try sema.getArrayCatInfo(block, lhs_src, lhs, rhs_ty) orelse lhs_info: { |
| 13507 | if (lhs_is_tuple) break :lhs_info undefined; |
| 13508 | return sema.fail(block, lhs_src, "expected indexable; found '{f}'", .{lhs_ty.fmt(pt)}); |
| 13509 | }; |
| 13510 | const rhs_info = try sema.getArrayCatInfo(block, rhs_src, rhs, lhs_ty) orelse { |
| 13511 | assert(!rhs_is_tuple); |
| 13512 | return sema.fail(block, rhs_src, "expected indexable; found '{f}'", .{rhs_ty.fmt(pt)}); |
| 13513 | }; |
| 13514 | |
| 13515 | const resolved_elem_ty = t: { |
| 13516 | var trash_block = block.makeSubBlock(); |
| 13517 | trash_block.comptime_reason = null; |
| 13518 | defer trash_block.instructions.deinit(sema.gpa); |
| 13519 | |
| 13520 | const instructions = [_]Air.Inst.Ref{ |
| 13521 | try trash_block.addTyOp(.bit_cast, lhs_info.elem_type, .void_value), |
| 13522 | try trash_block.addTyOp(.bit_cast, rhs_info.elem_type, .void_value), |
| 13523 | }; |
| 13524 | break :t try sema.resolvePeerTypes(block, src, &instructions, .{ |
| 13525 | .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, |
| 13526 | }); |
| 13527 | }; |
| 13528 | |
| 13529 | // When there is a sentinel mismatch, no sentinel on the result. |
| 13530 | // Otherwise, use the sentinel value provided by either operand, |
| 13531 | // coercing it to the peer-resolved element type. |
| 13532 | const res_sent_val: ?Value = s: { |
| 13533 | if (lhs_info.sentinel) |lhs_sent_val| { |
| 13534 | const lhs_sent = Air.internedToRef(lhs_sent_val.toIntern()); |
| 13535 | if (rhs_info.sentinel) |rhs_sent_val| { |
| 13536 | const rhs_sent = Air.internedToRef(rhs_sent_val.toIntern()); |
| 13537 | const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src); |
| 13538 | const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src); |
| 13539 | const lhs_sent_casted_val = (try sema.resolveDefinedValue(block, lhs_src, lhs_sent_casted)).?; |
| 13540 | const rhs_sent_casted_val = (try sema.resolveDefinedValue(block, rhs_src, rhs_sent_casted)).?; |
| 13541 | if (try sema.valuesEqual(lhs_sent_casted_val, rhs_sent_casted_val, resolved_elem_ty)) { |
| 13542 | break :s lhs_sent_casted_val; |
| 13543 | } else { |
| 13544 | break :s null; |
| 13545 | } |
| 13546 | } else { |
| 13547 | const lhs_sent_casted = try sema.coerce(block, resolved_elem_ty, lhs_sent, lhs_src); |
| 13548 | const lhs_sent_casted_val = (try sema.resolveDefinedValue(block, lhs_src, lhs_sent_casted)).?; |
| 13549 | break :s lhs_sent_casted_val; |
| 13550 | } |
| 13551 | } else { |
| 13552 | if (rhs_info.sentinel) |rhs_sent_val| { |
| 13553 | const rhs_sent = Air.internedToRef(rhs_sent_val.toIntern()); |
| 13554 | const rhs_sent_casted = try sema.coerce(block, resolved_elem_ty, rhs_sent, rhs_src); |
| 13555 | const rhs_sent_casted_val = (try sema.resolveDefinedValue(block, rhs_src, rhs_sent_casted)).?; |
| 13556 | break :s rhs_sent_casted_val; |
| 13557 | } else { |
| 13558 | break :s null; |
| 13559 | } |
| 13560 | } |
| 13561 | }; |
| 13562 | |
| 13563 | const lhs_len = try sema.usizeCast(block, lhs_src, lhs_info.len); |
| 13564 | const rhs_len = try sema.usizeCast(block, rhs_src, rhs_info.len); |
| 13565 | const result_len = std.math.add(usize, lhs_len, rhs_len) catch |err| switch (err) { |
| 13566 | error.Overflow => return sema.fail( |
| 13567 | block, |
| 13568 | src, |
| 13569 | "concatenating arrays of length {d} and {d} produces an array too large for this compiler implementation to handle", |
| 13570 | .{ lhs_len, rhs_len }, |
| 13571 | ), |
| 13572 | }; |
| 13573 | |
| 13574 | const result_ty = try pt.arrayType(.{ |
| 13575 | .len = result_len, |
| 13576 | .sentinel = if (res_sent_val) |v| v.toIntern() else .none, |
| 13577 | .child = resolved_elem_ty.toIntern(), |
| 13578 | }); |
| 13579 | const ptr_addrspace = p: { |
| 13580 | if (lhs_ty.zigTypeTag(zcu) == .pointer) break :p lhs_ty.ptrAddressSpace(zcu); |
| 13581 | if (rhs_ty.zigTypeTag(zcu) == .pointer) break :p rhs_ty.ptrAddressSpace(zcu); |
| 13582 | break :p null; |
| 13583 | }; |
| 13584 | |
| 13585 | const runtime_src = if (switch (lhs_ty.zigTypeTag(zcu)) { |
| 13586 | .array, .@"struct" => sema.resolveValue(lhs), |
| 13587 | .pointer => try sema.resolveDefinedValue(block, lhs_src, lhs), |
| 13588 | else => unreachable, |
| 13589 | }) |lhs_val| rs: { |
| 13590 | if (switch (rhs_ty.zigTypeTag(zcu)) { |
| 13591 | .array, .@"struct" => sema.resolveValue(rhs), |
| 13592 | .pointer => try sema.resolveDefinedValue(block, rhs_src, rhs), |
| 13593 | else => unreachable, |
| 13594 | }) |rhs_val| { |
| 13595 | const lhs_sub_val = if (lhs_ty.isSinglePointer(zcu)) |
| 13596 | try sema.pointerDeref(block, lhs_src, lhs_val, lhs_ty) orelse break :rs lhs_src |
| 13597 | else if (lhs_ty.isSlice(zcu)) |
| 13598 | try sema.maybeDerefSliceAsArray(block, lhs_src, lhs_val) orelse break :rs lhs_src |
| 13599 | else |
| 13600 | lhs_val; |
| 13601 | |
| 13602 | const rhs_sub_val = if (rhs_ty.isSinglePointer(zcu)) |
| 13603 | try sema.pointerDeref(block, rhs_src, rhs_val, rhs_ty) orelse break :rs rhs_src |
| 13604 | else if (rhs_ty.isSlice(zcu)) |
| 13605 | try sema.maybeDerefSliceAsArray(block, rhs_src, rhs_val) orelse break :rs rhs_src |
| 13606 | else |
| 13607 | rhs_val; |
| 13608 | |
| 13609 | const element_vals = try sema.arena.alloc(InternPool.Index, result_len); |
| 13610 | var elem_i: u32 = 0; |
| 13611 | while (elem_i < lhs_len) : (elem_i += 1) { |
| 13612 | const lhs_elem_i = elem_i; |
| 13613 | const elem_default_val: ?Value = if (lhs_is_tuple) lhs_ty.structFieldDefaultValue(lhs_elem_i, zcu) else null; |
| 13614 | const elem_val = elem_default_val orelse |
| 13615 | if (lhs_sub_val.isUndef(zcu)) try pt.undefValue(resolved_elem_ty) else try lhs_sub_val.elemValue(pt, lhs_elem_i); |
| 13616 | const operand_src = block.src(.{ .array_cat_lhs = .{ |
| 13617 | .array_cat_offset = inst_data.src_node, |
| 13618 | .elem_index = elem_i, |
| 13619 | } }); |
| 13620 | const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, .fromValue(elem_val), operand_src); |
| 13621 | const coerced_elem_val = sema.resolveValue(coerced_elem_val_inst).?; |
| 13622 | element_vals[elem_i] = coerced_elem_val.toIntern(); |
| 13623 | } |
| 13624 | while (elem_i < result_len) : (elem_i += 1) { |
| 13625 | const rhs_elem_i = elem_i - lhs_len; |
| 13626 | const elem_default_val: ?Value = if (rhs_is_tuple) rhs_ty.structFieldDefaultValue(rhs_elem_i, zcu) else null; |
| 13627 | const elem_val = elem_default_val orelse |
| 13628 | if (rhs_sub_val.isUndef(zcu)) try pt.undefValue(resolved_elem_ty) else try rhs_sub_val.elemValue(pt, rhs_elem_i); |
| 13629 | const operand_src = block.src(.{ .array_cat_rhs = .{ |
| 13630 | .array_cat_offset = inst_data.src_node, |
| 13631 | .elem_index = @intCast(rhs_elem_i), |
| 13632 | } }); |
| 13633 | const coerced_elem_val_inst = try sema.coerce(block, resolved_elem_ty, .fromValue(elem_val), operand_src); |
| 13634 | const coerced_elem_val = sema.resolveValue(coerced_elem_val_inst).?; |
| 13635 | element_vals[elem_i] = coerced_elem_val.toIntern(); |
| 13636 | } |
| 13637 | return sema.addConstantMaybeRef( |
| 13638 | try pt.aggregateValue(result_ty, element_vals), |
| 13639 | ptr_addrspace != null, |
| 13640 | ); |
| 13641 | } else break :rs rhs_src; |
| 13642 | } else lhs_src; |
| 13643 | |
| 13644 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 13645 | |
| 13646 | if (ptr_addrspace) |ptr_as| { |
| 13647 | const constant_alloc_ty = try pt.ptrType(.{ |
| 13648 | .child = result_ty.toIntern(), |
| 13649 | .flags = .{ |
| 13650 | .address_space = ptr_as, |
| 13651 | .is_const = true, |
| 13652 | }, |
| 13653 | }); |
| 13654 | const alloc_ty = try pt.ptrType(.{ |
| 13655 | .child = result_ty.toIntern(), |
| 13656 | .flags = .{ .address_space = ptr_as }, |
| 13657 | }); |
| 13658 | const elem_ptr_ty = try pt.ptrType(.{ |
| 13659 | .child = resolved_elem_ty.toIntern(), |
| 13660 | .flags = .{ .address_space = ptr_as }, |
| 13661 | }); |
| 13662 | |
| 13663 | const mutable_alloc = try block.addTy(.alloc, alloc_ty); |
| 13664 | |
| 13665 | // if both the source and destination are arrays |
| 13666 | // we can hotpath via a memcpy. |
| 13667 | if (lhs_ty.zigTypeTag(zcu) == .pointer and |
| 13668 | rhs_ty.zigTypeTag(zcu) == .pointer) |
| 13669 | { |
| 13670 | const slice_ty = try pt.ptrType(.{ |
| 13671 | .child = resolved_elem_ty.toIntern(), |
| 13672 | .flags = .{ |
| 13673 | .size = .slice, |
| 13674 | .address_space = ptr_as, |
| 13675 | }, |
| 13676 | }); |
| 13677 | |
| 13678 | const many_ty = slice_ty.slicePtrFieldType(zcu); |
| 13679 | const many_alloc = try block.addTyOp(.ptr_cast, many_ty, mutable_alloc); |
| 13680 | |
| 13681 | // lhs_dest_slice = dest[0..lhs.len] |
| 13682 | if (lhs_len > 0) { |
| 13683 | const lhs_dest_slice = try block.addInst(.{ |
| 13684 | .tag = .slice, |
| 13685 | .data = .{ .ty_pl = .{ |
| 13686 | .ty = slice_ty, |
| 13687 | .payload = try sema.addExtra(Air.Bin{ |
| 13688 | .lhs = many_alloc, |
| 13689 | .rhs = try pt.intRef(.usize, lhs_len), |
| 13690 | }), |
| 13691 | } }, |
| 13692 | }); |
| 13693 | _ = try block.addBinOp(.memcpy, lhs_dest_slice, lhs); |
| 13694 | } |
| 13695 | |
| 13696 | // rhs_dest_slice = dest[lhs.len..][0..rhs.len] |
| 13697 | if (rhs_len > 0) { |
| 13698 | const rhs_dest_offset = try block.addInst(.{ |
| 13699 | .tag = .ptr_add, |
| 13700 | .data = .{ .ty_pl = .{ |
| 13701 | .ty = many_ty, |
| 13702 | .payload = try sema.addExtra(Air.Bin{ |
| 13703 | .lhs = many_alloc, |
| 13704 | .rhs = try pt.intRef(.usize, lhs_len), |
| 13705 | }), |
| 13706 | } }, |
| 13707 | }); |
| 13708 | const rhs_dest_slice = try block.addInst(.{ |
| 13709 | .tag = .slice, |
| 13710 | .data = .{ .ty_pl = .{ |
| 13711 | .ty = slice_ty, |
| 13712 | .payload = try sema.addExtra(Air.Bin{ |
| 13713 | .lhs = rhs_dest_offset, |
| 13714 | .rhs = try pt.intRef(.usize, rhs_len), |
| 13715 | }), |
| 13716 | } }, |
| 13717 | }); |
| 13718 | _ = try block.addBinOp(.memcpy, rhs_dest_slice, rhs); |
| 13719 | } |
| 13720 | |
| 13721 | if (res_sent_val) |sent_val| { |
| 13722 | const elem_index = try pt.intRef(.usize, result_len); |
| 13723 | const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty); |
| 13724 | const init = Air.internedToRef((try pt.getCoerced(sent_val, lhs_info.elem_type)).toIntern()); |
| 13725 | try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); |
| 13726 | } |
| 13727 | |
| 13728 | return block.addTyOp(.ptr_cast, constant_alloc_ty, mutable_alloc); |
| 13729 | } |
| 13730 | |
| 13731 | var elem_i: u32 = 0; |
| 13732 | while (elem_i < lhs_len) : (elem_i += 1) { |
| 13733 | const elem_index = try pt.intRef(.usize, elem_i); |
| 13734 | const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty); |
| 13735 | const operand_src = block.src(.{ .array_cat_lhs = .{ |
| 13736 | .array_cat_offset = inst_data.src_node, |
| 13737 | .elem_index = elem_i, |
| 13738 | } }); |
| 13739 | const init = try sema.elemVal(block, operand_src, lhs, elem_index, src, true); |
| 13740 | try sema.storePtr2(block, src, elem_ptr, src, init, operand_src, .store); |
| 13741 | } |
| 13742 | while (elem_i < result_len) : (elem_i += 1) { |
| 13743 | const rhs_elem_i = elem_i - lhs_len; |
| 13744 | const elem_index = try pt.intRef(.usize, elem_i); |
| 13745 | const rhs_index = try pt.intRef(.usize, rhs_elem_i); |
| 13746 | const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty); |
| 13747 | const operand_src = block.src(.{ .array_cat_rhs = .{ |
| 13748 | .array_cat_offset = inst_data.src_node, |
| 13749 | .elem_index = @intCast(rhs_elem_i), |
| 13750 | } }); |
| 13751 | const init = try sema.elemVal(block, operand_src, rhs, rhs_index, src, true); |
| 13752 | try sema.storePtr2(block, src, elem_ptr, src, init, operand_src, .store); |
| 13753 | } |
| 13754 | if (res_sent_val) |sent_val| { |
| 13755 | const elem_index = try pt.intRef(.usize, result_len); |
| 13756 | const elem_ptr = try block.addPtrElemPtr(mutable_alloc, elem_index, elem_ptr_ty); |
| 13757 | const init = Air.internedToRef((try pt.getCoerced(sent_val, lhs_info.elem_type)).toIntern()); |
| 13758 | try sema.storePtr2(block, src, elem_ptr, src, init, lhs_src, .store); |
| 13759 | } |
| 13760 | |
| 13761 | return block.addTyOp(.ptr_cast, constant_alloc_ty, mutable_alloc); |
| 13762 | } |
| 13763 | |
| 13764 | const element_refs = try sema.arena.alloc(Air.Inst.Ref, result_len); |
| 13765 | { |
| 13766 | var elem_i: u32 = 0; |
| 13767 | while (elem_i < lhs_len) : (elem_i += 1) { |
| 13768 | const index = try pt.intRef(.usize, elem_i); |
| 13769 | const operand_src = block.src(.{ .array_cat_lhs = .{ |
| 13770 | .array_cat_offset = inst_data.src_node, |
| 13771 | .elem_index = elem_i, |
| 13772 | } }); |
| 13773 | const init = try sema.elemVal(block, operand_src, lhs, index, src, true); |
| 13774 | element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, operand_src); |
| 13775 | } |
| 13776 | while (elem_i < result_len) : (elem_i += 1) { |
| 13777 | const rhs_elem_i = elem_i - lhs_len; |
| 13778 | const index = try pt.intRef(.usize, rhs_elem_i); |
| 13779 | const operand_src = block.src(.{ .array_cat_rhs = .{ |
| 13780 | .array_cat_offset = inst_data.src_node, |
| 13781 | .elem_index = @intCast(rhs_elem_i), |
| 13782 | } }); |
| 13783 | const init = try sema.elemVal(block, operand_src, rhs, index, src, true); |
| 13784 | element_refs[elem_i] = try sema.coerce(block, resolved_elem_ty, init, operand_src); |
| 13785 | } |
| 13786 | } |
| 13787 | |
| 13788 | return block.addAggregateInit(result_ty, element_refs); |
| 13789 | } |
| 13790 | |
| 13791 | fn getArrayCatInfo(sema: *Sema, block: *Block, src: LazySrcLoc, operand: Air.Inst.Ref, peer_ty: Type) !?Type.ArrayInfo { |
| 13792 | const pt = sema.pt; |
| 13793 | const zcu = pt.zcu; |
| 13794 | const operand_ty = sema.typeOf(operand); |
| 13795 | switch (operand_ty.zigTypeTag(zcu)) { |
| 13796 | .array => return operand_ty.arrayInfo(zcu), |
| 13797 | .pointer => { |
| 13798 | const ptr_info = operand_ty.ptrInfo(zcu); |
| 13799 | switch (ptr_info.flags.size) { |
| 13800 | .slice => { |
| 13801 | const val = try sema.resolveConstDefinedValue(block, src, operand, .{ .simple = .slice_cat_operand }); |
| 13802 | return .{ |
| 13803 | .elem_type = .fromInterned(ptr_info.child), |
| 13804 | .sentinel = switch (ptr_info.sentinel) { |
| 13805 | .none => null, |
| 13806 | else => Value.fromInterned(ptr_info.sentinel), |
| 13807 | }, |
| 13808 | .len = val.sliceLen(zcu), |
| 13809 | }; |
| 13810 | }, |
| 13811 | .one => { |
| 13812 | if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .array) { |
| 13813 | return Type.fromInterned(ptr_info.child).arrayInfo(zcu); |
| 13814 | } |
| 13815 | }, |
| 13816 | .c, .many => {}, |
| 13817 | } |
| 13818 | }, |
| 13819 | .@"struct" => { |
| 13820 | if (operand_ty.isTuple(zcu) and peer_ty.isIndexable(zcu)) { |
| 13821 | assert(!peer_ty.isTuple(zcu)); |
| 13822 | const peer_elem_ty = switch (peer_ty.zigTypeTag(zcu)) { |
| 13823 | .pointer => switch (peer_ty.ptrSize(zcu)) { |
| 13824 | .one => switch (peer_ty.childType(zcu).zigTypeTag(zcu)) { |
| 13825 | .array, .vector => peer_ty.childType(zcu).childType(zcu), |
| 13826 | .@"struct" => return null, |
| 13827 | else => unreachable, |
| 13828 | }, |
| 13829 | .many, .c, .slice => peer_ty.childType(zcu), |
| 13830 | }, |
| 13831 | .vector, .array => peer_ty.childType(zcu), |
| 13832 | else => unreachable, |
| 13833 | }; |
| 13834 | return .{ |
| 13835 | .elem_type = peer_elem_ty, |
| 13836 | .sentinel = null, |
| 13837 | .len = operand_ty.arrayLen(zcu), |
| 13838 | }; |
| 13839 | } |
| 13840 | }, |
| 13841 | else => {}, |
| 13842 | } |
| 13843 | return null; |
| 13844 | } |
| 13845 | |
| 13846 | fn analyzeTupleMul( |
| 13847 | sema: *Sema, |
| 13848 | block: *Block, |
| 13849 | src_node: std.zig.Ast.Node.Offset, |
| 13850 | operand: Air.Inst.Ref, |
| 13851 | factor: usize, |
| 13852 | ) CompileError!Air.Inst.Ref { |
| 13853 | const pt = sema.pt; |
| 13854 | const zcu = pt.zcu; |
| 13855 | const comp = zcu.comp; |
| 13856 | const gpa = comp.gpa; |
| 13857 | const io = comp.io; |
| 13858 | |
| 13859 | const operand_ty = sema.typeOf(operand); |
| 13860 | const src = block.nodeOffset(src_node); |
| 13861 | const len_src = block.src(.{ .node_offset_bin_rhs = src_node }); |
| 13862 | |
| 13863 | const tuple_len = operand_ty.structFieldCount(zcu); |
| 13864 | const final_len = std.math.mul(usize, tuple_len, factor) catch |
| 13865 | return sema.fail(block, len_src, "operation results in overflow", .{}); |
| 13866 | |
| 13867 | if (final_len == 0) { |
| 13868 | return .empty_tuple; |
| 13869 | } |
| 13870 | const types = try sema.arena.alloc(InternPool.Index, final_len); |
| 13871 | const values = try sema.arena.alloc(InternPool.Index, final_len); |
| 13872 | |
| 13873 | const opt_runtime_src = rs: { |
| 13874 | var runtime_src: ?LazySrcLoc = null; |
| 13875 | for (0..tuple_len) |i| { |
| 13876 | types[i] = operand_ty.fieldType(i, zcu).toIntern(); |
| 13877 | const operand_src = block.src(.{ .array_cat_lhs = .{ |
| 13878 | .array_cat_offset = src_node, |
| 13879 | .elem_index = @intCast(i), |
| 13880 | } }); |
| 13881 | if (operand_ty.structFieldDefaultValue(i, zcu)) |default_val| { |
| 13882 | values[i] = default_val.toIntern(); |
| 13883 | } else { |
| 13884 | runtime_src = operand_src; |
| 13885 | values[i] = .none; // TODO don't treat unreachable_value as special |
| 13886 | } |
| 13887 | } |
| 13888 | for (0..factor) |i| { |
| 13889 | @memmove(types[tuple_len * i ..][0..tuple_len], types[0..tuple_len]); |
| 13890 | @memmove(values[tuple_len * i ..][0..tuple_len], values[0..tuple_len]); |
| 13891 | } |
| 13892 | break :rs runtime_src; |
| 13893 | }; |
| 13894 | |
| 13895 | const tuple_ty: Type = .fromInterned(try zcu.intern_pool.getTupleType(gpa, io, pt.tid, .{ |
| 13896 | .types = types, |
| 13897 | .values = values, |
| 13898 | })); |
| 13899 | |
| 13900 | const runtime_src = opt_runtime_src orelse { |
| 13901 | const tuple_val = try pt.aggregateValue(tuple_ty, values); |
| 13902 | return Air.internedToRef(tuple_val.toIntern()); |
| 13903 | }; |
| 13904 | |
| 13905 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 13906 | |
| 13907 | const element_refs = try sema.arena.alloc(Air.Inst.Ref, final_len); |
| 13908 | var i: u32 = 0; |
| 13909 | while (i < tuple_len) : (i += 1) { |
| 13910 | element_refs[i] = try sema.tupleFieldValByIndex(block, operand, @intCast(i), operand_ty); |
| 13911 | } |
| 13912 | i = 1; |
| 13913 | while (i < factor) : (i += 1) { |
| 13914 | @memcpy(element_refs[tuple_len * i ..][0..tuple_len], element_refs[0..tuple_len]); |
| 13915 | } |
| 13916 | |
| 13917 | return block.addAggregateInit(tuple_ty, element_refs); |
| 13918 | } |
| 13919 | |
| 13920 | fn zirNegate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 13921 | const pt = sema.pt; |
| 13922 | const zcu = pt.zcu; |
| 13923 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 13924 | const src = block.nodeOffset(inst_data.src_node); |
| 13925 | const lhs_src = src; |
| 13926 | const rhs_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); |
| 13927 | |
| 13928 | const rhs = sema.resolveInst(inst_data.operand); |
| 13929 | const rhs_ty = sema.typeOf(rhs); |
| 13930 | const rhs_scalar_ty = rhs_ty.scalarType(zcu); |
| 13931 | |
| 13932 | if (rhs_scalar_ty.isUnsignedInt(zcu) or switch (rhs_scalar_ty.zigTypeTag(zcu)) { |
| 13933 | .int, .comptime_int, .float, .comptime_float => false, |
| 13934 | else => true, |
| 13935 | }) { |
| 13936 | return sema.fail(block, src, "negation of type '{f}'", .{rhs_ty.fmt(pt)}); |
| 13937 | } |
| 13938 | |
| 13939 | if (rhs_scalar_ty.isAnyFloat()) { |
| 13940 | // We handle float negation here to ensure negative zero is represented in the bits. |
| 13941 | if (sema.resolveValue(rhs)) |rhs_val| { |
| 13942 | const result = try arith.negateFloat(sema, rhs_ty, rhs_val); |
| 13943 | return Air.internedToRef(result.toIntern()); |
| 13944 | } |
| 13945 | try sema.requireRuntimeBlock(block, src, null); |
| 13946 | return block.addUnOp(if (block.float_mode == .optimized) .neg_optimized else .neg, rhs); |
| 13947 | } |
| 13948 | |
| 13949 | const lhs = Air.internedToRef((try sema.splat(rhs_ty, try pt.intValue(rhs_scalar_ty, 0))).toIntern()); |
| 13950 | return sema.analyzeArithmetic(block, .sub, lhs, rhs, src, lhs_src, rhs_src, true); |
| 13951 | } |
| 13952 | |
| 13953 | fn zirNegateWrap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 13954 | const pt = sema.pt; |
| 13955 | const zcu = pt.zcu; |
| 13956 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 13957 | const src = block.nodeOffset(inst_data.src_node); |
| 13958 | const lhs_src = src; |
| 13959 | const rhs_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); |
| 13960 | |
| 13961 | const rhs = sema.resolveInst(inst_data.operand); |
| 13962 | const rhs_ty = sema.typeOf(rhs); |
| 13963 | const rhs_scalar_ty = rhs_ty.scalarType(zcu); |
| 13964 | |
| 13965 | switch (rhs_scalar_ty.zigTypeTag(zcu)) { |
| 13966 | .int, .comptime_int, .float, .comptime_float => {}, |
| 13967 | else => return sema.fail(block, src, "negation of type '{f}'", .{rhs_ty.fmt(pt)}), |
| 13968 | } |
| 13969 | |
| 13970 | const lhs = Air.internedToRef((try sema.splat(rhs_ty, try pt.intValue(rhs_scalar_ty, 0))).toIntern()); |
| 13971 | return sema.analyzeArithmetic(block, .subwrap, lhs, rhs, src, lhs_src, rhs_src, true); |
| 13972 | } |
| 13973 | |
| 13974 | fn zirArithmetic( |
| 13975 | sema: *Sema, |
| 13976 | block: *Block, |
| 13977 | inst: Zir.Inst.Index, |
| 13978 | zir_tag: Zir.Inst.Tag, |
| 13979 | safety: bool, |
| 13980 | ) CompileError!Air.Inst.Ref { |
| 13981 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 13982 | const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); |
| 13983 | const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); |
| 13984 | const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); |
| 13985 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 13986 | const lhs = sema.resolveInst(extra.lhs); |
| 13987 | const rhs = sema.resolveInst(extra.rhs); |
| 13988 | |
| 13989 | return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, src, lhs_src, rhs_src, safety); |
| 13990 | } |
| 13991 | |
| 13992 | fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 13993 | const pt = sema.pt; |
| 13994 | const zcu = pt.zcu; |
| 13995 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 13996 | const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); |
| 13997 | const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); |
| 13998 | const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); |
| 13999 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 14000 | const lhs = sema.resolveInst(extra.lhs); |
| 14001 | const rhs = sema.resolveInst(extra.rhs); |
| 14002 | const lhs_ty = sema.typeOf(lhs); |
| 14003 | const rhs_ty = sema.typeOf(rhs); |
| 14004 | const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); |
| 14005 | const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); |
| 14006 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 14007 | try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); |
| 14008 | |
| 14009 | const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ |
| 14010 | .override = &.{ lhs_src, rhs_src }, |
| 14011 | }); |
| 14012 | |
| 14013 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 14014 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 14015 | |
| 14016 | const lhs_scalar_ty = lhs_ty.scalarType(zcu); |
| 14017 | const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); |
| 14018 | |
| 14019 | const is_int = scalar_tag == .int or scalar_tag == .comptime_int; |
| 14020 | |
| 14021 | try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div); |
| 14022 | |
| 14023 | const maybe_lhs_val = sema.resolveValue(casted_lhs); |
| 14024 | const maybe_rhs_val = sema.resolveValue(casted_rhs); |
| 14025 | |
| 14026 | if ((lhs_ty.zigTypeTag(zcu) == .comptime_float and rhs_ty.zigTypeTag(zcu) == .comptime_int) or |
| 14027 | (lhs_ty.zigTypeTag(zcu) == .comptime_int and rhs_ty.zigTypeTag(zcu) == .comptime_float)) |
| 14028 | { |
| 14029 | // If it makes a difference whether we coerce to ints or floats before doing the division, error. |
| 14030 | // If lhs % rhs is 0, it doesn't matter. |
| 14031 | const lhs_val = maybe_lhs_val orelse unreachable; |
| 14032 | const rhs_val = maybe_rhs_val orelse unreachable; |
| 14033 | const rem = arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .rem) catch unreachable; |
| 14034 | if (!rem.compareAllWithZero(.eq, zcu)) { |
| 14035 | return sema.fail( |
| 14036 | block, |
| 14037 | src, |
| 14038 | "ambiguous coercion of division operands '{f}' and '{f}'; non-zero remainder '{f}'", |
| 14039 | .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt), rem.fmtValueSema(pt, sema) }, |
| 14040 | ); |
| 14041 | } |
| 14042 | } |
| 14043 | |
| 14044 | // TODO: emit compile error when .div is used on integers and there would be an |
| 14045 | // ambiguous result between div_floor and div_trunc. |
| 14046 | |
| 14047 | // The rules here are like those in `analyzeArithmetic`: |
| 14048 | // |
| 14049 | // * If both operands are comptime-known, call the corresponding function in `arith`. |
| 14050 | // Inputs which would be IB at runtime are compile errors. |
| 14051 | // |
| 14052 | // * Otherwise, if one operand is comptime-known `undefined`, we either trigger a compile error |
| 14053 | // or return `undefined`, depending on whether this operator can trigger IB. |
| 14054 | // |
| 14055 | // * No other comptime operand determines a comptime result, so remaining cases are runtime ops. |
| 14056 | |
| 14057 | const allow_div_zero = !is_int and |
| 14058 | resolved_type.toIntern() != .comptime_float_type and |
| 14059 | block.float_mode == .strict; |
| 14060 | |
| 14061 | if (maybe_lhs_val) |lhs_val| { |
| 14062 | if (maybe_rhs_val) |rhs_val| { |
| 14063 | return .fromValue(try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div)); |
| 14064 | } |
| 14065 | if (allow_div_zero) { |
| 14066 | if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); |
| 14067 | } else { |
| 14068 | try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); |
| 14069 | } |
| 14070 | } else if (maybe_rhs_val) |rhs_val| { |
| 14071 | if (allow_div_zero) { |
| 14072 | if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); |
| 14073 | } else { |
| 14074 | try sema.checkAllScalarsDefined(block, rhs_src, rhs_val); |
| 14075 | if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); |
| 14076 | } |
| 14077 | } |
| 14078 | |
| 14079 | if (block.wantSafety()) { |
| 14080 | try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); |
| 14081 | try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); |
| 14082 | } |
| 14083 | |
| 14084 | const air_tag: Air.Inst.Tag = if (is_int) blk: { |
| 14085 | if (lhs_ty.isSignedInt(zcu) or rhs_ty.isSignedInt(zcu)) { |
| 14086 | return sema.fail( |
| 14087 | block, |
| 14088 | src, |
| 14089 | "division with '{f}' and '{f}': signed integers must use @divTrunc, @divFloor, @divCeil, or @divExact", |
| 14090 | .{ lhs_ty.fmt(pt), rhs_ty.fmt(pt) }, |
| 14091 | ); |
| 14092 | } |
| 14093 | break :blk .div_trunc; |
| 14094 | } else switch (block.float_mode) { |
| 14095 | .optimized => .div_float_optimized, |
| 14096 | .strict => .div_float, |
| 14097 | }; |
| 14098 | return block.addBinOp(air_tag, casted_lhs, casted_rhs); |
| 14099 | } |
| 14100 | |
| 14101 | fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 14102 | const pt = sema.pt; |
| 14103 | const zcu = pt.zcu; |
| 14104 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 14105 | const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); |
| 14106 | const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 14107 | const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 14108 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 14109 | const lhs = sema.resolveInst(extra.lhs); |
| 14110 | const rhs = sema.resolveInst(extra.rhs); |
| 14111 | const lhs_ty = sema.typeOf(lhs); |
| 14112 | const rhs_ty = sema.typeOf(rhs); |
| 14113 | const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); |
| 14114 | const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); |
| 14115 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 14116 | try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); |
| 14117 | |
| 14118 | const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ |
| 14119 | .override = &.{ lhs_src, rhs_src }, |
| 14120 | }); |
| 14121 | |
| 14122 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 14123 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 14124 | |
| 14125 | const lhs_scalar_ty = lhs_ty.scalarType(zcu); |
| 14126 | const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); |
| 14127 | |
| 14128 | const is_int = scalar_tag == .int or scalar_tag == .comptime_int; |
| 14129 | |
| 14130 | try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_exact); |
| 14131 | |
| 14132 | const maybe_lhs_val = sema.resolveValue(casted_lhs); |
| 14133 | const maybe_rhs_val = sema.resolveValue(casted_rhs); |
| 14134 | |
| 14135 | // Because `@divExact` can trigger Illegal Behavior, undefined operands trigger Illegal Behavior. |
| 14136 | |
| 14137 | if (maybe_lhs_val) |lhs_val| { |
| 14138 | if (maybe_rhs_val) |rhs_val| { |
| 14139 | const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_exact); |
| 14140 | return Air.internedToRef(result.toIntern()); |
| 14141 | } |
| 14142 | try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); |
| 14143 | } else if (maybe_rhs_val) |rhs_val| { |
| 14144 | try sema.checkAllScalarsDefined(block, rhs_src, rhs_val); |
| 14145 | if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); |
| 14146 | } |
| 14147 | |
| 14148 | // Depending on whether safety is enabled, we will have a slightly different strategy |
| 14149 | // here. The `div_exact` AIR instruction causes illegal behavior if a remainder |
| 14150 | // is produced, so in the safety check case, it cannot be used. Instead we do a |
| 14151 | // div_trunc and check for remainder. |
| 14152 | |
| 14153 | if (block.wantSafety()) { |
| 14154 | try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); |
| 14155 | try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); |
| 14156 | |
| 14157 | const result = try block.addBinOp(.div_trunc, casted_lhs, casted_rhs); |
| 14158 | const ok = if (!is_int) ok: { |
| 14159 | const floored = try block.addUnOp(.floor, result); |
| 14160 | |
| 14161 | if (resolved_type.zigTypeTag(zcu) == .vector) { |
| 14162 | const eql = try block.addCmpVector(result, floored, .eq); |
| 14163 | break :ok try block.addReduce(eql, .And); |
| 14164 | } else { |
| 14165 | const is_in_range = try block.addBinOp(switch (block.float_mode) { |
| 14166 | .strict => .cmp_eq, |
| 14167 | .optimized => .cmp_eq_optimized, |
| 14168 | }, result, floored); |
| 14169 | break :ok is_in_range; |
| 14170 | } |
| 14171 | } else ok: { |
| 14172 | const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs); |
| 14173 | |
| 14174 | const scalar_zero = switch (scalar_tag) { |
| 14175 | .comptime_float, .float => try pt.floatValue(resolved_type.scalarType(zcu), 0.0), |
| 14176 | .comptime_int, .int => try pt.intValue(resolved_type.scalarType(zcu), 0), |
| 14177 | else => unreachable, |
| 14178 | }; |
| 14179 | if (resolved_type.zigTypeTag(zcu) == .vector) { |
| 14180 | const zero_val = try sema.splat(resolved_type, scalar_zero); |
| 14181 | const zero = Air.internedToRef(zero_val.toIntern()); |
| 14182 | const eql = try block.addCmpVector(remainder, zero, .eq); |
| 14183 | break :ok try block.addReduce(eql, .And); |
| 14184 | } else { |
| 14185 | const zero = Air.internedToRef(scalar_zero.toIntern()); |
| 14186 | const is_in_range = try block.addBinOp(.cmp_eq, remainder, zero); |
| 14187 | break :ok is_in_range; |
| 14188 | } |
| 14189 | }; |
| 14190 | try sema.addSafetyCheck(block, src, ok, .exact_division_remainder); |
| 14191 | return result; |
| 14192 | } |
| 14193 | |
| 14194 | return block.addBinOp(airTag(block, is_int, .div_exact, .div_exact_optimized), casted_lhs, casted_rhs); |
| 14195 | } |
| 14196 | |
| 14197 | fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 14198 | const pt = sema.pt; |
| 14199 | const zcu = pt.zcu; |
| 14200 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 14201 | const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); |
| 14202 | const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 14203 | const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 14204 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 14205 | const lhs = sema.resolveInst(extra.lhs); |
| 14206 | const rhs = sema.resolveInst(extra.rhs); |
| 14207 | const lhs_ty = sema.typeOf(lhs); |
| 14208 | const rhs_ty = sema.typeOf(rhs); |
| 14209 | const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); |
| 14210 | const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); |
| 14211 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 14212 | try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); |
| 14213 | |
| 14214 | const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ |
| 14215 | .override = &.{ lhs_src, rhs_src }, |
| 14216 | }); |
| 14217 | |
| 14218 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 14219 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 14220 | |
| 14221 | const lhs_scalar_ty = lhs_ty.scalarType(zcu); |
| 14222 | const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); |
| 14223 | |
| 14224 | const is_int = scalar_tag == .int or scalar_tag == .comptime_int; |
| 14225 | |
| 14226 | try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_floor); |
| 14227 | |
| 14228 | const maybe_lhs_val = sema.resolveValue(casted_lhs); |
| 14229 | const maybe_rhs_val = sema.resolveValue(casted_rhs); |
| 14230 | |
| 14231 | const allow_div_zero = !is_int and |
| 14232 | resolved_type.toIntern() != .comptime_float_type and |
| 14233 | block.float_mode == .strict; |
| 14234 | |
| 14235 | if (maybe_lhs_val) |lhs_val| { |
| 14236 | if (maybe_rhs_val) |rhs_val| { |
| 14237 | const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_floor); |
| 14238 | return Air.internedToRef(result.toIntern()); |
| 14239 | } |
| 14240 | if (allow_div_zero) { |
| 14241 | if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); |
| 14242 | } else { |
| 14243 | try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); |
| 14244 | } |
| 14245 | } else if (maybe_rhs_val) |rhs_val| { |
| 14246 | if (allow_div_zero) { |
| 14247 | if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); |
| 14248 | } else { |
| 14249 | try sema.checkAllScalarsDefined(block, rhs_src, rhs_val); |
| 14250 | if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); |
| 14251 | } |
| 14252 | } |
| 14253 | |
| 14254 | if (block.wantSafety()) { |
| 14255 | try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); |
| 14256 | try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); |
| 14257 | } |
| 14258 | |
| 14259 | return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs); |
| 14260 | } |
| 14261 | |
| 14262 | fn zirDivCeil(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 14263 | const pt = sema.pt; |
| 14264 | const zcu = pt.zcu; |
| 14265 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 14266 | const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); |
| 14267 | const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 14268 | const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 14269 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 14270 | const lhs = sema.resolveInst(extra.lhs); |
| 14271 | const rhs = sema.resolveInst(extra.rhs); |
| 14272 | const lhs_ty = sema.typeOf(lhs); |
| 14273 | const rhs_ty = sema.typeOf(rhs); |
| 14274 | const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); |
| 14275 | const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); |
| 14276 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 14277 | try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); |
| 14278 | |
| 14279 | const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ |
| 14280 | .override = &.{ lhs_src, rhs_src }, |
| 14281 | }); |
| 14282 | |
| 14283 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 14284 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 14285 | |
| 14286 | const lhs_scalar_ty = lhs_ty.scalarType(zcu); |
| 14287 | const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); |
| 14288 | |
| 14289 | const is_int = scalar_tag == .int or scalar_tag == .comptime_int; |
| 14290 | |
| 14291 | try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_ceil); |
| 14292 | |
| 14293 | const maybe_lhs_val = sema.resolveValue(casted_lhs); |
| 14294 | const maybe_rhs_val = sema.resolveValue(casted_rhs); |
| 14295 | |
| 14296 | const allow_div_zero = !is_int and |
| 14297 | resolved_type.toIntern() != .comptime_float_type and |
| 14298 | block.float_mode == .strict; |
| 14299 | |
| 14300 | if (maybe_lhs_val) |lhs_val| { |
| 14301 | if (maybe_rhs_val) |rhs_val| { |
| 14302 | const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_ceil); |
| 14303 | return Air.internedToRef(result.toIntern()); |
| 14304 | } |
| 14305 | if (allow_div_zero) { |
| 14306 | if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); |
| 14307 | } else { |
| 14308 | try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); |
| 14309 | } |
| 14310 | } else if (maybe_rhs_val) |rhs_val| { |
| 14311 | if (allow_div_zero) { |
| 14312 | if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); |
| 14313 | } else { |
| 14314 | try sema.checkAllScalarsDefined(block, rhs_src, rhs_val); |
| 14315 | if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); |
| 14316 | } |
| 14317 | } |
| 14318 | |
| 14319 | if (block.wantSafety()) { |
| 14320 | try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); |
| 14321 | try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); |
| 14322 | } |
| 14323 | |
| 14324 | return block.addBinOp(airTag(block, is_int, .div_ceil, .div_ceil_optimized), casted_lhs, casted_rhs); |
| 14325 | } |
| 14326 | |
| 14327 | fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 14328 | const pt = sema.pt; |
| 14329 | const zcu = pt.zcu; |
| 14330 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 14331 | const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); |
| 14332 | const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 14333 | const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 14334 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 14335 | const lhs = sema.resolveInst(extra.lhs); |
| 14336 | const rhs = sema.resolveInst(extra.rhs); |
| 14337 | const lhs_ty = sema.typeOf(lhs); |
| 14338 | const rhs_ty = sema.typeOf(rhs); |
| 14339 | const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); |
| 14340 | const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); |
| 14341 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 14342 | try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); |
| 14343 | |
| 14344 | const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ |
| 14345 | .override = &.{ lhs_src, rhs_src }, |
| 14346 | }); |
| 14347 | |
| 14348 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 14349 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 14350 | |
| 14351 | const lhs_scalar_ty = lhs_ty.scalarType(zcu); |
| 14352 | const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); |
| 14353 | |
| 14354 | const is_int = scalar_tag == .int or scalar_tag == .comptime_int; |
| 14355 | |
| 14356 | try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_trunc); |
| 14357 | |
| 14358 | const maybe_lhs_val = sema.resolveValue(casted_lhs); |
| 14359 | const maybe_rhs_val = sema.resolveValue(casted_rhs); |
| 14360 | |
| 14361 | const allow_div_zero = !is_int and |
| 14362 | resolved_type.toIntern() != .comptime_float_type and |
| 14363 | block.float_mode == .strict; |
| 14364 | |
| 14365 | if (maybe_lhs_val) |lhs_val| { |
| 14366 | if (maybe_rhs_val) |rhs_val| { |
| 14367 | const result = try arith.div(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src, .div_trunc); |
| 14368 | return Air.internedToRef(result.toIntern()); |
| 14369 | } |
| 14370 | if (allow_div_zero) { |
| 14371 | if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); |
| 14372 | } else { |
| 14373 | try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); |
| 14374 | } |
| 14375 | } else if (maybe_rhs_val) |rhs_val| { |
| 14376 | if (allow_div_zero) { |
| 14377 | if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); |
| 14378 | } else { |
| 14379 | try sema.checkAllScalarsDefined(block, rhs_src, rhs_val); |
| 14380 | if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); |
| 14381 | } |
| 14382 | } |
| 14383 | |
| 14384 | if (block.wantSafety()) { |
| 14385 | try sema.addDivIntOverflowSafety(block, src, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int); |
| 14386 | try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); |
| 14387 | } |
| 14388 | |
| 14389 | return block.addBinOp(airTag(block, is_int, .div_trunc, .div_trunc_optimized), casted_lhs, casted_rhs); |
| 14390 | } |
| 14391 | |
| 14392 | fn addDivIntOverflowSafety( |
| 14393 | sema: *Sema, |
| 14394 | block: *Block, |
| 14395 | src: LazySrcLoc, |
| 14396 | resolved_type: Type, |
| 14397 | lhs_scalar_ty: Type, |
| 14398 | maybe_lhs_val: ?Value, |
| 14399 | maybe_rhs_val: ?Value, |
| 14400 | casted_lhs: Air.Inst.Ref, |
| 14401 | casted_rhs: Air.Inst.Ref, |
| 14402 | is_int: bool, |
| 14403 | ) CompileError!void { |
| 14404 | const pt = sema.pt; |
| 14405 | const zcu = pt.zcu; |
| 14406 | if (!is_int) return; |
| 14407 | |
| 14408 | // If the LHS is unsigned, it cannot cause overflow. |
| 14409 | if (!lhs_scalar_ty.isSignedInt(zcu)) return; |
| 14410 | |
| 14411 | // If the LHS is widened to a larger integer type, no overflow is possible. |
| 14412 | if (lhs_scalar_ty.intInfo(zcu).bits < resolved_type.intInfo(zcu).bits) { |
| 14413 | return; |
| 14414 | } |
| 14415 | |
| 14416 | const min_int = try resolved_type.minInt(pt, resolved_type); |
| 14417 | const neg_one_scalar = try pt.intValue(lhs_scalar_ty, -1); |
| 14418 | const neg_one = try sema.splat(resolved_type, neg_one_scalar); |
| 14419 | |
| 14420 | // If the LHS is comptime-known to be not equal to the min int, |
| 14421 | // no overflow is possible. |
| 14422 | if (maybe_lhs_val) |lhs_val| { |
| 14423 | if (try lhs_val.compareAll(.neq, min_int, resolved_type, pt)) return; |
| 14424 | } |
| 14425 | |
| 14426 | // If the RHS is comptime-known to not be equal to -1, no overflow is possible. |
| 14427 | if (maybe_rhs_val) |rhs_val| { |
| 14428 | if (try rhs_val.compareAll(.neq, neg_one, resolved_type, pt)) return; |
| 14429 | } |
| 14430 | |
| 14431 | if (resolved_type.zigTypeTag(zcu) == .vector) { |
| 14432 | const vec_len = resolved_type.vectorLen(zcu); |
| 14433 | |
| 14434 | // This is a bool vector whose elements are true if the LHS element does NOT equal `min_int`. |
| 14435 | const lhs_ok: Air.Inst.Ref = if (maybe_lhs_val) |lhs_val| ok: { |
| 14436 | // The operand is comptime-known; intern a constant bool vector for the potentially unsafe elements. |
| 14437 | const min_int_scalar = try min_int.elemValue(pt, 0); |
| 14438 | const elems_ok = try sema.arena.alloc(InternPool.Index, vec_len); |
| 14439 | for (elems_ok, 0..) |*elem_ok, elem_idx| { |
| 14440 | const elem_val = try lhs_val.elemValue(pt, elem_idx); |
| 14441 | elem_ok.* = if (elem_val.eqlScalarNum(min_int_scalar, zcu)) .bool_false else .bool_true; |
| 14442 | } |
| 14443 | break :ok .fromValue(try pt.aggregateValue(try pt.vectorType(.{ |
| 14444 | .len = vec_len, |
| 14445 | .child = .bool_type, |
| 14446 | }), elems_ok)); |
| 14447 | } else ok: { |
| 14448 | // The operand isn't comptime-known; add a runtime comparison. |
| 14449 | const min_int_ref = Air.internedToRef(min_int.toIntern()); |
| 14450 | break :ok try block.addCmpVector(casted_lhs, min_int_ref, .neq); |
| 14451 | }; |
| 14452 | |
| 14453 | // This is a bool vector whose elements are true if the RHS element does NOT equal -1. |
| 14454 | const rhs_ok: Air.Inst.Ref = if (maybe_rhs_val) |rhs_val| ok: { |
| 14455 | // The operand is comptime-known; intern a constant bool vector for the potentially unsafe elements. |
| 14456 | const elems_ok = try sema.arena.alloc(InternPool.Index, vec_len); |
| 14457 | for (elems_ok, 0..) |*elem_ok, elem_idx| { |
| 14458 | const elem_val = try rhs_val.elemValue(pt, elem_idx); |
| 14459 | elem_ok.* = if (elem_val.eqlScalarNum(neg_one_scalar, zcu)) .bool_false else .bool_true; |
| 14460 | } |
| 14461 | break :ok .fromValue(try pt.aggregateValue(try pt.vectorType(.{ |
| 14462 | .len = vec_len, |
| 14463 | .child = .bool_type, |
| 14464 | }), elems_ok)); |
| 14465 | } else ok: { |
| 14466 | // The operand isn't comptime-known; add a runtime comparison. |
| 14467 | const neg_one_ref = Air.internedToRef(neg_one.toIntern()); |
| 14468 | break :ok try block.addCmpVector(casted_rhs, neg_one_ref, .neq); |
| 14469 | }; |
| 14470 | |
| 14471 | const ok = try block.addReduce(try block.addBinOp(.bit_or, lhs_ok, rhs_ok), .And); |
| 14472 | try sema.addSafetyCheck(block, src, ok, .integer_overflow); |
| 14473 | } else { |
| 14474 | const lhs_ok: Air.Inst.Ref = if (maybe_lhs_val == null) ok: { |
| 14475 | const min_int_ref = Air.internedToRef(min_int.toIntern()); |
| 14476 | break :ok try block.addBinOp(.cmp_neq, casted_lhs, min_int_ref); |
| 14477 | } else .none; // means false |
| 14478 | const rhs_ok: Air.Inst.Ref = if (maybe_rhs_val == null) ok: { |
| 14479 | const neg_one_ref = Air.internedToRef(neg_one.toIntern()); |
| 14480 | break :ok try block.addBinOp(.cmp_neq, casted_rhs, neg_one_ref); |
| 14481 | } else .none; // means false |
| 14482 | |
| 14483 | const ok = if (lhs_ok != .none and rhs_ok != .none) |
| 14484 | try block.addBinOp(.bit_or, lhs_ok, rhs_ok) |
| 14485 | else if (lhs_ok != .none) |
| 14486 | lhs_ok |
| 14487 | else if (rhs_ok != .none) |
| 14488 | rhs_ok |
| 14489 | else |
| 14490 | unreachable; |
| 14491 | |
| 14492 | try sema.addSafetyCheck(block, src, ok, .integer_overflow); |
| 14493 | } |
| 14494 | } |
| 14495 | |
| 14496 | fn addDivByZeroSafety( |
| 14497 | sema: *Sema, |
| 14498 | block: *Block, |
| 14499 | src: LazySrcLoc, |
| 14500 | resolved_type: Type, |
| 14501 | maybe_rhs_val: ?Value, |
| 14502 | casted_rhs: Air.Inst.Ref, |
| 14503 | is_int: bool, |
| 14504 | ) CompileError!void { |
| 14505 | // Strict IEEE floats have well-defined division by zero. |
| 14506 | if (!is_int and block.float_mode == .strict) return; |
| 14507 | |
| 14508 | // If rhs was comptime-known to be zero a compile error would have been |
| 14509 | // emitted above. |
| 14510 | if (maybe_rhs_val != null) return; |
| 14511 | |
| 14512 | const pt = sema.pt; |
| 14513 | const zcu = pt.zcu; |
| 14514 | const scalar_zero = if (is_int) |
| 14515 | try pt.intValue(resolved_type.scalarType(zcu), 0) |
| 14516 | else |
| 14517 | try pt.floatValue(resolved_type.scalarType(zcu), 0.0); |
| 14518 | const ok = if (resolved_type.zigTypeTag(zcu) == .vector) ok: { |
| 14519 | const zero_val = try sema.splat(resolved_type, scalar_zero); |
| 14520 | const zero = Air.internedToRef(zero_val.toIntern()); |
| 14521 | const ok = try block.addCmpVector(casted_rhs, zero, .neq); |
| 14522 | break :ok try block.addReduce(ok, .And); |
| 14523 | } else ok: { |
| 14524 | const zero = Air.internedToRef(scalar_zero.toIntern()); |
| 14525 | break :ok try block.addBinOp(if (is_int) .cmp_neq else .cmp_neq_optimized, casted_rhs, zero); |
| 14526 | }; |
| 14527 | try sema.addSafetyCheck(block, src, ok, .divide_by_zero); |
| 14528 | } |
| 14529 | |
| 14530 | fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst.Tag) Air.Inst.Tag { |
| 14531 | if (is_int) return normal; |
| 14532 | return switch (block.float_mode) { |
| 14533 | .strict => normal, |
| 14534 | .optimized => optimized, |
| 14535 | }; |
| 14536 | } |
| 14537 | |
| 14538 | fn zirModRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 14539 | const pt = sema.pt; |
| 14540 | const zcu = pt.zcu; |
| 14541 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 14542 | const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); |
| 14543 | const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); |
| 14544 | const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); |
| 14545 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 14546 | const lhs = sema.resolveInst(extra.lhs); |
| 14547 | const rhs = sema.resolveInst(extra.rhs); |
| 14548 | const lhs_ty = sema.typeOf(lhs); |
| 14549 | const rhs_ty = sema.typeOf(rhs); |
| 14550 | const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); |
| 14551 | const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); |
| 14552 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 14553 | try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); |
| 14554 | |
| 14555 | const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ |
| 14556 | .override = &.{ lhs_src, rhs_src }, |
| 14557 | }); |
| 14558 | |
| 14559 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 14560 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 14561 | |
| 14562 | const lhs_scalar_ty = lhs_ty.scalarType(zcu); |
| 14563 | const rhs_scalar_ty = rhs_ty.scalarType(zcu); |
| 14564 | const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); |
| 14565 | |
| 14566 | const is_int = scalar_tag == .int or scalar_tag == .comptime_int; |
| 14567 | |
| 14568 | try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod_rem); |
| 14569 | |
| 14570 | const maybe_lhs_val = sema.resolveValue(casted_lhs); |
| 14571 | const maybe_rhs_val = sema.resolveValue(casted_rhs); |
| 14572 | |
| 14573 | const lhs_maybe_negative = a: { |
| 14574 | if (lhs_scalar_ty.isUnsignedInt(zcu)) break :a false; |
| 14575 | const lhs_val = maybe_lhs_val orelse break :a true; |
| 14576 | if (lhs_val.compareAllWithZero(.gte, zcu)) break :a false; |
| 14577 | break :a true; |
| 14578 | }; |
| 14579 | const rhs_maybe_negative = a: { |
| 14580 | if (rhs_scalar_ty.isUnsignedInt(zcu)) break :a false; |
| 14581 | const rhs_val = maybe_rhs_val orelse break :a true; |
| 14582 | if (rhs_val.compareAllWithZero(.gte, zcu)) break :a false; |
| 14583 | break :a true; |
| 14584 | }; |
| 14585 | |
| 14586 | if (maybe_lhs_val) |lhs_val| { |
| 14587 | if (maybe_rhs_val) |rhs_val| { |
| 14588 | const result = try arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .rem); |
| 14589 | if (lhs_maybe_negative or rhs_maybe_negative) { |
| 14590 | if (!result.compareAllWithZero(.eq, zcu)) { |
| 14591 | // Non-zero result means ambiguity between mod and rem |
| 14592 | return sema.failWithModRemNegative(block, src: { |
| 14593 | if (lhs_maybe_negative) break :src lhs_src; |
| 14594 | if (rhs_maybe_negative) break :src rhs_src; |
| 14595 | unreachable; |
| 14596 | }, lhs_ty, rhs_ty); |
| 14597 | } |
| 14598 | } |
| 14599 | return Air.internedToRef(result.toIntern()); |
| 14600 | } |
| 14601 | } |
| 14602 | |
| 14603 | // Result not comptime-known, so floats and signed integers are illegal due to mod/rem ambiguity |
| 14604 | if (lhs_maybe_negative or rhs_maybe_negative) { |
| 14605 | return sema.failWithModRemNegative(block, src: { |
| 14606 | if (lhs_maybe_negative) break :src lhs_src; |
| 14607 | if (rhs_maybe_negative) break :src rhs_src; |
| 14608 | unreachable; |
| 14609 | }, lhs_ty, rhs_ty); |
| 14610 | } |
| 14611 | |
| 14612 | const allow_div_zero = !is_int and |
| 14613 | resolved_type.toIntern() != .comptime_float_type and |
| 14614 | block.float_mode == .strict; |
| 14615 | |
| 14616 | if (maybe_lhs_val) |lhs_val| { |
| 14617 | if (allow_div_zero) { |
| 14618 | if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); |
| 14619 | } else { |
| 14620 | try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); |
| 14621 | } |
| 14622 | } else if (maybe_rhs_val) |rhs_val| { |
| 14623 | if (allow_div_zero) { |
| 14624 | if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); |
| 14625 | } else { |
| 14626 | try sema.checkAllScalarsDefined(block, rhs_src, rhs_val); |
| 14627 | if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); |
| 14628 | } |
| 14629 | } |
| 14630 | |
| 14631 | if (block.wantSafety()) { |
| 14632 | try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); |
| 14633 | } |
| 14634 | |
| 14635 | const air_tag = airTag(block, is_int, .rem, .rem_optimized); |
| 14636 | return block.addBinOp(air_tag, casted_lhs, casted_rhs); |
| 14637 | } |
| 14638 | |
| 14639 | fn zirMod(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 14640 | const pt = sema.pt; |
| 14641 | const zcu = pt.zcu; |
| 14642 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 14643 | const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); |
| 14644 | const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 14645 | const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 14646 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 14647 | const lhs = sema.resolveInst(extra.lhs); |
| 14648 | const rhs = sema.resolveInst(extra.rhs); |
| 14649 | const lhs_ty = sema.typeOf(lhs); |
| 14650 | const rhs_ty = sema.typeOf(rhs); |
| 14651 | const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); |
| 14652 | const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); |
| 14653 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 14654 | try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); |
| 14655 | |
| 14656 | const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ |
| 14657 | .override = &.{ lhs_src, rhs_src }, |
| 14658 | }); |
| 14659 | |
| 14660 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 14661 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 14662 | |
| 14663 | const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); |
| 14664 | |
| 14665 | const is_int = scalar_tag == .int or scalar_tag == .comptime_int; |
| 14666 | |
| 14667 | try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .mod); |
| 14668 | |
| 14669 | const maybe_lhs_val = sema.resolveValue(casted_lhs); |
| 14670 | const maybe_rhs_val = sema.resolveValue(casted_rhs); |
| 14671 | |
| 14672 | const allow_div_zero = !is_int and |
| 14673 | resolved_type.toIntern() != .comptime_float_type and |
| 14674 | block.float_mode == .strict; |
| 14675 | |
| 14676 | if (maybe_lhs_val) |lhs_val| { |
| 14677 | if (maybe_rhs_val) |rhs_val| { |
| 14678 | const result = try arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .mod); |
| 14679 | return Air.internedToRef(result.toIntern()); |
| 14680 | } |
| 14681 | if (allow_div_zero) { |
| 14682 | if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); |
| 14683 | } else { |
| 14684 | try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); |
| 14685 | } |
| 14686 | } else if (maybe_rhs_val) |rhs_val| { |
| 14687 | if (allow_div_zero) { |
| 14688 | if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); |
| 14689 | } else { |
| 14690 | try sema.checkAllScalarsDefined(block, rhs_src, rhs_val); |
| 14691 | if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); |
| 14692 | } |
| 14693 | } |
| 14694 | |
| 14695 | if (block.wantSafety()) { |
| 14696 | try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); |
| 14697 | } |
| 14698 | |
| 14699 | const air_tag = airTag(block, is_int, .mod, .mod_optimized); |
| 14700 | return block.addBinOp(air_tag, casted_lhs, casted_rhs); |
| 14701 | } |
| 14702 | |
| 14703 | fn zirRem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 14704 | const pt = sema.pt; |
| 14705 | const zcu = pt.zcu; |
| 14706 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 14707 | const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); |
| 14708 | const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 14709 | const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 14710 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 14711 | const lhs = sema.resolveInst(extra.lhs); |
| 14712 | const rhs = sema.resolveInst(extra.rhs); |
| 14713 | const lhs_ty = sema.typeOf(lhs); |
| 14714 | const rhs_ty = sema.typeOf(rhs); |
| 14715 | const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); |
| 14716 | const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); |
| 14717 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 14718 | try sema.checkInvalidPtrIntArithmetic(block, src, lhs_ty); |
| 14719 | |
| 14720 | const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ |
| 14721 | .override = &.{ lhs_src, rhs_src }, |
| 14722 | }); |
| 14723 | |
| 14724 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 14725 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 14726 | |
| 14727 | const scalar_tag = resolved_type.scalarType(zcu).zigTypeTag(zcu); |
| 14728 | |
| 14729 | const is_int = scalar_tag == .int or scalar_tag == .comptime_int; |
| 14730 | |
| 14731 | try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .rem); |
| 14732 | |
| 14733 | const maybe_lhs_val = sema.resolveValue(casted_lhs); |
| 14734 | const maybe_rhs_val = sema.resolveValue(casted_rhs); |
| 14735 | |
| 14736 | const allow_div_zero = !is_int and |
| 14737 | resolved_type.toIntern() != .comptime_float_type and |
| 14738 | block.float_mode == .strict; |
| 14739 | |
| 14740 | if (maybe_lhs_val) |lhs_val| { |
| 14741 | if (maybe_rhs_val) |rhs_val| { |
| 14742 | const result = try arith.modRem(sema, block, resolved_type, lhs_val, rhs_val, lhs_src, rhs_src, .rem); |
| 14743 | return Air.internedToRef(result.toIntern()); |
| 14744 | } |
| 14745 | if (allow_div_zero) { |
| 14746 | if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); |
| 14747 | } else { |
| 14748 | try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); |
| 14749 | } |
| 14750 | } else if (maybe_rhs_val) |rhs_val| { |
| 14751 | if (allow_div_zero) { |
| 14752 | if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); |
| 14753 | } else { |
| 14754 | try sema.checkAllScalarsDefined(block, rhs_src, rhs_val); |
| 14755 | if (rhs_val.anyScalarIsZero(zcu)) return sema.failWithDivideByZero(block, rhs_src); |
| 14756 | } |
| 14757 | } |
| 14758 | |
| 14759 | if (block.wantSafety()) { |
| 14760 | try sema.addDivByZeroSafety(block, src, resolved_type, maybe_rhs_val, casted_rhs, is_int); |
| 14761 | } |
| 14762 | |
| 14763 | const air_tag = airTag(block, is_int, .rem, .rem_optimized); |
| 14764 | return block.addBinOp(air_tag, casted_lhs, casted_rhs); |
| 14765 | } |
| 14766 | |
| 14767 | fn zirOverflowArithmetic( |
| 14768 | sema: *Sema, |
| 14769 | block: *Block, |
| 14770 | extended: Zir.Inst.Extended.InstData, |
| 14771 | zir_tag: Zir.Inst.Extended, |
| 14772 | ) CompileError!Air.Inst.Ref { |
| 14773 | const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; |
| 14774 | const src = block.nodeOffset(extra.node); |
| 14775 | |
| 14776 | const lhs_src = block.builtinCallArgSrc(extra.node, 0); |
| 14777 | const rhs_src = block.builtinCallArgSrc(extra.node, 1); |
| 14778 | |
| 14779 | const uncasted_lhs = sema.resolveInst(extra.lhs); |
| 14780 | const uncasted_rhs = sema.resolveInst(extra.rhs); |
| 14781 | |
| 14782 | const lhs_ty = sema.typeOf(uncasted_lhs); |
| 14783 | const rhs_ty = sema.typeOf(uncasted_rhs); |
| 14784 | const pt = sema.pt; |
| 14785 | const zcu = pt.zcu; |
| 14786 | const ip = &zcu.intern_pool; |
| 14787 | |
| 14788 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 14789 | |
| 14790 | const instructions = &[_]Air.Inst.Ref{ uncasted_lhs, uncasted_rhs }; |
| 14791 | const dest_ty = if (zir_tag == .shl_with_overflow) |
| 14792 | lhs_ty |
| 14793 | else |
| 14794 | try sema.resolvePeerTypes(block, src, instructions, .{ |
| 14795 | .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, |
| 14796 | }); |
| 14797 | |
| 14798 | const rhs_dest_ty = if (zir_tag == .shl_with_overflow) |
| 14799 | try sema.log2IntType(block, lhs_ty, src) |
| 14800 | else |
| 14801 | dest_ty; |
| 14802 | |
| 14803 | const lhs = try sema.coerce(block, dest_ty, uncasted_lhs, lhs_src); |
| 14804 | const rhs = try sema.coerce(block, rhs_dest_ty, uncasted_rhs, rhs_src); |
| 14805 | |
| 14806 | if (dest_ty.scalarType(zcu).zigTypeTag(zcu) != .int) { |
| 14807 | return sema.fail(block, src, "expected vector of integers or integer tag type, found '{f}'", .{dest_ty.fmt(pt)}); |
| 14808 | } |
| 14809 | |
| 14810 | const maybe_lhs_val = sema.resolveValue(lhs); |
| 14811 | const maybe_rhs_val = sema.resolveValue(rhs); |
| 14812 | |
| 14813 | const tuple_ty = try pt.overflowArithmeticTupleType(dest_ty); |
| 14814 | const overflow_ty: Type = .fromInterned(ip.indexToKey(tuple_ty.toIntern()).tuple_type.types.get(ip)[1]); |
| 14815 | |
| 14816 | var result: struct { |
| 14817 | inst: Air.Inst.Ref = .none, |
| 14818 | wrapped: Value = Value.@"unreachable", |
| 14819 | overflow_bit: Value, |
| 14820 | } = result: { |
| 14821 | switch (zir_tag) { |
| 14822 | .add_with_overflow => { |
| 14823 | // If either of the arguments is zero, `false` is returned and the other is stored |
| 14824 | // to the result, even if it is undefined.. |
| 14825 | // Otherwise, if either of the argument is undefined, undefined is returned. |
| 14826 | if (maybe_lhs_val) |lhs_val| { |
| 14827 | if (!lhs_val.isUndef(zcu) and lhs_val.compareAllWithZero(.eq, zcu)) { |
| 14828 | break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs }; |
| 14829 | } |
| 14830 | } |
| 14831 | if (maybe_rhs_val) |rhs_val| { |
| 14832 | if (!rhs_val.isUndef(zcu) and rhs_val.compareAllWithZero(.eq, zcu)) { |
| 14833 | break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; |
| 14834 | } |
| 14835 | } |
| 14836 | if (maybe_lhs_val) |lhs_val| { |
| 14837 | if (maybe_rhs_val) |rhs_val| { |
| 14838 | if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) { |
| 14839 | break :result .{ .overflow_bit = .undef, .wrapped = .undef }; |
| 14840 | } |
| 14841 | |
| 14842 | const result = try arith.addWithOverflow(sema, dest_ty, lhs_val, rhs_val); |
| 14843 | break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; |
| 14844 | } |
| 14845 | } |
| 14846 | }, |
| 14847 | .sub_with_overflow => { |
| 14848 | // If the rhs is zero, then the result is lhs and no overflow occured. |
| 14849 | // Otherwise, if either result is undefined, both results are undefined. |
| 14850 | if (maybe_rhs_val) |rhs_val| { |
| 14851 | if (rhs_val.isUndef(zcu)) { |
| 14852 | break :result .{ .overflow_bit = .undef, .wrapped = .undef }; |
| 14853 | } else if (rhs_val.compareAllWithZero(.eq, zcu)) { |
| 14854 | break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; |
| 14855 | } else if (maybe_lhs_val) |lhs_val| { |
| 14856 | if (lhs_val.isUndef(zcu)) { |
| 14857 | break :result .{ .overflow_bit = .undef, .wrapped = .undef }; |
| 14858 | } |
| 14859 | |
| 14860 | const result = try arith.subWithOverflow(sema, dest_ty, lhs_val, rhs_val); |
| 14861 | break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; |
| 14862 | } |
| 14863 | } |
| 14864 | }, |
| 14865 | .mul_with_overflow => { |
| 14866 | // If either of the arguments is zero, the result is zero and no overflow occured. |
| 14867 | if (maybe_lhs_val) |lhs_val| { |
| 14868 | if (!lhs_val.isUndef(zcu) and lhs_val.compareAllWithZero(.eq, zcu)) { |
| 14869 | break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; |
| 14870 | } |
| 14871 | } |
| 14872 | if (maybe_rhs_val) |rhs_val| { |
| 14873 | if (!rhs_val.isUndef(zcu) and rhs_val.compareAllWithZero(.eq, zcu)) { |
| 14874 | break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs }; |
| 14875 | } |
| 14876 | } |
| 14877 | // If either of the arguments is one, the result is the other and no overflow occured. |
| 14878 | const dest_scalar_ty = dest_ty.scalarType(zcu); |
| 14879 | const dest_scalar_int = dest_scalar_ty.intInfo(zcu); |
| 14880 | // We could still be working with i1, where '1' is not a legal value! |
| 14881 | if (!(dest_scalar_int.bits == 1 and dest_scalar_int.signedness == .signed)) { |
| 14882 | const scalar_one = try pt.intValue(dest_scalar_ty, 1); |
| 14883 | const vec_one = try sema.splat(dest_ty, scalar_one); |
| 14884 | if (maybe_lhs_val) |lhs_val| { |
| 14885 | if (!lhs_val.isUndef(zcu) and try sema.compareAll(lhs_val, .eq, vec_one, dest_ty)) { |
| 14886 | break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = rhs }; |
| 14887 | } |
| 14888 | } |
| 14889 | if (maybe_rhs_val) |rhs_val| { |
| 14890 | if (!rhs_val.isUndef(zcu) and try sema.compareAll(rhs_val, .eq, vec_one, dest_ty)) { |
| 14891 | break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; |
| 14892 | } |
| 14893 | } |
| 14894 | } |
| 14895 | |
| 14896 | if (maybe_lhs_val) |lhs_val| { |
| 14897 | if (maybe_rhs_val) |rhs_val| { |
| 14898 | if (lhs_val.isUndef(zcu) or rhs_val.isUndef(zcu)) { |
| 14899 | break :result .{ .overflow_bit = .undef, .wrapped = .undef }; |
| 14900 | } |
| 14901 | const result = try arith.mulWithOverflow(sema, dest_ty, lhs_val, rhs_val); |
| 14902 | break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; |
| 14903 | } |
| 14904 | } |
| 14905 | }, |
| 14906 | .shl_with_overflow => { |
| 14907 | // If either of the arguments is undefined, IB is possible and we return an error. |
| 14908 | // If lhs is zero, the result is zero and no overflow occurred. |
| 14909 | // If rhs is zero, the result is lhs and no overflow occurred. |
| 14910 | const scalar_ty = lhs_ty.scalarType(zcu); |
| 14911 | if (maybe_rhs_val) |rhs_val| { |
| 14912 | if (maybe_lhs_val) |lhs_val| { |
| 14913 | const result = try arith.shlWithOverflow(sema, block, lhs_ty, lhs_val, rhs_val, lhs_src, rhs_src); |
| 14914 | break :result .{ .overflow_bit = result.overflow_bit, .wrapped = result.wrapped_result }; |
| 14915 | } |
| 14916 | if (rhs_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, null); |
| 14917 | const bits = scalar_ty.intInfo(zcu).bits; |
| 14918 | switch (rhs_ty.zigTypeTag(zcu)) { |
| 14919 | .int, .comptime_int => { |
| 14920 | switch (Value.order(rhs_val, .zero_comptime_int, zcu)) { |
| 14921 | .gt => { |
| 14922 | var rhs_space: Value.BigIntSpace = undefined; |
| 14923 | const rhs_bigint = rhs_val.toBigInt(&rhs_space, zcu); |
| 14924 | if (rhs_bigint.orderAgainstScalar(bits) != .lt) { |
| 14925 | return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_val, rhs_src, null); |
| 14926 | } |
| 14927 | }, |
| 14928 | .eq => break :result .{ .overflow_bit = .zero_u1, .inst = lhs }, |
| 14929 | .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_val, null), |
| 14930 | } |
| 14931 | }, |
| 14932 | .vector => { |
| 14933 | var any_positive: bool = false; |
| 14934 | for (0..rhs_ty.vectorLen(zcu)) |elem_idx| { |
| 14935 | const rhs_elem = try rhs_val.elemValue(pt, elem_idx); |
| 14936 | if (rhs_elem.isUndef(zcu)) return sema.failWithUseOfUndef(block, rhs_src, elem_idx); |
| 14937 | switch (Value.order(rhs_elem, .zero_comptime_int, zcu)) { |
| 14938 | .gt => { |
| 14939 | var rhs_elem_space: Value.BigIntSpace = undefined; |
| 14940 | const rhs_elem_bigint = rhs_elem.toBigInt(&rhs_elem_space, zcu); |
| 14941 | if (rhs_elem_bigint.orderAgainstScalar(bits) != .lt) { |
| 14942 | return sema.failWithTooLargeShiftAmount(block, lhs_ty, rhs_elem, rhs_src, elem_idx); |
| 14943 | } |
| 14944 | any_positive = true; |
| 14945 | }, |
| 14946 | .eq => {}, |
| 14947 | .lt => return sema.failWithNegativeShiftAmount(block, rhs_src, rhs_elem, elem_idx), |
| 14948 | } |
| 14949 | } |
| 14950 | if (!any_positive) break :result .{ .overflow_bit = try pt.aggregateSplatValue(overflow_ty, .zero_u1), .inst = lhs }; |
| 14951 | }, |
| 14952 | else => unreachable, |
| 14953 | } |
| 14954 | if (rhs_val.compareAllWithZero(.eq, zcu)) { |
| 14955 | break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; |
| 14956 | } |
| 14957 | } else { |
| 14958 | if (scalar_ty.toIntern() == .comptime_int_type) { |
| 14959 | return sema.fail(block, src, "LHS of shift must be a fixed-width integer type, or RHS must be comptime-known", .{}); |
| 14960 | } |
| 14961 | if (maybe_lhs_val) |lhs_val| { |
| 14962 | try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); |
| 14963 | if (lhs_val.compareAllWithZero(.eq, zcu)) { |
| 14964 | break :result .{ .overflow_bit = try sema.splat(overflow_ty, .zero_u1), .inst = lhs }; |
| 14965 | } |
| 14966 | } |
| 14967 | } |
| 14968 | }, |
| 14969 | else => unreachable, |
| 14970 | } |
| 14971 | |
| 14972 | const air_tag: Air.Inst.Tag = switch (zir_tag) { |
| 14973 | .add_with_overflow => .add_with_overflow, |
| 14974 | .mul_with_overflow => .mul_with_overflow, |
| 14975 | .sub_with_overflow => .sub_with_overflow, |
| 14976 | .shl_with_overflow => .shl_with_overflow, |
| 14977 | else => unreachable, |
| 14978 | }; |
| 14979 | |
| 14980 | return block.addInst(.{ |
| 14981 | .tag = air_tag, |
| 14982 | .data = .{ .ty_pl = .{ |
| 14983 | .ty = tuple_ty, |
| 14984 | .payload = try block.sema.addExtra(Air.Bin{ |
| 14985 | .lhs = lhs, |
| 14986 | .rhs = rhs, |
| 14987 | }), |
| 14988 | } }, |
| 14989 | }); |
| 14990 | }; |
| 14991 | |
| 14992 | if (result.inst != .none) { |
| 14993 | if (sema.resolveValue(result.inst)) |some| { |
| 14994 | result.wrapped = some; |
| 14995 | result.inst = .none; |
| 14996 | } |
| 14997 | } |
| 14998 | |
| 14999 | if (result.inst == .none) { |
| 15000 | return Air.internedToRef((try pt.aggregateValue(tuple_ty, &.{ |
| 15001 | result.wrapped.toIntern(), |
| 15002 | result.overflow_bit.toIntern(), |
| 15003 | })).toIntern()); |
| 15004 | } |
| 15005 | |
| 15006 | const element_refs = try sema.arena.alloc(Air.Inst.Ref, 2); |
| 15007 | element_refs[0] = result.inst; |
| 15008 | element_refs[1] = Air.internedToRef(result.overflow_bit.toIntern()); |
| 15009 | return block.addAggregateInit(tuple_ty, element_refs); |
| 15010 | } |
| 15011 | |
| 15012 | fn splat(sema: *Sema, ty: Type, val: Value) !Value { |
| 15013 | const pt = sema.pt; |
| 15014 | if (ty.zigTypeTag(pt.zcu) != .vector) return val; |
| 15015 | return pt.aggregateSplatValue(ty, val); |
| 15016 | } |
| 15017 | |
| 15018 | fn analyzeArithmetic( |
| 15019 | sema: *Sema, |
| 15020 | block: *Block, |
| 15021 | zir_tag: Zir.Inst.Tag, |
| 15022 | lhs: Air.Inst.Ref, |
| 15023 | rhs: Air.Inst.Ref, |
| 15024 | src: LazySrcLoc, |
| 15025 | lhs_src: LazySrcLoc, |
| 15026 | rhs_src: LazySrcLoc, |
| 15027 | want_safety: bool, |
| 15028 | ) CompileError!Air.Inst.Ref { |
| 15029 | const pt = sema.pt; |
| 15030 | const zcu = pt.zcu; |
| 15031 | const lhs_ty = sema.typeOf(lhs); |
| 15032 | const rhs_ty = sema.typeOf(rhs); |
| 15033 | const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); |
| 15034 | const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); |
| 15035 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 15036 | |
| 15037 | if (lhs_zig_ty_tag == .pointer) { |
| 15038 | if (rhs_zig_ty_tag == .pointer) { |
| 15039 | if (lhs_ty.ptrSize(zcu) != .slice and rhs_ty.ptrSize(zcu) != .slice) { |
| 15040 | if (zir_tag != .sub) { |
| 15041 | return sema.failWithInvalidPtrArithmetic(block, src, "pointer-pointer", "subtraction"); |
| 15042 | } |
| 15043 | |
| 15044 | // TODO: these semantics are really weird. Pointer subtraction works in increments |
| 15045 | // of the pointer child for indexable pointers (excluding pointers to vectors), |
| 15046 | // which makes sense, but we also allow it for arbitrary single-item pointers, which |
| 15047 | // leads to the weird result that subtraction of '*T' works completely differently |
| 15048 | // depending on whether 'T' is an array. That seems dangerous and confusing, and |
| 15049 | // requires the odd logic below. This behavior originally came from a now-removed |
| 15050 | // function `Type.elemType2`, which was removed precisely *because* the thing it did |
| 15051 | // wasn't really well-defined; for that reason, these semantics were probably |
| 15052 | // largely accidental to begin with. We should change the langauge to avoid this |
| 15053 | // confusing behavior. For instance, perhaps pointer subtraction should only work on |
| 15054 | // indexable pointers. |
| 15055 | const lhs_elem_ty = ty: { |
| 15056 | const ptr_elem_ty = lhs_ty.childType(zcu); |
| 15057 | if (lhs_ty.ptrSize(zcu) == .one and ptr_elem_ty.zigTypeTag(zcu) == .array) break :ty ptr_elem_ty.childType(zcu); |
| 15058 | break :ty ptr_elem_ty; |
| 15059 | }; |
| 15060 | const rhs_elem_ty = ty: { |
| 15061 | const ptr_elem_ty = rhs_ty.childType(zcu); |
| 15062 | if (rhs_ty.ptrSize(zcu) == .one and ptr_elem_ty.zigTypeTag(zcu) == .array) break :ty ptr_elem_ty.childType(zcu); |
| 15063 | break :ty ptr_elem_ty; |
| 15064 | }; |
| 15065 | if (lhs_elem_ty.toIntern() != rhs_elem_ty.toIntern()) { |
| 15066 | return sema.fail(block, src, "incompatible pointer arithmetic operands '{f}' and '{f}'", .{ |
| 15067 | lhs_ty.fmt(pt), rhs_ty.fmt(pt), |
| 15068 | }); |
| 15069 | } |
| 15070 | |
| 15071 | try sema.ensureLayoutResolved(lhs_elem_ty, src, .ptr_offset); |
| 15072 | const elem_size = lhs_elem_ty.abiSize(zcu); |
| 15073 | if (elem_size == 0) { |
| 15074 | return sema.fail(block, src, "pointer subtraction requires element type '{f}' to have runtime bits", .{ |
| 15075 | lhs_elem_ty.fmt(pt), |
| 15076 | }); |
| 15077 | } |
| 15078 | |
| 15079 | const runtime_src = runtime_src: { |
| 15080 | if (sema.resolveValue(lhs)) |lhs_value| { |
| 15081 | if (sema.resolveValue(rhs)) |rhs_value| { |
| 15082 | const lhs_ptr = switch (zcu.intern_pool.indexToKey(lhs_value.toIntern())) { |
| 15083 | .undef => return sema.failWithUseOfUndef(block, lhs_src, null), |
| 15084 | .ptr => |ptr| ptr, |
| 15085 | else => unreachable, |
| 15086 | }; |
| 15087 | const rhs_ptr = switch (zcu.intern_pool.indexToKey(rhs_value.toIntern())) { |
| 15088 | .undef => return sema.failWithUseOfUndef(block, rhs_src, null), |
| 15089 | .ptr => |ptr| ptr, |
| 15090 | else => unreachable, |
| 15091 | }; |
| 15092 | // Make sure the pointers point to the same data. |
| 15093 | if (!lhs_ptr.base_addr.eql(rhs_ptr.base_addr)) break :runtime_src src; |
| 15094 | const address = std.math.sub(u64, lhs_ptr.byte_offset, rhs_ptr.byte_offset) catch |
| 15095 | return sema.fail(block, src, "operation results in overflow", .{}); |
| 15096 | const result = address / elem_size; |
| 15097 | return try pt.intRef(.usize, result); |
| 15098 | } else { |
| 15099 | break :runtime_src lhs_src; |
| 15100 | } |
| 15101 | } else { |
| 15102 | break :runtime_src rhs_src; |
| 15103 | } |
| 15104 | }; |
| 15105 | |
| 15106 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 15107 | try sema.checkLogicalPtrOperation(block, src, lhs_ty); |
| 15108 | try sema.checkLogicalPtrOperation(block, src, rhs_ty); |
| 15109 | const lhs_int = try block.addTyOp(.int_from_ptr, .usize, lhs); |
| 15110 | const rhs_int = try block.addTyOp(.int_from_ptr, .usize, rhs); |
| 15111 | const address = try block.addBinOp(.sub_wrap, lhs_int, rhs_int); |
| 15112 | return try block.addBinOp(.div_exact, address, try pt.intRef(.usize, elem_size)); |
| 15113 | } |
| 15114 | } else { |
| 15115 | switch (lhs_ty.ptrSize(zcu)) { |
| 15116 | .one, .slice => {}, |
| 15117 | .many, .c => { |
| 15118 | const air_tag: Air.Inst.Tag = switch (zir_tag) { |
| 15119 | .add => .ptr_add, |
| 15120 | .sub => .ptr_sub, |
| 15121 | else => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"), |
| 15122 | }; |
| 15123 | |
| 15124 | try sema.ensureLayoutResolved(lhs_ty.childType(zcu), src, .ptr_offset); |
| 15125 | return sema.analyzePtrArithmetic(block, src, lhs, rhs, air_tag, rhs_src); |
| 15126 | }, |
| 15127 | } |
| 15128 | } |
| 15129 | } |
| 15130 | |
| 15131 | const resolved_type = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ |
| 15132 | .override = &.{ lhs_src, rhs_src }, |
| 15133 | }); |
| 15134 | |
| 15135 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 15136 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 15137 | |
| 15138 | const scalar_type = resolved_type.scalarType(zcu); |
| 15139 | const scalar_tag = scalar_type.zigTypeTag(zcu); |
| 15140 | |
| 15141 | try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, zir_tag); |
| 15142 | |
| 15143 | // The rules we'll implement below are as follows: |
| 15144 | // |
| 15145 | // * If both operands are comptime-known, we call the corresponding function in `arith` to get |
| 15146 | // the comptime-known result. Inputs which would be IB at runtime are compile errors. |
| 15147 | // |
| 15148 | // * Otherwise, if one operand is comptime-known `undefined`, we trigger a compile error if this |
| 15149 | // operator can ever possibly trigger IB, or otherwise return comptime-known `undefined`. |
| 15150 | // |
| 15151 | // * No other comptime operand detemines a comptime result; e.g. `0 * x` isn't always `0` because |
| 15152 | // of `undefined`. Therefore, the remaining cases all become runtime operations. |
| 15153 | |
| 15154 | const is_int = switch (scalar_tag) { |
| 15155 | .int, .comptime_int => true, |
| 15156 | .float, .comptime_float => false, |
| 15157 | else => unreachable, |
| 15158 | }; |
| 15159 | |
| 15160 | const maybe_lhs_val = sema.resolveValue(casted_lhs); |
| 15161 | const maybe_rhs_val = sema.resolveValue(casted_rhs); |
| 15162 | |
| 15163 | if (maybe_lhs_val) |lhs_val| { |
| 15164 | if (maybe_rhs_val) |rhs_val| { |
| 15165 | const result_val = switch (zir_tag) { |
| 15166 | .add, .add_unsafe => try arith.add(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src), |
| 15167 | .addwrap => try arith.addWrap(sema, resolved_type, lhs_val, rhs_val), |
| 15168 | .add_sat => try arith.addSat(sema, resolved_type, lhs_val, rhs_val), |
| 15169 | .sub => try arith.sub(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src), |
| 15170 | .subwrap => try arith.subWrap(sema, resolved_type, lhs_val, rhs_val), |
| 15171 | .sub_sat => try arith.subSat(sema, resolved_type, lhs_val, rhs_val), |
| 15172 | .mul => try arith.mul(sema, block, resolved_type, lhs_val, rhs_val, src, lhs_src, rhs_src), |
| 15173 | .mulwrap => try arith.mulWrap(sema, resolved_type, lhs_val, rhs_val), |
| 15174 | .mul_sat => try arith.mulSat(sema, resolved_type, lhs_val, rhs_val), |
| 15175 | else => unreachable, |
| 15176 | }; |
| 15177 | return Air.internedToRef(result_val.toIntern()); |
| 15178 | } |
| 15179 | } |
| 15180 | |
| 15181 | const air_tag: Air.Inst.Tag, const air_tag_safe: Air.Inst.Tag, const allow_undef: bool = switch (zir_tag) { |
| 15182 | .add, .add_unsafe => .{ if (block.float_mode == .optimized) .add_optimized else .add, .add_safe, !is_int }, |
| 15183 | .addwrap => .{ .add_wrap, .add_wrap, true }, |
| 15184 | .add_sat => .{ .add_sat, .add_sat, true }, |
| 15185 | .sub => .{ if (block.float_mode == .optimized) .sub_optimized else .sub, .sub_safe, !is_int }, |
| 15186 | .subwrap => .{ .sub_wrap, .sub_wrap, true }, |
| 15187 | .sub_sat => .{ .sub_sat, .sub_sat, true }, |
| 15188 | .mul => .{ if (block.float_mode == .optimized) .mul_optimized else .mul, .mul_safe, !is_int }, |
| 15189 | .mulwrap => .{ .mul_wrap, .mul_wrap, true }, |
| 15190 | .mul_sat => .{ .mul_sat, .mul_sat, true }, |
| 15191 | else => unreachable, |
| 15192 | }; |
| 15193 | |
| 15194 | if (allow_undef) { |
| 15195 | if (maybe_lhs_val) |lhs_val| { |
| 15196 | if (lhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); |
| 15197 | } |
| 15198 | if (maybe_rhs_val) |rhs_val| { |
| 15199 | if (rhs_val.isUndef(zcu)) return pt.undefRef(resolved_type); |
| 15200 | } |
| 15201 | } else { |
| 15202 | if (maybe_lhs_val) |lhs_val| { |
| 15203 | try sema.checkAllScalarsDefined(block, lhs_src, lhs_val); |
| 15204 | } |
| 15205 | if (maybe_rhs_val) |rhs_val| { |
| 15206 | try sema.checkAllScalarsDefined(block, rhs_src, rhs_val); |
| 15207 | } |
| 15208 | } |
| 15209 | |
| 15210 | if (block.wantSafety() and want_safety and scalar_tag == .int) { |
| 15211 | if (air_tag != air_tag_safe) try sema.preparePanicId(src, .integer_overflow); |
| 15212 | return block.addBinOp(air_tag_safe, casted_lhs, casted_rhs); |
| 15213 | } |
| 15214 | return block.addBinOp(air_tag, casted_lhs, casted_rhs); |
| 15215 | } |
| 15216 | |
| 15217 | /// Asserts that the layout of the pointer child type is already resolved. |
| 15218 | fn analyzePtrArithmetic( |
| 15219 | sema: *Sema, |
| 15220 | block: *Block, |
| 15221 | op_src: LazySrcLoc, |
| 15222 | ptr: Air.Inst.Ref, |
| 15223 | uncasted_offset: Air.Inst.Ref, |
| 15224 | air_tag: Air.Inst.Tag, |
| 15225 | offset_src: LazySrcLoc, |
| 15226 | ) CompileError!Air.Inst.Ref { |
| 15227 | // TODO if the operand is comptime-known to be negative, or is a negative int, |
| 15228 | // coerce to isize instead of usize. |
| 15229 | const offset = try sema.coerce(block, .usize, uncasted_offset, offset_src); |
| 15230 | const pt = sema.pt; |
| 15231 | const zcu = pt.zcu; |
| 15232 | const ptr_ty = sema.typeOf(ptr); |
| 15233 | const ptr_info = ptr_ty.ptrInfo(zcu); |
| 15234 | assert(ptr_info.flags.size == .many or ptr_info.flags.size == .c); |
| 15235 | |
| 15236 | const maybe_index: ?u64 = if (try sema.resolveDefinedValue(block, offset_src, offset)) |val| off: { |
| 15237 | break :off val.toUnsignedInt(zcu); |
| 15238 | } else null; |
| 15239 | |
| 15240 | const elem_ty: Type = .fromInterned(ptr_info.child); |
| 15241 | elem_ty.assertHasLayout(zcu); |
| 15242 | |
| 15243 | switch (elem_ty.classify(zcu)) { |
| 15244 | .no_possible_value, .one_possible_value => { |
| 15245 | // Offset will be multiplied by zero, so result is the same as the base pointer. |
| 15246 | return ptr; |
| 15247 | }, |
| 15248 | else => {}, |
| 15249 | } |
| 15250 | |
| 15251 | const elem_ptr_ty = try ptr_ty.elemPtrType(maybe_index, pt); |
| 15252 | // `elem_ptr_ty` is a single-item pointer, but we want a many-item or C pointer, and to preserve |
| 15253 | // any input sentinel. |
| 15254 | const new_ptr_ty = try pt.ptrType(info: { |
| 15255 | var info = elem_ptr_ty.ptrInfo(zcu); |
| 15256 | info.flags.size = ptr_info.flags.size; |
| 15257 | info.sentinel = ptr_info.sentinel; |
| 15258 | break :info info; |
| 15259 | }); |
| 15260 | |
| 15261 | ct: { |
| 15262 | const ptr_val = sema.resolveValue(ptr) orelse break :ct; |
| 15263 | if (ptr_val.isUndef(zcu)) return pt.undefRef(new_ptr_ty); |
| 15264 | const index = maybe_index orelse break :ct; |
| 15265 | |
| 15266 | if (index == 0) return ptr; |
| 15267 | if (air_tag == .ptr_sub) { |
| 15268 | const elem_size = elem_ty.abiSize(zcu); |
| 15269 | return .fromValue(try sema.ptrSubtract(block, op_src, ptr_val, index * elem_size, new_ptr_ty)); |
| 15270 | } else { |
| 15271 | return .fromValue(try pt.getCoerced(try ptr_val.ptrElem(index, pt), new_ptr_ty)); |
| 15272 | } |
| 15273 | } |
| 15274 | |
| 15275 | try sema.checkLogicalPtrOperation(block, op_src, ptr_ty); |
| 15276 | |
| 15277 | return block.addInst(.{ |
| 15278 | .tag = air_tag, |
| 15279 | .data = .{ .ty_pl = .{ |
| 15280 | .ty = new_ptr_ty, |
| 15281 | .payload = try sema.addExtra(Air.Bin{ |
| 15282 | .lhs = ptr, |
| 15283 | .rhs = offset, |
| 15284 | }), |
| 15285 | } }, |
| 15286 | }); |
| 15287 | } |
| 15288 | |
| 15289 | fn zirLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 15290 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 15291 | const src = block.nodeOffset(inst_data.src_node); |
| 15292 | const ptr_src = src; // TODO better source location |
| 15293 | const ptr = sema.resolveInst(inst_data.operand); |
| 15294 | return sema.analyzeLoad(block, src, ptr, ptr_src); |
| 15295 | } |
| 15296 | |
| 15297 | fn zirAsm( |
| 15298 | sema: *Sema, |
| 15299 | block: *Block, |
| 15300 | extended: Zir.Inst.Extended.InstData, |
| 15301 | tmpl_is_expr: bool, |
| 15302 | ) CompileError!Air.Inst.Ref { |
| 15303 | const pt = sema.pt; |
| 15304 | const zcu = pt.zcu; |
| 15305 | const comp = zcu.comp; |
| 15306 | const gpa = comp.gpa; |
| 15307 | const io = comp.io; |
| 15308 | const ip = &zcu.intern_pool; |
| 15309 | |
| 15310 | const extra = sema.code.extraData(Zir.Inst.Asm, extended.operand); |
| 15311 | const src = block.nodeOffset(extra.data.src_node); |
| 15312 | const ret_ty_src = block.src(.{ .node_offset_asm_ret_ty = extra.data.src_node }); |
| 15313 | const small: Zir.Inst.Asm.Small = @bitCast(extended.small); |
| 15314 | const outputs_len = small.outputs_len; |
| 15315 | const inputs_len = small.inputs_len; |
| 15316 | const is_volatile = small.is_volatile; |
| 15317 | const is_global_assembly = sema.func_index == .none; |
| 15318 | |
| 15319 | const asm_source: []const u8 = if (tmpl_is_expr) s: { |
| 15320 | const tmpl: Zir.Inst.Ref = @fromBackingInt(@intCast(@backingInt(extra.data.asm_source))); |
| 15321 | break :s try sema.resolveConstString(block, src, tmpl, .{ .simple = .inline_assembly_code }); |
| 15322 | } else sema.code.nullTerminatedString(extra.data.asm_source); |
| 15323 | |
| 15324 | if (is_global_assembly) { |
| 15325 | assert(outputs_len == 0); // validated by AstGen |
| 15326 | assert(inputs_len == 0); // validated by AstGen |
| 15327 | assert(extra.data.clobbers == .none); // validated by AstGen |
| 15328 | assert(!is_volatile); // validated by AstGen |
| 15329 | |
| 15330 | try zcu.addGlobalAssembly(sema.owner, asm_source); |
| 15331 | return .void_value; |
| 15332 | } |
| 15333 | |
| 15334 | try sema.requireRuntimeBlock(block, src, null); |
| 15335 | |
| 15336 | var extra_i = extra.end; |
| 15337 | var output_type_bits = extra.data.output_type_bits; |
| 15338 | var needed_capacity: usize = @typeInfo(Air.Asm).@"struct".field_names.len + outputs_len + inputs_len; |
| 15339 | |
| 15340 | const ConstraintName = struct { c: []const u8, n: []const u8 }; |
| 15341 | const out_args = try sema.arena.alloc(Air.Inst.Ref, outputs_len); |
| 15342 | const outputs = try sema.arena.alloc(ConstraintName, outputs_len); |
| 15343 | var expr_ty: Type = .void; |
| 15344 | |
| 15345 | for (out_args, 0..) |*arg, out_i| { |
| 15346 | const output = sema.code.extraData(Zir.Inst.Asm.Output, extra_i); |
| 15347 | const output_src = block.src(.{ .asm_output = .{ |
| 15348 | .offset = src.offset.node_offset.x, |
| 15349 | .output_index = @intCast(out_i), |
| 15350 | } }); |
| 15351 | extra_i = output.end; |
| 15352 | |
| 15353 | const is_type = @as(u1, @truncate(output_type_bits)) != 0; |
| 15354 | output_type_bits >>= 1; |
| 15355 | |
| 15356 | const name = sema.code.nullTerminatedString(output.data.name); |
| 15357 | |
| 15358 | const out_ty: Type = out_ty: { |
| 15359 | if (is_type) { |
| 15360 | // Indicate the output is the asm instruction return value. |
| 15361 | arg.* = .none; |
| 15362 | |
| 15363 | const out_ty = try sema.resolveType(block, ret_ty_src, output.data.operand); |
| 15364 | try sema.ensureLayoutResolved(out_ty, ret_ty_src, .asm_out_type); |
| 15365 | expr_ty = out_ty; |
| 15366 | break :out_ty out_ty; |
| 15367 | } else { |
| 15368 | const inst = sema.resolveInst(output.data.operand); |
| 15369 | arg.* = inst; |
| 15370 | |
| 15371 | if (!sema.checkRuntimeValue(inst)) { |
| 15372 | const output_name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls); |
| 15373 | return sema.failWithContainsReferenceToComptimeVar(block, output_src, output_name, "assembly output", .fromInterned(inst.toInterned().?)); |
| 15374 | } |
| 15375 | break :out_ty sema.typeOf(inst).childType(zcu); |
| 15376 | } |
| 15377 | }; |
| 15378 | switch (out_ty.zigTypeTag(zcu)) { |
| 15379 | .int, .float, .bool, .vector => {}, |
| 15380 | |
| 15381 | .pointer => if (out_ty.isSlice(zcu)) return sema.failWithOwnedErrorMsg(block, msg: { |
| 15382 | const msg = try sema.errMsg(output_src, "invalid inline assembly output type '{f}'", .{out_ty.fmt(pt)}); |
| 15383 | errdefer msg.destroy(gpa); |
| 15384 | try sema.errNote(output_src, msg, "consider separate outputs for 'ptr' and 'len'", .{}); |
| 15385 | break :msg msg; |
| 15386 | }), |
| 15387 | |
| 15388 | .optional => if (!out_ty.isPtrLikeOptional(zcu)) { |
| 15389 | return sema.fail(block, output_src, "invalid inline assembly output type '{f}'", .{out_ty.fmt(pt)}); |
| 15390 | }, |
| 15391 | |
| 15392 | .@"enum" => switch (ip.loadEnumType(out_ty.toIntern()).int_tag_mode) { |
| 15393 | .explicit => {}, |
| 15394 | .auto => return sema.failWithOwnedErrorMsg(block, msg: { |
| 15395 | const msg = try sema.errMsg(output_src, "invalid inline assembly output type '{f}'", .{out_ty.fmt(pt)}); |
| 15396 | errdefer msg.destroy(gpa); |
| 15397 | try sema.errNote(out_ty.srcLoc(zcu), msg, "integer tag type of enum is inferred", .{}); |
| 15398 | try sema.errNote(out_ty.srcLoc(zcu), msg, "consider explicitly specifying the integer tag type", .{}); |
| 15399 | break :msg msg; |
| 15400 | }), |
| 15401 | }, |
| 15402 | |
| 15403 | .@"struct" => switch (out_ty.containerLayout(zcu)) { |
| 15404 | .@"packed" => {}, |
| 15405 | .auto, .@"extern" => return sema.failWithOwnedErrorMsg(block, msg: { |
| 15406 | const msg = try sema.errMsg(output_src, "invalid inline assembly output type '{f}'", .{out_ty.fmt(pt)}); |
| 15407 | errdefer msg.destroy(gpa); |
| 15408 | try sema.errNote(output_src, msg, "struct types cannot be passed to inline assembly", .{}); |
| 15409 | try sema.addDeclaredHereNote(msg, out_ty); |
| 15410 | break :msg msg; |
| 15411 | }), |
| 15412 | }, |
| 15413 | |
| 15414 | .@"union" => switch (out_ty.containerLayout(zcu)) { |
| 15415 | .@"packed" => {}, |
| 15416 | .auto, .@"extern" => return sema.failWithOwnedErrorMsg(block, msg: { |
| 15417 | const msg = try sema.errMsg(output_src, "invalid inline assembly output type '{f}'", .{out_ty.fmt(pt)}); |
| 15418 | errdefer msg.destroy(gpa); |
| 15419 | try sema.errNote(output_src, msg, "union types cannot be passed to inline assembly", .{}); |
| 15420 | try sema.addDeclaredHereNote(msg, out_ty); |
| 15421 | break :msg msg; |
| 15422 | }), |
| 15423 | }, |
| 15424 | |
| 15425 | .array => return sema.failWithOwnedErrorMsg(block, msg: { |
| 15426 | const msg = try sema.errMsg(output_src, "invalid inline assembly output type '{f}'", .{out_ty.fmt(pt)}); |
| 15427 | errdefer msg.destroy(gpa); |
| 15428 | try sema.errNote(output_src, msg, "array types cannot be passed to inline assembly", .{}); |
| 15429 | break :msg msg; |
| 15430 | }), |
| 15431 | |
| 15432 | .void, |
| 15433 | .type, |
| 15434 | .noreturn, |
| 15435 | .comptime_float, |
| 15436 | .comptime_int, |
| 15437 | .undefined, |
| 15438 | .null, |
| 15439 | .error_union, |
| 15440 | .error_set, |
| 15441 | .@"fn", |
| 15442 | .@"opaque", |
| 15443 | .frame, |
| 15444 | .@"anyframe", |
| 15445 | .enum_literal, |
| 15446 | .spirv, |
| 15447 | => return sema.fail(block, output_src, "invalid inline assembly output type '{f}'", .{out_ty.fmt(pt)}), |
| 15448 | } |
| 15449 | |
| 15450 | const constraint = sema.code.nullTerminatedString(output.data.constraint); |
| 15451 | needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; |
| 15452 | |
| 15453 | // AstGen gives us a reference to a variable |
| 15454 | if (arg.* != .none and sema.typeOf(arg.*).isConstPtr(zcu)) { |
| 15455 | return sema.fail(block, output_src, "asm cannot output to const '{s}'", .{name}); |
| 15456 | } |
| 15457 | |
| 15458 | outputs[out_i] = .{ .c = constraint, .n = name }; |
| 15459 | } |
| 15460 | |
| 15461 | const args = try sema.arena.alloc(Air.Inst.Ref, inputs_len); |
| 15462 | const inputs = try sema.arena.alloc(ConstraintName, inputs_len); |
| 15463 | |
| 15464 | for (args, 0..) |*arg, arg_i| { |
| 15465 | const input = sema.code.extraData(Zir.Inst.Asm.Input, extra_i); |
| 15466 | const input_src = block.src(.{ .asm_input = .{ |
| 15467 | .offset = src.offset.node_offset.x, |
| 15468 | .input_index = @intCast(arg_i), |
| 15469 | } }); |
| 15470 | extra_i = input.end; |
| 15471 | |
| 15472 | const uncasted_arg = sema.resolveInst(input.data.operand); |
| 15473 | const name = sema.code.nullTerminatedString(input.data.name); |
| 15474 | if (!sema.checkRuntimeValue(uncasted_arg)) { |
| 15475 | const input_name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls); |
| 15476 | return sema.failWithContainsReferenceToComptimeVar(block, input_src, input_name, "assembly input", .fromInterned(uncasted_arg.toInterned().?)); |
| 15477 | } |
| 15478 | const uncasted_arg_ty = sema.typeOf(uncasted_arg); |
| 15479 | switch (uncasted_arg_ty.zigTypeTag(zcu)) { |
| 15480 | .comptime_int => arg.* = try sema.coerce(block, .usize, uncasted_arg, src), |
| 15481 | .comptime_float => arg.* = try sema.coerce(block, .f64, uncasted_arg, src), |
| 15482 | else => { |
| 15483 | arg.* = uncasted_arg; |
| 15484 | }, |
| 15485 | } |
| 15486 | |
| 15487 | const constraint = sema.code.nullTerminatedString(input.data.constraint); |
| 15488 | if (zcu.getTarget().cpu.arch.isSpirV() and std.mem.eql(u8, constraint, "c")) { |
| 15489 | const val = sema.resolveValue(arg.*) orelse { |
| 15490 | return sema.fail(block, input_src, "assembly input with 'c' constraint must be compile-time known", .{}); |
| 15491 | }; |
| 15492 | if (val.isUndef(zcu)) { |
| 15493 | return sema.fail(block, input_src, "assembly input with 'c' constraint cannot be undefined", .{}); |
| 15494 | } |
| 15495 | const bad_type: bool = switch (uncasted_arg_ty.zigTypeTag(zcu)) { |
| 15496 | .bool, .int, .float, .comptime_int, .comptime_float, .enum_literal => false, |
| 15497 | .vector => switch (uncasted_arg_ty.childType(zcu).zigTypeTag(zcu)) { |
| 15498 | .bool, .int, .float => false, |
| 15499 | else => true, |
| 15500 | }, |
| 15501 | else => true, |
| 15502 | }; |
| 15503 | if (bad_type) return sema.fail(block, input_src, "unsupported type '{f}' for 'c' constraint", .{uncasted_arg_ty.fmt(pt)}); |
| 15504 | } |
| 15505 | needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; |
| 15506 | inputs[arg_i] = .{ .c = constraint, .n = name }; |
| 15507 | } |
| 15508 | |
| 15509 | const clobbers_src = block.src(.{ .asm_clobbers = src.offset.node_offset.x }); |
| 15510 | const clobbers_ty = try sema.getStdLangType(src, .@"assembly.Clobbers"); |
| 15511 | const clobbers = if (extra.data.clobbers == .none) empty: { |
| 15512 | break :empty try sema.structInitEmpty(block, clobbers_ty, src, src); |
| 15513 | } else clobbers: { |
| 15514 | const uncoerced = sema.resolveInst(extra.data.clobbers); |
| 15515 | break :clobbers try sema.coerce(block, clobbers_ty, uncoerced, clobbers_src); |
| 15516 | }; |
| 15517 | const clobbers_val = try sema.resolveConstDefinedValue(block, clobbers_src, clobbers, .{ .simple = .clobber }); |
| 15518 | needed_capacity += asm_source.len / 4 + 1; |
| 15519 | |
| 15520 | try sema.air_extra.ensureUnusedCapacity(gpa, needed_capacity); |
| 15521 | const asm_air = try block.addInst(.{ |
| 15522 | .tag = .assembly, |
| 15523 | .data = .{ .ty_pl = .{ |
| 15524 | .ty = expr_ty, |
| 15525 | .payload = sema.addExtraAssumeCapacity(Air.Asm{ |
| 15526 | .source_len = @intCast(asm_source.len), |
| 15527 | .inputs_len = @intCast(args.len), |
| 15528 | .clobbers = clobbers_val.toIntern(), |
| 15529 | .flags = .{ |
| 15530 | .is_volatile = is_volatile, |
| 15531 | .outputs_len = outputs_len, |
| 15532 | }, |
| 15533 | }), |
| 15534 | } }, |
| 15535 | }); |
| 15536 | sema.appendRefsAssumeCapacity(out_args); |
| 15537 | sema.appendRefsAssumeCapacity(args); |
| 15538 | { |
| 15539 | const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); |
| 15540 | @memcpy(buffer[0..asm_source.len], asm_source); |
| 15541 | buffer[asm_source.len] = 0; |
| 15542 | sema.air_extra.items.len += asm_source.len / 4 + 1; |
| 15543 | } |
| 15544 | for (outputs) |o| { |
| 15545 | const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); |
| 15546 | @memcpy(buffer[0..o.c.len], o.c); |
| 15547 | buffer[o.c.len] = 0; |
| 15548 | @memcpy(buffer[o.c.len + 1 ..][0..o.n.len], o.n); |
| 15549 | buffer[o.c.len + 1 + o.n.len] = 0; |
| 15550 | sema.air_extra.items.len += (o.c.len + o.n.len + (2 + 3)) / 4; |
| 15551 | } |
| 15552 | for (inputs) |input| { |
| 15553 | const buffer = mem.sliceAsBytes(sema.air_extra.unusedCapacitySlice()); |
| 15554 | @memcpy(buffer[0..input.c.len], input.c); |
| 15555 | buffer[input.c.len] = 0; |
| 15556 | @memcpy(buffer[input.c.len + 1 ..][0..input.n.len], input.n); |
| 15557 | buffer[input.c.len + 1 + input.n.len] = 0; |
| 15558 | sema.air_extra.items.len += (input.c.len + input.n.len + (2 + 3)) / 4; |
| 15559 | } |
| 15560 | if (try expr_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 15561 | return asm_air; |
| 15562 | } |
| 15563 | |
| 15564 | /// Only called for equality operators. See also `zirCmp`. |
| 15565 | fn zirCmpEq( |
| 15566 | sema: *Sema, |
| 15567 | block: *Block, |
| 15568 | inst: Zir.Inst.Index, |
| 15569 | op: std.math.CompareOperator, |
| 15570 | air_tag: Air.Inst.Tag, |
| 15571 | ) CompileError!Air.Inst.Ref { |
| 15572 | const pt = sema.pt; |
| 15573 | const zcu = pt.zcu; |
| 15574 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 15575 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 15576 | const src: LazySrcLoc = block.nodeOffset(inst_data.src_node); |
| 15577 | const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); |
| 15578 | const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); |
| 15579 | const lhs = sema.resolveInst(extra.lhs); |
| 15580 | const rhs = sema.resolveInst(extra.rhs); |
| 15581 | |
| 15582 | const lhs_ty = sema.typeOf(lhs); |
| 15583 | const rhs_ty = sema.typeOf(rhs); |
| 15584 | const lhs_ty_tag = lhs_ty.zigTypeTag(zcu); |
| 15585 | const rhs_ty_tag = rhs_ty.zigTypeTag(zcu); |
| 15586 | if (lhs_ty_tag == .null and rhs_ty_tag == .null) { |
| 15587 | // null == null, null != null |
| 15588 | return if (op == .eq) .bool_true else .bool_false; |
| 15589 | } |
| 15590 | |
| 15591 | // comparing null with optionals |
| 15592 | if (lhs_ty_tag == .null and (rhs_ty_tag == .optional or rhs_ty.isCPtr(zcu))) { |
| 15593 | return sema.analyzeIsNull(block, src, rhs, op == .neq); |
| 15594 | } |
| 15595 | if (rhs_ty_tag == .null and (lhs_ty_tag == .optional or lhs_ty.isCPtr(zcu))) { |
| 15596 | return sema.analyzeIsNull(block, src, lhs, op == .neq); |
| 15597 | } |
| 15598 | |
| 15599 | if (lhs_ty_tag == .null or rhs_ty_tag == .null) { |
| 15600 | const non_null_type = if (lhs_ty_tag == .null) rhs_ty else lhs_ty; |
| 15601 | return sema.fail(block, src, "comparison of '{f}' with null", .{non_null_type.fmt(pt)}); |
| 15602 | } |
| 15603 | |
| 15604 | if (lhs_ty_tag == .@"union" and (rhs_ty_tag == .enum_literal or rhs_ty_tag == .@"enum")) { |
| 15605 | return sema.analyzeCmpUnionTag(block, src, lhs, lhs_src, rhs, rhs_src, op); |
| 15606 | } |
| 15607 | if (rhs_ty_tag == .@"union" and (lhs_ty_tag == .enum_literal or lhs_ty_tag == .@"enum")) { |
| 15608 | return sema.analyzeCmpUnionTag(block, src, rhs, rhs_src, lhs, lhs_src, op); |
| 15609 | } |
| 15610 | |
| 15611 | if (lhs_ty_tag == .error_set and rhs_ty_tag == .error_set) { |
| 15612 | const runtime_src: LazySrcLoc = src: { |
| 15613 | if (sema.resolveValue(lhs)) |lval| { |
| 15614 | if (sema.resolveValue(rhs)) |rval| { |
| 15615 | if (lval.isUndef(zcu) or rval.isUndef(zcu)) return .undef_bool; |
| 15616 | const lkey = zcu.intern_pool.indexToKey(lval.toIntern()); |
| 15617 | const rkey = zcu.intern_pool.indexToKey(rval.toIntern()); |
| 15618 | return if ((lkey.err.name == rkey.err.name) == (op == .eq)) |
| 15619 | .bool_true |
| 15620 | else |
| 15621 | .bool_false; |
| 15622 | } else { |
| 15623 | break :src rhs_src; |
| 15624 | } |
| 15625 | } else { |
| 15626 | break :src lhs_src; |
| 15627 | } |
| 15628 | }; |
| 15629 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 15630 | return block.addBinOp(air_tag, lhs, rhs); |
| 15631 | } |
| 15632 | if (lhs_ty_tag == .type and rhs_ty_tag == .type) { |
| 15633 | const lhs_as_type = try sema.analyzeAsType(block, lhs_src, .type, lhs); |
| 15634 | const rhs_as_type = try sema.analyzeAsType(block, rhs_src, .type, rhs); |
| 15635 | return if (lhs_as_type.eql(rhs_as_type) == (op == .eq)) .bool_true else .bool_false; |
| 15636 | } |
| 15637 | return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, true); |
| 15638 | } |
| 15639 | |
| 15640 | fn analyzeCmpUnionTag( |
| 15641 | sema: *Sema, |
| 15642 | block: *Block, |
| 15643 | src: LazySrcLoc, |
| 15644 | un: Air.Inst.Ref, |
| 15645 | un_src: LazySrcLoc, |
| 15646 | tag: Air.Inst.Ref, |
| 15647 | tag_src: LazySrcLoc, |
| 15648 | op: std.math.CompareOperator, |
| 15649 | ) CompileError!Air.Inst.Ref { |
| 15650 | const pt = sema.pt; |
| 15651 | const zcu = pt.zcu; |
| 15652 | const union_ty = sema.typeOf(un); |
| 15653 | const union_tag_ty = union_ty.unionTagType(zcu) orelse { |
| 15654 | const msg = msg: { |
| 15655 | const msg = try sema.errMsg(un_src, "comparison of union and enum literal is only valid for tagged union types", .{}); |
| 15656 | errdefer msg.destroy(sema.gpa); |
| 15657 | try sema.errNote(union_ty.srcLoc(zcu), msg, "union '{f}' is not a tagged union", .{union_ty.fmt(pt)}); |
| 15658 | break :msg msg; |
| 15659 | }; |
| 15660 | return sema.failWithOwnedErrorMsg(block, msg); |
| 15661 | }; |
| 15662 | // Coerce both the union and the tag to the union's tag type, and then execute the |
| 15663 | // enum comparison codepath. |
| 15664 | const coerced_tag = try sema.coerce(block, union_tag_ty, tag, tag_src); |
| 15665 | const coerced_union = try sema.coerce(block, union_tag_ty, un, un_src); |
| 15666 | |
| 15667 | if (sema.resolveValue(coerced_tag)) |enum_val| { |
| 15668 | if (enum_val.isUndef(zcu)) return .undef_bool; |
| 15669 | const field_ty = union_ty.unionFieldType(enum_val, zcu).?; |
| 15670 | if (field_ty.classify(zcu) == .no_possible_value) { |
| 15671 | return .bool_false; |
| 15672 | } |
| 15673 | } |
| 15674 | |
| 15675 | return sema.cmpSelf(block, src, coerced_union, coerced_tag, op, un_src, tag_src); |
| 15676 | } |
| 15677 | |
| 15678 | /// Only called for non-equality operators. See also `zirCmpEq`. |
| 15679 | fn zirCmp( |
| 15680 | sema: *Sema, |
| 15681 | block: *Block, |
| 15682 | inst: Zir.Inst.Index, |
| 15683 | op: std.math.CompareOperator, |
| 15684 | ) CompileError!Air.Inst.Ref { |
| 15685 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 15686 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 15687 | const src: LazySrcLoc = block.nodeOffset(inst_data.src_node); |
| 15688 | const lhs_src = block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); |
| 15689 | const rhs_src = block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); |
| 15690 | const lhs = sema.resolveInst(extra.lhs); |
| 15691 | const rhs = sema.resolveInst(extra.rhs); |
| 15692 | return sema.analyzeCmp(block, src, lhs, rhs, op, lhs_src, rhs_src, false); |
| 15693 | } |
| 15694 | |
| 15695 | fn analyzeCmp( |
| 15696 | sema: *Sema, |
| 15697 | block: *Block, |
| 15698 | src: LazySrcLoc, |
| 15699 | lhs: Air.Inst.Ref, |
| 15700 | rhs: Air.Inst.Ref, |
| 15701 | op: std.math.CompareOperator, |
| 15702 | lhs_src: LazySrcLoc, |
| 15703 | rhs_src: LazySrcLoc, |
| 15704 | is_equality_cmp: bool, |
| 15705 | ) CompileError!Air.Inst.Ref { |
| 15706 | const pt = sema.pt; |
| 15707 | const zcu = pt.zcu; |
| 15708 | const lhs_ty = sema.typeOf(lhs); |
| 15709 | const rhs_ty = sema.typeOf(rhs); |
| 15710 | if (lhs_ty.zigTypeTag(zcu) != .optional and rhs_ty.zigTypeTag(zcu) != .optional) { |
| 15711 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 15712 | } |
| 15713 | |
| 15714 | if (lhs_ty.zigTypeTag(zcu) == .vector and rhs_ty.zigTypeTag(zcu) == .vector) { |
| 15715 | return sema.cmpVector(block, src, lhs, rhs, op, lhs_src, rhs_src); |
| 15716 | } |
| 15717 | if (lhs_ty.isNumeric(zcu) and rhs_ty.isNumeric(zcu)) { |
| 15718 | // This operation allows any combination of integer and float types, regardless of the |
| 15719 | // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for |
| 15720 | // numeric types. |
| 15721 | return sema.cmpNumeric(block, src, lhs, rhs, op, lhs_src, rhs_src); |
| 15722 | } |
| 15723 | if (is_equality_cmp and lhs_ty.zigTypeTag(zcu) == .error_union and rhs_ty.zigTypeTag(zcu) == .error_set) { |
| 15724 | if (try sema.resolveDefinedValue(block, lhs_src, lhs)) |lhs_val| { |
| 15725 | if (lhs_val.errorUnionIsPayload(zcu)) return .bool_false; |
| 15726 | } |
| 15727 | const casted_lhs = try sema.analyzeErrUnionCode(block, lhs_src, lhs); |
| 15728 | return sema.cmpSelf(block, src, casted_lhs, rhs, op, lhs_src, rhs_src); |
| 15729 | } |
| 15730 | if (is_equality_cmp and lhs_ty.zigTypeTag(zcu) == .error_set and rhs_ty.zigTypeTag(zcu) == .error_union) { |
| 15731 | if (try sema.resolveDefinedValue(block, rhs_src, rhs)) |rhs_val| { |
| 15732 | if (rhs_val.errorUnionIsPayload(zcu)) return .bool_false; |
| 15733 | } |
| 15734 | const casted_rhs = try sema.analyzeErrUnionCode(block, rhs_src, rhs); |
| 15735 | return sema.cmpSelf(block, src, lhs, casted_rhs, op, lhs_src, rhs_src); |
| 15736 | } |
| 15737 | const instructions = &[_]Air.Inst.Ref{ lhs, rhs }; |
| 15738 | const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{ .override = &[_]?LazySrcLoc{ lhs_src, rhs_src } }); |
| 15739 | if (!resolved_type.isSelfComparable(zcu, is_equality_cmp)) { |
| 15740 | return sema.fail(block, src, "operator {s} not allowed for type '{f}'", .{ |
| 15741 | compareOperatorName(op), resolved_type.fmt(pt), |
| 15742 | }); |
| 15743 | } |
| 15744 | const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src); |
| 15745 | const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src); |
| 15746 | return sema.cmpSelf(block, src, casted_lhs, casted_rhs, op, lhs_src, rhs_src); |
| 15747 | } |
| 15748 | |
| 15749 | fn compareOperatorName(comp: std.math.CompareOperator) []const u8 { |
| 15750 | return switch (comp) { |
| 15751 | .lt => "<", |
| 15752 | .lte => "<=", |
| 15753 | .eq => "==", |
| 15754 | .gte => ">=", |
| 15755 | .gt => ">", |
| 15756 | .neq => "!=", |
| 15757 | }; |
| 15758 | } |
| 15759 | |
| 15760 | fn cmpSelf( |
| 15761 | sema: *Sema, |
| 15762 | block: *Block, |
| 15763 | src: LazySrcLoc, |
| 15764 | casted_lhs: Air.Inst.Ref, |
| 15765 | casted_rhs: Air.Inst.Ref, |
| 15766 | op: std.math.CompareOperator, |
| 15767 | lhs_src: LazySrcLoc, |
| 15768 | rhs_src: LazySrcLoc, |
| 15769 | ) CompileError!Air.Inst.Ref { |
| 15770 | const pt = sema.pt; |
| 15771 | const zcu = pt.zcu; |
| 15772 | const resolved_type = sema.typeOf(casted_lhs); |
| 15773 | |
| 15774 | const maybe_lhs_val = sema.resolveValue(casted_lhs); |
| 15775 | const maybe_rhs_val = sema.resolveValue(casted_rhs); |
| 15776 | if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return .undef_bool; |
| 15777 | if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return .undef_bool; |
| 15778 | |
| 15779 | const runtime_src: LazySrcLoc = src: { |
| 15780 | if (maybe_lhs_val) |lhs_val| { |
| 15781 | if (maybe_rhs_val) |rhs_val| { |
| 15782 | if (resolved_type.zigTypeTag(zcu) == .vector) { |
| 15783 | const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_type); |
| 15784 | return Air.internedToRef(cmp_val.toIntern()); |
| 15785 | } |
| 15786 | |
| 15787 | return if (try sema.compareAll(lhs_val, op, rhs_val, resolved_type)) |
| 15788 | .bool_true |
| 15789 | else |
| 15790 | .bool_false; |
| 15791 | } else { |
| 15792 | if (resolved_type.zigTypeTag(zcu) == .bool) { |
| 15793 | // We can lower bool eq/neq more efficiently. |
| 15794 | return sema.runtimeBoolCmp(block, src, op, casted_rhs, lhs_val.toBool(), rhs_src); |
| 15795 | } |
| 15796 | break :src rhs_src; |
| 15797 | } |
| 15798 | } else { |
| 15799 | // For bools, we still check the other operand, because we can lower |
| 15800 | // bool eq/neq more efficiently. |
| 15801 | if (resolved_type.zigTypeTag(zcu) == .bool) { |
| 15802 | if (maybe_rhs_val) |rhs_val| { |
| 15803 | return sema.runtimeBoolCmp(block, src, op, casted_lhs, rhs_val.toBool(), lhs_src); |
| 15804 | } |
| 15805 | } |
| 15806 | break :src lhs_src; |
| 15807 | } |
| 15808 | }; |
| 15809 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 15810 | if (resolved_type.zigTypeTag(zcu) == .vector) { |
| 15811 | return block.addCmpVector(casted_lhs, casted_rhs, op); |
| 15812 | } |
| 15813 | const tag = Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized); |
| 15814 | return block.addBinOp(tag, casted_lhs, casted_rhs); |
| 15815 | } |
| 15816 | |
| 15817 | /// cmp_eq (x, false) => not(x) |
| 15818 | /// cmp_eq (x, true ) => x |
| 15819 | /// cmp_neq(x, false) => x |
| 15820 | /// cmp_neq(x, true ) => not(x) |
| 15821 | fn runtimeBoolCmp( |
| 15822 | sema: *Sema, |
| 15823 | block: *Block, |
| 15824 | src: LazySrcLoc, |
| 15825 | op: std.math.CompareOperator, |
| 15826 | lhs: Air.Inst.Ref, |
| 15827 | rhs: bool, |
| 15828 | runtime_src: LazySrcLoc, |
| 15829 | ) CompileError!Air.Inst.Ref { |
| 15830 | if ((op == .neq) == rhs) { |
| 15831 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 15832 | return block.addTyOp(.not, .bool, lhs); |
| 15833 | } else { |
| 15834 | return lhs; |
| 15835 | } |
| 15836 | } |
| 15837 | |
| 15838 | fn zirSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 15839 | const pt = sema.pt; |
| 15840 | const zcu = pt.zcu; |
| 15841 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 15842 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 15843 | const ty = try sema.resolveType(block, operand_src, inst_data.operand); |
| 15844 | try sema.ensureLayoutResolved(ty, operand_src, .size_of); |
| 15845 | switch (ty.classify(zcu)) { |
| 15846 | .no_possible_value, |
| 15847 | => return sema.fail(block, operand_src, "no size available for uninstantiable type '{f}'", .{ty.fmt(pt)}), |
| 15848 | |
| 15849 | .partially_comptime, |
| 15850 | .fully_comptime, |
| 15851 | => return sema.fail(block, operand_src, "no size available for comptime-only type '{f}'", .{ty.fmt(pt)}), |
| 15852 | |
| 15853 | .one_possible_value => { |
| 15854 | assert(ty.abiSize(zcu) == 0); |
| 15855 | return .zero; |
| 15856 | }, |
| 15857 | |
| 15858 | .runtime => return .fromValue(try pt.intValue(.comptime_int, ty.abiSize(zcu))), |
| 15859 | } |
| 15860 | } |
| 15861 | |
| 15862 | fn zirBitSizeOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 15863 | const pt = sema.pt; |
| 15864 | const zcu = pt.zcu; |
| 15865 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 15866 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 15867 | const operand_ty = try sema.resolveType(block, operand_src, inst_data.operand); |
| 15868 | if (!operand_ty.hasBitRepresentation(zcu) and |
| 15869 | // TODO: allow these types too for now because this is used in some places. We need to |
| 15870 | // figure out whether we think errors and auto-enums have bit representations! |
| 15871 | operand_ty.zigTypeTag(zcu) != .error_set and |
| 15872 | operand_ty.zigTypeTag(zcu) != .@"enum") |
| 15873 | { |
| 15874 | return sema.fail(block, operand_src, "no bit size available for type '{f}'", .{operand_ty.fmt(pt)}); |
| 15875 | } |
| 15876 | try sema.ensureLayoutResolved(operand_ty, operand_src, .size_of); |
| 15877 | return .fromValue(try pt.intValue(.comptime_int, operand_ty.bitSize(zcu))); |
| 15878 | } |
| 15879 | |
| 15880 | fn zirThis( |
| 15881 | sema: *Sema, |
| 15882 | block: *Block, |
| 15883 | extended: Zir.Inst.Extended.InstData, |
| 15884 | ) CompileError!Air.Inst.Ref { |
| 15885 | _ = extended; |
| 15886 | return .fromIntern(sema.pt.zcu.namespacePtr(block.namespace).owner_type); |
| 15887 | } |
| 15888 | |
| 15889 | fn zirClosureGet(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 15890 | const pt = sema.pt; |
| 15891 | const zcu = pt.zcu; |
| 15892 | const ip = &zcu.intern_pool; |
| 15893 | const captures = Type.fromInterned(zcu.namespacePtr(block.namespace).owner_type).getCaptures(zcu); |
| 15894 | |
| 15895 | const src_node: std.zig.Ast.Node.Offset = @fromBackingInt(@intCast(@as(i32, @bitCast(extended.operand)))); |
| 15896 | const src = block.nodeOffset(src_node); |
| 15897 | |
| 15898 | const capture_ty = switch (captures.get(ip)[extended.small].unwrap()) { |
| 15899 | .@"comptime" => |index| return Air.internedToRef(index), |
| 15900 | .runtime => |index| index, |
| 15901 | .nav_val => |nav| return sema.analyzeNavVal(block, src, nav), |
| 15902 | .nav_ref => |nav| return sema.analyzeNavRef(block, src, nav), |
| 15903 | }; |
| 15904 | |
| 15905 | // The comptime case is handled already above. Runtime case below. |
| 15906 | |
| 15907 | if (!block.is_typeof and sema.func_index == .none) { |
| 15908 | const msg = msg: { |
| 15909 | const name = name: { |
| 15910 | // TODO: we should probably store this name in the ZIR to avoid this complexity. |
| 15911 | const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?; |
| 15912 | const tree = file.getTree(zcu) catch |err| { |
| 15913 | // In this case we emit a warning + a less precise source location. |
| 15914 | log.warn("unable to load {f}: {s}", .{ |
| 15915 | file.path.fmt(zcu.comp), @errorName(err), |
| 15916 | }); |
| 15917 | break :name null; |
| 15918 | }; |
| 15919 | const node = src_node.toAbsolute(src_base_node); |
| 15920 | const token = tree.nodeMainToken(node); |
| 15921 | break :name tree.tokenSlice(token); |
| 15922 | }; |
| 15923 | |
| 15924 | const msg = if (name) |some| |
| 15925 | try sema.errMsg(src, "'{s}' not accessible outside function scope", .{some}) |
| 15926 | else |
| 15927 | try sema.errMsg(src, "variable not accessible outside function scope", .{}); |
| 15928 | errdefer msg.destroy(sema.gpa); |
| 15929 | |
| 15930 | // TODO add "declared here" note |
| 15931 | break :msg msg; |
| 15932 | }; |
| 15933 | return sema.failWithOwnedErrorMsg(block, msg); |
| 15934 | } |
| 15935 | |
| 15936 | if (!block.is_typeof and !block.isComptime() and sema.func_index != .none) { |
| 15937 | const msg = msg: { |
| 15938 | const name = name: { |
| 15939 | const file, const src_base_node = Zcu.LazySrcLoc.resolveBaseNode(block.src_base_inst, zcu).?; |
| 15940 | const tree = file.getTree(zcu) catch |err| { |
| 15941 | // In this case we emit a warning + a less precise source location. |
| 15942 | log.warn("unable to load {f}: {s}", .{ |
| 15943 | file.path.fmt(zcu.comp), @errorName(err), |
| 15944 | }); |
| 15945 | break :name null; |
| 15946 | }; |
| 15947 | const node = src_node.toAbsolute(src_base_node); |
| 15948 | const token = tree.nodeMainToken(node); |
| 15949 | break :name tree.tokenSlice(token); |
| 15950 | }; |
| 15951 | |
| 15952 | const msg = if (name) |some| |
| 15953 | try sema.errMsg(src, "'{s}' not accessible from inner function", .{some}) |
| 15954 | else |
| 15955 | try sema.errMsg(src, "variable not accessible from inner function", .{}); |
| 15956 | errdefer msg.destroy(sema.gpa); |
| 15957 | |
| 15958 | try sema.errNote(block.nodeOffset(.zero), msg, "crossed function definition here", .{}); |
| 15959 | |
| 15960 | // TODO add "declared here" note |
| 15961 | break :msg msg; |
| 15962 | }; |
| 15963 | return sema.failWithOwnedErrorMsg(block, msg); |
| 15964 | } |
| 15965 | |
| 15966 | assert(block.is_typeof); |
| 15967 | // We need a dummy runtime instruction with the correct type. |
| 15968 | return block.addTy(.alloc, .fromInterned(capture_ty)); |
| 15969 | } |
| 15970 | |
| 15971 | fn zirRetAddr( |
| 15972 | sema: *Sema, |
| 15973 | block: *Block, |
| 15974 | extended: Zir.Inst.Extended.InstData, |
| 15975 | ) CompileError!Air.Inst.Ref { |
| 15976 | _ = sema; |
| 15977 | _ = extended; |
| 15978 | if (block.isComptime()) { |
| 15979 | // TODO: we could give a meaningful value here. #14938 |
| 15980 | return .zero_usize; |
| 15981 | } else { |
| 15982 | return block.addNoOp(.ret_addr); |
| 15983 | } |
| 15984 | } |
| 15985 | |
| 15986 | fn zirFrameAddress( |
| 15987 | sema: *Sema, |
| 15988 | block: *Block, |
| 15989 | extended: Zir.Inst.Extended.InstData, |
| 15990 | ) CompileError!Air.Inst.Ref { |
| 15991 | const src_node: std.zig.Ast.Node.Offset = @fromBackingInt(@intCast(@as(i32, @bitCast(extended.operand)))); |
| 15992 | const src = block.nodeOffset(src_node); |
| 15993 | try sema.requireRuntimeBlock(block, src, null); |
| 15994 | return try block.addNoOp(.frame_addr); |
| 15995 | } |
| 15996 | |
| 15997 | fn zirBuiltinSrc( |
| 15998 | sema: *Sema, |
| 15999 | block: *Block, |
| 16000 | extended: Zir.Inst.Extended.InstData, |
| 16001 | ) CompileError!Air.Inst.Ref { |
| 16002 | const pt = sema.pt; |
| 16003 | const zcu = pt.zcu; |
| 16004 | const comp = zcu.comp; |
| 16005 | const gpa = comp.gpa; |
| 16006 | const io = comp.io; |
| 16007 | const ip = &zcu.intern_pool; |
| 16008 | |
| 16009 | const extra = sema.code.extraData(Zir.Inst.Src, extended.operand).data; |
| 16010 | const fn_name = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).name; |
| 16011 | const file_scope = block.getFileScope(zcu); |
| 16012 | |
| 16013 | const func_name_val = v: { |
| 16014 | const func_name_len = fn_name.length(ip); |
| 16015 | const array_ty = try pt.intern(.{ .array_type = .{ |
| 16016 | .len = func_name_len, |
| 16017 | .sentinel = .zero_u8, |
| 16018 | .child = .u8_type, |
| 16019 | } }); |
| 16020 | break :v try pt.intern(.{ .slice = .{ |
| 16021 | .ty = .slice_const_u8_sentinel_0_type, |
| 16022 | .ptr = try pt.intern(.{ .ptr = .{ |
| 16023 | .ty = .manyptr_const_u8_sentinel_0_type, |
| 16024 | .base_addr = .{ .uav = .{ |
| 16025 | .orig_ty = .slice_const_u8_sentinel_0_type, |
| 16026 | .val = try pt.intern(.{ .aggregate = .{ |
| 16027 | .ty = array_ty, |
| 16028 | .storage = .{ .bytes = fn_name.toString() }, |
| 16029 | } }), |
| 16030 | } }, |
| 16031 | .byte_offset = 0, |
| 16032 | } }), |
| 16033 | .len = (try pt.intValue(.usize, func_name_len)).toIntern(), |
| 16034 | } }); |
| 16035 | }; |
| 16036 | |
| 16037 | const module_name_val = v: { |
| 16038 | const module_name = file_scope.mod.?.fully_qualified_name; |
| 16039 | const array_ty = try pt.intern(.{ .array_type = .{ |
| 16040 | .len = module_name.len, |
| 16041 | .sentinel = .zero_u8, |
| 16042 | .child = .u8_type, |
| 16043 | } }); |
| 16044 | break :v try pt.intern(.{ .slice = .{ |
| 16045 | .ty = .slice_const_u8_sentinel_0_type, |
| 16046 | .ptr = try pt.intern(.{ .ptr = .{ |
| 16047 | .ty = .manyptr_const_u8_sentinel_0_type, |
| 16048 | .base_addr = .{ .uav = .{ |
| 16049 | .orig_ty = .slice_const_u8_sentinel_0_type, |
| 16050 | .val = try pt.intern(.{ .aggregate = .{ |
| 16051 | .ty = array_ty, |
| 16052 | .storage = .{ |
| 16053 | .bytes = try ip.getOrPutString(gpa, io, pt.tid, module_name, .maybe_embedded_nulls), |
| 16054 | }, |
| 16055 | } }), |
| 16056 | } }, |
| 16057 | .byte_offset = 0, |
| 16058 | } }), |
| 16059 | .len = (try pt.intValue(.usize, module_name.len)).toIntern(), |
| 16060 | } }); |
| 16061 | }; |
| 16062 | |
| 16063 | const file_name_val = v: { |
| 16064 | const file_name = file_scope.sub_file_path; |
| 16065 | const array_ty = try pt.intern(.{ .array_type = .{ |
| 16066 | .len = file_name.len, |
| 16067 | .sentinel = .zero_u8, |
| 16068 | .child = .u8_type, |
| 16069 | } }); |
| 16070 | break :v try pt.intern(.{ .slice = .{ |
| 16071 | .ty = .slice_const_u8_sentinel_0_type, |
| 16072 | .ptr = try pt.intern(.{ .ptr = .{ |
| 16073 | .ty = .manyptr_const_u8_sentinel_0_type, |
| 16074 | .base_addr = .{ .uav = .{ |
| 16075 | .orig_ty = .slice_const_u8_sentinel_0_type, |
| 16076 | .val = try pt.intern(.{ .aggregate = .{ |
| 16077 | .ty = array_ty, |
| 16078 | .storage = .{ |
| 16079 | .bytes = try ip.getOrPutString(gpa, io, pt.tid, file_name, .maybe_embedded_nulls), |
| 16080 | }, |
| 16081 | } }), |
| 16082 | } }, |
| 16083 | .byte_offset = 0, |
| 16084 | } }), |
| 16085 | .len = (try pt.intValue(.usize, file_name.len)).toIntern(), |
| 16086 | } }); |
| 16087 | }; |
| 16088 | |
| 16089 | const src_loc_ty = try sema.getStdLangType(block.nodeOffset(.zero), .SourceLocation); |
| 16090 | const fields = .{ |
| 16091 | // module: [:0]const u8, |
| 16092 | module_name_val, |
| 16093 | // file: [:0]const u8, |
| 16094 | file_name_val, |
| 16095 | // fn_name: [:0]const u8, |
| 16096 | func_name_val, |
| 16097 | // line: u32, |
| 16098 | (try pt.intValue(.u32, extra.line + 1)).toIntern(), |
| 16099 | // column: u32, |
| 16100 | (try pt.intValue(.u32, extra.column + 1)).toIntern(), |
| 16101 | }; |
| 16102 | return Air.internedToRef((try pt.aggregateValue(src_loc_ty, &fields)).toIntern()); |
| 16103 | } |
| 16104 | |
| 16105 | fn zirTypeInfo(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 16106 | const pt = sema.pt; |
| 16107 | const zcu = pt.zcu; |
| 16108 | const comp = zcu.comp; |
| 16109 | const gpa = comp.gpa; |
| 16110 | const io = comp.io; |
| 16111 | const ip = &zcu.intern_pool; |
| 16112 | |
| 16113 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 16114 | const src = block.nodeOffset(inst_data.src_node); |
| 16115 | const ty = try sema.resolveType(block, src, inst_data.operand); |
| 16116 | const type_info_ty = try sema.getStdLangType(src, .Type); |
| 16117 | const type_info_tag_ty = type_info_ty.unionTagType(zcu).?; |
| 16118 | |
| 16119 | try sema.ensureLayoutResolved(ty, src, .type_info); |
| 16120 | |
| 16121 | if (ty.typeDeclInst(zcu)) |type_decl_inst| { |
| 16122 | try sema.declareDependency(.{ .namespace = type_decl_inst }); |
| 16123 | } |
| 16124 | |
| 16125 | switch (ty.zigTypeTag(zcu)) { |
| 16126 | .type, |
| 16127 | .void, |
| 16128 | .bool, |
| 16129 | .noreturn, |
| 16130 | .comptime_float, |
| 16131 | .comptime_int, |
| 16132 | .undefined, |
| 16133 | .null, |
| 16134 | .enum_literal, |
| 16135 | => |type_info_tag| return .fromValue(try pt.unionValue( |
| 16136 | type_info_ty, |
| 16137 | Value.uninterpret(type_info_tag, type_info_tag_ty, pt) catch |err| switch (err) { |
| 16138 | error.TypeMismatch => @panic("std.lang is corrupt"), |
| 16139 | error.OutOfMemory => |e| return e, |
| 16140 | }, |
| 16141 | .void, |
| 16142 | )), |
| 16143 | |
| 16144 | .@"fn" => { |
| 16145 | const fn_info_ty = try sema.getStdLangType(src, .@"Type.Fn"); |
| 16146 | const param_attrs_ty = try sema.getStdLangType(src, .@"Type.Fn.ParamAttributes"); |
| 16147 | const fn_attr_ty = try sema.getStdLangType(src, .@"Type.Fn.Attributes"); |
| 16148 | |
| 16149 | const func_ty_info = zcu.typeToFunc(ty).?; |
| 16150 | const param_type_vals = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len); |
| 16151 | const param_attr_vals = try sema.arena.alloc(InternPool.Index, func_ty_info.param_types.len); |
| 16152 | var func_is_generic = false; |
| 16153 | |
| 16154 | for ( |
| 16155 | param_type_vals, |
| 16156 | param_attr_vals, |
| 16157 | 0.., |
| 16158 | ) |*param_type_val, *param_attr_val, param_index| { |
| 16159 | const param_ty = func_ty_info.param_types.get(ip)[param_index]; |
| 16160 | const is_generic = param_ty == .generic_poison_type; |
| 16161 | const is_noalias, const is_comptime = flags: { |
| 16162 | const i = std.math.cast(u5, param_index) orelse break :flags .{ false, false }; |
| 16163 | break :flags .{ func_ty_info.paramIsNoalias(i), func_ty_info.paramIsComptime(i) }; |
| 16164 | }; |
| 16165 | |
| 16166 | if (is_generic or is_comptime or Type.fromInterned(param_ty).comptimeOnly(zcu)) { |
| 16167 | func_is_generic = true; |
| 16168 | } |
| 16169 | |
| 16170 | const param_ty_val = try pt.intern(.{ .opt = .{ |
| 16171 | .ty = try pt.intern(.{ .opt_type = .type_type }), |
| 16172 | .val = if (is_generic) .none else param_ty, |
| 16173 | } }); |
| 16174 | |
| 16175 | const param_attrs_fields = .{ |
| 16176 | // @"noalias": bool, |
| 16177 | Value.makeBool(is_noalias).toIntern(), |
| 16178 | }; |
| 16179 | |
| 16180 | param_type_val.* = param_ty_val; |
| 16181 | param_attr_val.* = (try pt.aggregateValue(param_attrs_ty, &param_attrs_fields)).toIntern(); |
| 16182 | } |
| 16183 | |
| 16184 | const param_types_val = v: { |
| 16185 | const new_decl_ty = try pt.arrayType(.{ |
| 16186 | .len = param_type_vals.len, |
| 16187 | .child = try pt.intern(.{ .opt_type = .type_type }), |
| 16188 | }); |
| 16189 | const new_decl_val = (try pt.aggregateValue(new_decl_ty, param_type_vals)).toIntern(); |
| 16190 | const slice_ty = (try pt.ptrType(.{ |
| 16191 | .child = try pt.intern(.{ .opt_type = .type_type }), |
| 16192 | .flags = .{ |
| 16193 | .size = .slice, |
| 16194 | .is_const = true, |
| 16195 | }, |
| 16196 | })).toIntern(); |
| 16197 | const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); |
| 16198 | break :v try pt.intern(.{ .slice = .{ |
| 16199 | .ty = slice_ty, |
| 16200 | .ptr = try pt.intern(.{ .ptr = .{ |
| 16201 | .ty = manyptr_ty, |
| 16202 | .base_addr = .{ .uav = .{ |
| 16203 | .orig_ty = manyptr_ty, |
| 16204 | .val = new_decl_val, |
| 16205 | } }, |
| 16206 | .byte_offset = 0, |
| 16207 | } }), |
| 16208 | .len = (try pt.intValue(.usize, param_type_vals.len)).toIntern(), |
| 16209 | } }); |
| 16210 | }; |
| 16211 | const param_attrs_val = v: { |
| 16212 | const new_decl_ty = try pt.arrayType(.{ |
| 16213 | .len = param_attr_vals.len, |
| 16214 | .child = param_attrs_ty.toIntern(), |
| 16215 | }); |
| 16216 | const new_decl_val = (try pt.aggregateValue(new_decl_ty, param_attr_vals)).toIntern(); |
| 16217 | const slice_ty = (try pt.ptrType(.{ |
| 16218 | .child = param_attrs_ty.toIntern(), |
| 16219 | .flags = .{ |
| 16220 | .size = .slice, |
| 16221 | .is_const = true, |
| 16222 | }, |
| 16223 | })).toIntern(); |
| 16224 | const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); |
| 16225 | break :v try pt.intern(.{ .slice = .{ |
| 16226 | .ty = slice_ty, |
| 16227 | .ptr = try pt.intern(.{ .ptr = .{ |
| 16228 | .ty = manyptr_ty, |
| 16229 | .base_addr = .{ .uav = .{ |
| 16230 | .orig_ty = manyptr_ty, |
| 16231 | .val = new_decl_val, |
| 16232 | } }, |
| 16233 | .byte_offset = 0, |
| 16234 | } }), |
| 16235 | .len = (try pt.intValue(.usize, param_attr_vals.len)).toIntern(), |
| 16236 | } }); |
| 16237 | }; |
| 16238 | |
| 16239 | const ret_ty_is_generic = generic: { |
| 16240 | const ret_ty: Type = .fromInterned(func_ty_info.return_type); |
| 16241 | if (ret_ty.toIntern() == .generic_poison_type or |
| 16242 | (ret_ty.zigTypeTag(zcu) == .error_union and |
| 16243 | ret_ty.errorUnionPayload(zcu).toIntern() == .generic_poison_type)) |
| 16244 | { |
| 16245 | break :generic true; |
| 16246 | } |
| 16247 | break :generic false; |
| 16248 | }; |
| 16249 | if (ret_ty_is_generic or Type.fromInterned(func_ty_info.return_type).comptimeOnly(zcu)) { |
| 16250 | func_is_generic = true; |
| 16251 | } |
| 16252 | |
| 16253 | const ret_ty_opt = try pt.intern(.{ .opt = .{ |
| 16254 | .ty = try pt.intern(.{ .opt_type = .type_type }), |
| 16255 | .val = if (ret_ty_is_generic) .none else func_ty_info.return_type, |
| 16256 | } }); |
| 16257 | |
| 16258 | const callconv_ty = try sema.getStdLangType(src, .CallingConvention); |
| 16259 | const callconv_val = Value.uninterpret(func_ty_info.cc, callconv_ty, pt) catch |err| switch (err) { |
| 16260 | error.TypeMismatch => @panic("std.lang is corrupt"), |
| 16261 | error.OutOfMemory => |e| return e, |
| 16262 | }; |
| 16263 | |
| 16264 | const fn_attrs_values = .{ |
| 16265 | // @"callconv": CallingConvention = .auto, |
| 16266 | callconv_val.toIntern(), |
| 16267 | // varargs: bool = false, |
| 16268 | Value.makeBool(func_ty_info.is_var_args).toIntern(), |
| 16269 | }; |
| 16270 | |
| 16271 | const field_values = .{ |
| 16272 | // attrs: Attributes, |
| 16273 | (try pt.aggregateValue(fn_attr_ty, &fn_attrs_values)).toIntern(), |
| 16274 | // is_generic: bool, |
| 16275 | Value.makeBool(func_is_generic).toIntern(), |
| 16276 | // return_type: ?type, |
| 16277 | ret_ty_opt, |
| 16278 | |
| 16279 | // param_types: []const ?type, |
| 16280 | param_types_val, |
| 16281 | // param_attrs: []const ParamAttributes, |
| 16282 | param_attrs_val, |
| 16283 | }; |
| 16284 | return Air.internedToRef((try pt.internUnion(.{ |
| 16285 | .ty = type_info_ty.toIntern(), |
| 16286 | .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.@"fn"))).toIntern(), |
| 16287 | .val = (try pt.aggregateValue(fn_info_ty, &field_values)).toIntern(), |
| 16288 | }))); |
| 16289 | }, |
| 16290 | .int => { |
| 16291 | const int_info_ty = try sema.getStdLangType(src, .@"Type.Int"); |
| 16292 | const signedness_ty = try sema.getStdLangType(src, .Signedness); |
| 16293 | const info = ty.intInfo(zcu); |
| 16294 | const field_values = .{ |
| 16295 | // signedness: Signedness, |
| 16296 | (try pt.enumValueFieldIndex(signedness_ty, @backingInt(info.signedness))).toIntern(), |
| 16297 | // bits: u16, |
| 16298 | (try pt.intValue(.u16, info.bits)).toIntern(), |
| 16299 | }; |
| 16300 | return Air.internedToRef((try pt.internUnion(.{ |
| 16301 | .ty = type_info_ty.toIntern(), |
| 16302 | .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.int))).toIntern(), |
| 16303 | .val = (try pt.aggregateValue(int_info_ty, &field_values)).toIntern(), |
| 16304 | }))); |
| 16305 | }, |
| 16306 | .float => { |
| 16307 | const float_info_ty = try sema.getStdLangType(src, .@"Type.Float"); |
| 16308 | |
| 16309 | const field_vals = .{ |
| 16310 | // bits: u16, |
| 16311 | (try pt.intValue(.u16, ty.floatBits(zcu.getTarget()))).toIntern(), |
| 16312 | }; |
| 16313 | return Air.internedToRef((try pt.internUnion(.{ |
| 16314 | .ty = type_info_ty.toIntern(), |
| 16315 | .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.float))).toIntern(), |
| 16316 | .val = (try pt.aggregateValue(float_info_ty, &field_vals)).toIntern(), |
| 16317 | }))); |
| 16318 | }, |
| 16319 | .pointer => { |
| 16320 | const info = ty.ptrInfo(zcu); |
| 16321 | const alignment_ty = try pt.optionalType(.usize_type); |
| 16322 | const alignment_val: Value = val: { |
| 16323 | const bytes = info.flags.alignment.toByteUnits() orelse { |
| 16324 | break :val try pt.nullValue(alignment_ty); |
| 16325 | }; |
| 16326 | const int_val = try pt.intValue(.usize, bytes); |
| 16327 | break :val .fromInterned(try pt.intern(.{ .opt = .{ |
| 16328 | .ty = alignment_ty.toIntern(), |
| 16329 | .val = int_val.toIntern(), |
| 16330 | } })); |
| 16331 | }; |
| 16332 | |
| 16333 | const addrspace_ty = try sema.getStdLangType(src, .AddressSpace); |
| 16334 | const pointer_ty = try sema.getStdLangType(src, .@"Type.Pointer"); |
| 16335 | const ptr_size_ty = try sema.getStdLangType(src, .@"Type.Pointer.Size"); |
| 16336 | const ptr_attrs_ty = try sema.getStdLangType(src, .@"Type.Pointer.Attributes"); |
| 16337 | |
| 16338 | const opt_addrspace_val = try pt.intern(.{ .opt = .{ |
| 16339 | .ty = (try pt.optionalType(addrspace_ty.toIntern())).toIntern(), |
| 16340 | .val = (try sema.uninterpretStdLangType(info.flags.address_space, addrspace_ty)).toIntern(), |
| 16341 | } }); |
| 16342 | |
| 16343 | const attributes = .{ |
| 16344 | // @"const": bool = false, |
| 16345 | Value.makeBool(info.flags.is_const).toIntern(), |
| 16346 | // @"volatile": bool = false, |
| 16347 | Value.makeBool(info.flags.is_volatile).toIntern(), |
| 16348 | // @"allowzero": bool = false, |
| 16349 | Value.makeBool(info.flags.is_allowzero).toIntern(), |
| 16350 | // @"addrspace": ?AddressSpace = null, |
| 16351 | opt_addrspace_val, |
| 16352 | // @"align": ?usize = null, |
| 16353 | alignment_val.toIntern(), |
| 16354 | }; |
| 16355 | |
| 16356 | const field_values = .{ |
| 16357 | // size: Size, |
| 16358 | (try pt.enumValueFieldIndex(ptr_size_ty, @backingInt(info.flags.size))).toIntern(), |
| 16359 | // attrs: Attributes |
| 16360 | (try pt.aggregateValue(ptr_attrs_ty, &attributes)).toIntern(), |
| 16361 | // child: type, |
| 16362 | info.child, |
| 16363 | // sentinel_ptr: ?*const anyopaque, |
| 16364 | (try sema.optRefValue(switch (info.sentinel) { |
| 16365 | .none => null, |
| 16366 | else => Value.fromInterned(info.sentinel), |
| 16367 | })).toIntern(), |
| 16368 | }; |
| 16369 | return Air.internedToRef((try pt.internUnion(.{ |
| 16370 | .ty = type_info_ty.toIntern(), |
| 16371 | .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.pointer))).toIntern(), |
| 16372 | .val = (try pt.aggregateValue(pointer_ty, &field_values)).toIntern(), |
| 16373 | }))); |
| 16374 | }, |
| 16375 | .array => { |
| 16376 | const array_field_ty = try sema.getStdLangType(src, .@"Type.Array"); |
| 16377 | |
| 16378 | const info = ty.arrayInfo(zcu); |
| 16379 | const field_values = .{ |
| 16380 | // len: comptime_int, |
| 16381 | (try pt.intValue(.comptime_int, info.len)).toIntern(), |
| 16382 | // child: type, |
| 16383 | info.elem_type.toIntern(), |
| 16384 | // sentinel: ?*const anyopaque, |
| 16385 | (try sema.optRefValue(info.sentinel)).toIntern(), |
| 16386 | }; |
| 16387 | return Air.internedToRef((try pt.internUnion(.{ |
| 16388 | .ty = type_info_ty.toIntern(), |
| 16389 | .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.array))).toIntern(), |
| 16390 | .val = (try pt.aggregateValue(array_field_ty, &field_values)).toIntern(), |
| 16391 | }))); |
| 16392 | }, |
| 16393 | .vector => { |
| 16394 | const vector_field_ty = try sema.getStdLangType(src, .@"Type.Vector"); |
| 16395 | |
| 16396 | const info = ty.arrayInfo(zcu); |
| 16397 | const field_values = .{ |
| 16398 | // len: comptime_int, |
| 16399 | (try pt.intValue(.comptime_int, info.len)).toIntern(), |
| 16400 | // child: type, |
| 16401 | info.elem_type.toIntern(), |
| 16402 | }; |
| 16403 | return Air.internedToRef((try pt.internUnion(.{ |
| 16404 | .ty = type_info_ty.toIntern(), |
| 16405 | .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.vector))).toIntern(), |
| 16406 | .val = (try pt.aggregateValue(vector_field_ty, &field_values)).toIntern(), |
| 16407 | }))); |
| 16408 | }, |
| 16409 | .optional => { |
| 16410 | const optional_field_ty = try sema.getStdLangType(src, .@"Type.Optional"); |
| 16411 | |
| 16412 | const field_values = .{ |
| 16413 | // child: type, |
| 16414 | ty.optionalChild(zcu).toIntern(), |
| 16415 | }; |
| 16416 | return Air.internedToRef((try pt.internUnion(.{ |
| 16417 | .ty = type_info_ty.toIntern(), |
| 16418 | .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.optional))).toIntern(), |
| 16419 | .val = (try pt.aggregateValue(optional_field_ty, &field_values)).toIntern(), |
| 16420 | }))); |
| 16421 | }, |
| 16422 | .error_set => { |
| 16423 | const error_set_ty = try sema.getStdLangType(src, .@"Type.ErrorSet"); |
| 16424 | |
| 16425 | // Build our list of Error values |
| 16426 | // Optional value is only null if anyerror |
| 16427 | // Value can be zero-length slice otherwise |
| 16428 | const error_field_vals = switch (try sema.resolveInferredErrorSetTy(block, src, ty.toIntern())) { |
| 16429 | .anyerror_type => null, |
| 16430 | else => |err_set_ty_index| blk: { |
| 16431 | const names = ip.indexToKey(err_set_ty_index).error_set_type.names; |
| 16432 | const vals = try sema.arena.alloc(InternPool.Index, names.len); |
| 16433 | for (vals, 0..) |*field_val, error_index| { |
| 16434 | const error_name = names.get(ip)[error_index]; |
| 16435 | const error_name_len = error_name.length(ip); |
| 16436 | const error_name_val = v: { |
| 16437 | const new_decl_ty = try pt.arrayType(.{ |
| 16438 | .len = error_name_len, |
| 16439 | .sentinel = .zero_u8, |
| 16440 | .child = .u8_type, |
| 16441 | }); |
| 16442 | const new_decl_val = try pt.intern(.{ .aggregate = .{ |
| 16443 | .ty = new_decl_ty.toIntern(), |
| 16444 | .storage = .{ .bytes = error_name.toString() }, |
| 16445 | } }); |
| 16446 | break :v try pt.intern(.{ .slice = .{ |
| 16447 | .ty = .slice_const_u8_sentinel_0_type, |
| 16448 | .ptr = try pt.intern(.{ .ptr = .{ |
| 16449 | .ty = .manyptr_const_u8_sentinel_0_type, |
| 16450 | .base_addr = .{ .uav = .{ |
| 16451 | .val = new_decl_val, |
| 16452 | .orig_ty = .slice_const_u8_sentinel_0_type, |
| 16453 | } }, |
| 16454 | .byte_offset = 0, |
| 16455 | } }), |
| 16456 | .len = (try pt.intValue(.usize, error_name_len)).toIntern(), |
| 16457 | } }); |
| 16458 | }; |
| 16459 | |
| 16460 | field_val.* = error_name_val; |
| 16461 | } |
| 16462 | |
| 16463 | break :blk vals; |
| 16464 | }, |
| 16465 | }; |
| 16466 | |
| 16467 | // Build our ?[]const [:0]const u8 value |
| 16468 | const slice_errors_ty = try pt.ptrType(.{ |
| 16469 | .child = .slice_const_u8_sentinel_0_type, |
| 16470 | .flags = .{ |
| 16471 | .size = .slice, |
| 16472 | .is_const = true, |
| 16473 | }, |
| 16474 | }); |
| 16475 | const opt_slice_errors_ty = try pt.optionalType(slice_errors_ty.toIntern()); |
| 16476 | const errors_payload_val: InternPool.Index = if (error_field_vals) |vals| v: { |
| 16477 | const array_errors_ty = try pt.arrayType(.{ |
| 16478 | .len = vals.len, |
| 16479 | .child = .slice_const_u8_sentinel_0_type, |
| 16480 | }); |
| 16481 | const new_decl_val = (try pt.aggregateValue(array_errors_ty, vals)).toIntern(); |
| 16482 | const manyptr_errors_ty = slice_errors_ty.slicePtrFieldType(zcu).toIntern(); |
| 16483 | break :v try pt.intern(.{ .slice = .{ |
| 16484 | .ty = slice_errors_ty.toIntern(), |
| 16485 | .ptr = try pt.intern(.{ .ptr = .{ |
| 16486 | .ty = manyptr_errors_ty, |
| 16487 | .base_addr = .{ .uav = .{ |
| 16488 | .orig_ty = manyptr_errors_ty, |
| 16489 | .val = new_decl_val, |
| 16490 | } }, |
| 16491 | .byte_offset = 0, |
| 16492 | } }), |
| 16493 | .len = (try pt.intValue(.usize, vals.len)).toIntern(), |
| 16494 | } }); |
| 16495 | } else .none; |
| 16496 | const errors_val = try pt.intern(.{ .opt = .{ |
| 16497 | .ty = opt_slice_errors_ty.toIntern(), |
| 16498 | .val = errors_payload_val, |
| 16499 | } }); |
| 16500 | |
| 16501 | const field_values = .{ |
| 16502 | // error_names: ?[]const [:0]const u8 |
| 16503 | errors_val, |
| 16504 | }; |
| 16505 | |
| 16506 | // Construct Type{ .error_set = errors_val } |
| 16507 | return Air.internedToRef((try pt.internUnion(.{ |
| 16508 | .ty = type_info_ty.toIntern(), |
| 16509 | .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.error_set))).toIntern(), |
| 16510 | .val = (try pt.aggregateValue(error_set_ty, &field_values)).toIntern(), |
| 16511 | }))); |
| 16512 | }, |
| 16513 | .error_union => { |
| 16514 | const error_union_field_ty = try sema.getStdLangType(src, .@"Type.ErrorUnion"); |
| 16515 | |
| 16516 | const field_values = .{ |
| 16517 | // error_set: type, |
| 16518 | ty.errorUnionSet(zcu).toIntern(), |
| 16519 | // payload: type, |
| 16520 | ty.errorUnionPayload(zcu).toIntern(), |
| 16521 | }; |
| 16522 | return Air.internedToRef((try pt.internUnion(.{ |
| 16523 | .ty = type_info_ty.toIntern(), |
| 16524 | .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.error_union))).toIntern(), |
| 16525 | .val = (try pt.aggregateValue(error_union_field_ty, &field_values)).toIntern(), |
| 16526 | }))); |
| 16527 | }, |
| 16528 | .@"enum" => { |
| 16529 | const enum_obj = ip.loadEnumType(ty.toIntern()); |
| 16530 | |
| 16531 | const enum_mode_ty = try sema.getStdLangType(src, .@"Type.Enum.Mode"); |
| 16532 | |
| 16533 | const enum_mode_tag: std.builtin.Type.Enum.Mode = if (enum_obj.nonexhaustive) .nonexhaustive else .exhaustive; |
| 16534 | |
| 16535 | const enum_mode: Value = try sema.uninterpretStdLangType(enum_mode_tag, enum_mode_ty); |
| 16536 | |
| 16537 | const enum_field_name_vals = try sema.arena.alloc(InternPool.Index, enum_obj.field_names.len); |
| 16538 | const enum_field_value_vals = try sema.arena.alloc(InternPool.Index, enum_obj.field_names.len); |
| 16539 | for ( |
| 16540 | enum_field_name_vals, |
| 16541 | enum_field_value_vals, |
| 16542 | 0.., |
| 16543 | ) |*field_name_val, *field_value_val, tag_index| { |
| 16544 | const value_val = if (enum_obj.field_values.len > 0) |
| 16545 | try ip.getCoercedInts( |
| 16546 | gpa, |
| 16547 | io, |
| 16548 | pt.tid, |
| 16549 | ip.indexToKey(enum_obj.field_values.get(ip)[tag_index]).int, |
| 16550 | .comptime_int_type, |
| 16551 | ) |
| 16552 | else |
| 16553 | (try pt.intValue(.comptime_int, tag_index)).toIntern(); |
| 16554 | |
| 16555 | // TODO: write something like getCoercedInts to avoid needing to dupe |
| 16556 | const name_val = v: { |
| 16557 | const tag_name = enum_obj.field_names.get(ip)[tag_index]; |
| 16558 | const tag_name_len = tag_name.length(ip); |
| 16559 | const new_decl_ty = try pt.arrayType(.{ |
| 16560 | .len = tag_name_len, |
| 16561 | .sentinel = .zero_u8, |
| 16562 | .child = .u8_type, |
| 16563 | }); |
| 16564 | const new_decl_val = try pt.intern(.{ .aggregate = .{ |
| 16565 | .ty = new_decl_ty.toIntern(), |
| 16566 | .storage = .{ .bytes = tag_name.toString() }, |
| 16567 | } }); |
| 16568 | break :v try pt.intern(.{ .slice = .{ |
| 16569 | .ty = .slice_const_u8_sentinel_0_type, |
| 16570 | .ptr = try pt.intern(.{ .ptr = .{ |
| 16571 | .ty = .manyptr_const_u8_sentinel_0_type, |
| 16572 | .base_addr = .{ .uav = .{ |
| 16573 | .val = new_decl_val, |
| 16574 | .orig_ty = .slice_const_u8_sentinel_0_type, |
| 16575 | } }, |
| 16576 | .byte_offset = 0, |
| 16577 | } }), |
| 16578 | .len = (try pt.intValue(.usize, tag_name_len)).toIntern(), |
| 16579 | } }); |
| 16580 | }; |
| 16581 | |
| 16582 | field_name_val.* = name_val; |
| 16583 | field_value_val.* = value_val; |
| 16584 | } |
| 16585 | |
| 16586 | const fields_names_val = v: { |
| 16587 | const fields_names_array_ty = try pt.arrayType(.{ |
| 16588 | .len = enum_field_name_vals.len, |
| 16589 | .child = .slice_const_u8_sentinel_0_type, |
| 16590 | }); |
| 16591 | const new_decl_val = (try pt.aggregateValue(fields_names_array_ty, enum_field_name_vals)).toIntern(); |
| 16592 | const slice_ty = (try pt.ptrType(.{ |
| 16593 | .child = .slice_const_u8_sentinel_0_type, |
| 16594 | .flags = .{ |
| 16595 | .size = .slice, |
| 16596 | .is_const = true, |
| 16597 | }, |
| 16598 | })).toIntern(); |
| 16599 | const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); |
| 16600 | break :v try pt.intern(.{ .slice = .{ |
| 16601 | .ty = slice_ty, |
| 16602 | .ptr = try pt.intern(.{ .ptr = .{ |
| 16603 | .ty = manyptr_ty, |
| 16604 | .base_addr = .{ .uav = .{ |
| 16605 | .val = new_decl_val, |
| 16606 | .orig_ty = manyptr_ty, |
| 16607 | } }, |
| 16608 | .byte_offset = 0, |
| 16609 | } }), |
| 16610 | .len = (try pt.intValue(.usize, enum_field_name_vals.len)).toIntern(), |
| 16611 | } }); |
| 16612 | }; |
| 16613 | |
| 16614 | const fields_values_val = v: { |
| 16615 | const fields_values_array_ty = try pt.arrayType(.{ |
| 16616 | .len = enum_field_value_vals.len, |
| 16617 | .child = .comptime_int_type, |
| 16618 | }); |
| 16619 | const new_decl_val = (try pt.aggregateValue(fields_values_array_ty, enum_field_value_vals)).toIntern(); |
| 16620 | const slice_ty = (try pt.ptrType(.{ |
| 16621 | .child = .comptime_int_type, |
| 16622 | .flags = .{ |
| 16623 | .size = .slice, |
| 16624 | .is_const = true, |
| 16625 | }, |
| 16626 | })).toIntern(); |
| 16627 | const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); |
| 16628 | break :v try pt.intern(.{ .slice = .{ |
| 16629 | .ty = slice_ty, |
| 16630 | .ptr = try pt.intern(.{ .ptr = .{ |
| 16631 | .ty = manyptr_ty, |
| 16632 | .base_addr = .{ .uav = .{ |
| 16633 | .val = new_decl_val, |
| 16634 | .orig_ty = manyptr_ty, |
| 16635 | } }, |
| 16636 | .byte_offset = 0, |
| 16637 | } }), |
| 16638 | .len = (try pt.intValue(.usize, enum_field_value_vals.len)).toIntern(), |
| 16639 | } }); |
| 16640 | }; |
| 16641 | |
| 16642 | const decl_names_val = try sema.typeInfoDecls(ip.loadEnumType(ty.toIntern()).namespace.toOptional()); |
| 16643 | |
| 16644 | const type_enum_ty = try sema.getStdLangType(src, .@"Type.Enum"); |
| 16645 | |
| 16646 | const field_values = .{ |
| 16647 | // tag_type: type, |
| 16648 | ip.loadEnumType(ty.toIntern()).int_tag_type, |
| 16649 | // mode: Mode |
| 16650 | enum_mode.toIntern(), |
| 16651 | |
| 16652 | // field_names: []const [:0]const u8, |
| 16653 | fields_names_val, |
| 16654 | // field_values: []const comptime_int, |
| 16655 | fields_values_val, |
| 16656 | |
| 16657 | // decl_names: []const [:0]const u8, |
| 16658 | decl_names_val, |
| 16659 | }; |
| 16660 | return Air.internedToRef((try pt.internUnion(.{ |
| 16661 | .ty = type_info_ty.toIntern(), |
| 16662 | .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.@"enum"))).toIntern(), |
| 16663 | .val = (try pt.aggregateValue(type_enum_ty, &field_values)).toIntern(), |
| 16664 | }))); |
| 16665 | }, |
| 16666 | .@"union" => { |
| 16667 | const type_union_ty = try sema.getStdLangType(src, .@"Type.Union"); |
| 16668 | const union_field_attr_ty = try sema.getStdLangType(src, .@"Type.Union.FieldAttributes"); |
| 16669 | |
| 16670 | const union_obj = ip.loadUnionType(ty.toIntern()); |
| 16671 | const enum_obj = ip.loadEnumType(union_obj.enum_tag_type); |
| 16672 | const layout = union_obj.layout; |
| 16673 | |
| 16674 | const union_field_names = try gpa.alloc(InternPool.Index, enum_obj.field_names.len); |
| 16675 | defer gpa.free(union_field_names); |
| 16676 | const union_field_attrs = try gpa.alloc(InternPool.Index, enum_obj.field_names.len); |
| 16677 | defer gpa.free(union_field_attrs); |
| 16678 | |
| 16679 | for ( |
| 16680 | union_field_names, |
| 16681 | union_field_attrs, |
| 16682 | 0.., |
| 16683 | ) | |
| 16684 | *field_name_val, |
| 16685 | *field_attr_val, |
| 16686 | field_index, |
| 16687 | | { |
| 16688 | const name_val = v: { |
| 16689 | const field_name = enum_obj.field_names.get(ip)[field_index]; |
| 16690 | const field_name_len = field_name.length(ip); |
| 16691 | const new_decl_ty = try pt.arrayType(.{ |
| 16692 | .len = field_name_len, |
| 16693 | .sentinel = .zero_u8, |
| 16694 | .child = .u8_type, |
| 16695 | }); |
| 16696 | const new_decl_val = try pt.intern(.{ .aggregate = .{ |
| 16697 | .ty = new_decl_ty.toIntern(), |
| 16698 | .storage = .{ .bytes = field_name.toString() }, |
| 16699 | } }); |
| 16700 | break :v try pt.intern(.{ .slice = .{ |
| 16701 | .ty = .slice_const_u8_sentinel_0_type, |
| 16702 | .ptr = try pt.intern(.{ .ptr = .{ |
| 16703 | .ty = .manyptr_const_u8_sentinel_0_type, |
| 16704 | .base_addr = .{ .uav = .{ |
| 16705 | .val = new_decl_val, |
| 16706 | .orig_ty = .slice_const_u8_sentinel_0_type, |
| 16707 | } }, |
| 16708 | .byte_offset = 0, |
| 16709 | } }), |
| 16710 | .len = (try pt.intValue(.usize, field_name_len)).toIntern(), |
| 16711 | } }); |
| 16712 | }; |
| 16713 | |
| 16714 | const alignment_ty = try pt.optionalType(.usize_type); |
| 16715 | const alignment_val: Value = val: { |
| 16716 | const a: Alignment = switch (layout) { |
| 16717 | .auto, .@"extern" => ty.explicitFieldAlignment(field_index, zcu), |
| 16718 | .@"packed" => .none, |
| 16719 | }; |
| 16720 | const bytes = a.toByteUnits() orelse { |
| 16721 | break :val try pt.nullValue(alignment_ty); |
| 16722 | }; |
| 16723 | const int_val = try pt.intValue(.usize, bytes); |
| 16724 | break :val .fromInterned(try pt.intern(.{ .opt = .{ |
| 16725 | .ty = alignment_ty.toIntern(), |
| 16726 | .val = int_val.toIntern(), |
| 16727 | } })); |
| 16728 | }; |
| 16729 | |
| 16730 | field_name_val.* = name_val; |
| 16731 | const union_field_attr = .{ |
| 16732 | // alignment: ?usize, |
| 16733 | alignment_val.toIntern(), |
| 16734 | }; |
| 16735 | |
| 16736 | field_attr_val.* = (try pt.aggregateValue(union_field_attr_ty, &union_field_attr)).toIntern(); |
| 16737 | } |
| 16738 | |
| 16739 | const field_names_val = v: { |
| 16740 | const array_field_names_ty = try pt.arrayType(.{ |
| 16741 | .len = union_field_names.len, |
| 16742 | .child = .slice_const_u8_sentinel_0_type, |
| 16743 | }); |
| 16744 | const new_decl_val = (try pt.aggregateValue(array_field_names_ty, union_field_names)).toIntern(); |
| 16745 | const slice_ty = (try pt.ptrType(.{ |
| 16746 | .child = .slice_const_u8_sentinel_0_type, |
| 16747 | .flags = .{ |
| 16748 | .size = .slice, |
| 16749 | .is_const = true, |
| 16750 | }, |
| 16751 | })).toIntern(); |
| 16752 | const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); |
| 16753 | break :v try pt.intern(.{ .slice = .{ |
| 16754 | .ty = slice_ty, |
| 16755 | .ptr = try pt.intern(.{ .ptr = .{ |
| 16756 | .ty = manyptr_ty, |
| 16757 | .base_addr = .{ .uav = .{ |
| 16758 | .orig_ty = manyptr_ty, |
| 16759 | .val = new_decl_val, |
| 16760 | } }, |
| 16761 | .byte_offset = 0, |
| 16762 | } }), |
| 16763 | .len = (try pt.intValue(.usize, union_field_names.len)).toIntern(), |
| 16764 | } }); |
| 16765 | }; |
| 16766 | const field_types_val = v: { |
| 16767 | const union_field_types = union_obj.field_types.get(ip); |
| 16768 | |
| 16769 | const array_fields_ty = try pt.arrayType(.{ |
| 16770 | .len = union_field_types.len, |
| 16771 | .child = .type_type, |
| 16772 | }); |
| 16773 | const new_decl_val = (try pt.aggregateValue(array_fields_ty, union_field_types)).toIntern(); |
| 16774 | const slice_ty = (try pt.ptrType(.{ |
| 16775 | .child = .type_type, |
| 16776 | .flags = .{ |
| 16777 | .size = .slice, |
| 16778 | .is_const = true, |
| 16779 | }, |
| 16780 | })).toIntern(); |
| 16781 | const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); |
| 16782 | break :v try pt.intern(.{ .slice = .{ |
| 16783 | .ty = slice_ty, |
| 16784 | .ptr = try pt.intern(.{ .ptr = .{ |
| 16785 | .ty = manyptr_ty, |
| 16786 | .base_addr = .{ .uav = .{ |
| 16787 | .orig_ty = manyptr_ty, |
| 16788 | .val = new_decl_val, |
| 16789 | } }, |
| 16790 | .byte_offset = 0, |
| 16791 | } }), |
| 16792 | .len = (try pt.intValue(.usize, union_field_types.len)).toIntern(), |
| 16793 | } }); |
| 16794 | }; |
| 16795 | const field_attrs_val = v: { |
| 16796 | const array_fields_ty = try pt.arrayType(.{ |
| 16797 | .len = union_field_attrs.len, |
| 16798 | .child = union_field_attr_ty.toIntern(), |
| 16799 | }); |
| 16800 | const new_decl_val = (try pt.aggregateValue(array_fields_ty, union_field_attrs)).toIntern(); |
| 16801 | const slice_ty = (try pt.ptrType(.{ |
| 16802 | .child = union_field_attr_ty.toIntern(), |
| 16803 | .flags = .{ |
| 16804 | .size = .slice, |
| 16805 | .is_const = true, |
| 16806 | }, |
| 16807 | })).toIntern(); |
| 16808 | const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); |
| 16809 | break :v try pt.intern(.{ .slice = .{ |
| 16810 | .ty = slice_ty, |
| 16811 | .ptr = try pt.intern(.{ .ptr = .{ |
| 16812 | .ty = manyptr_ty, |
| 16813 | .base_addr = .{ .uav = .{ |
| 16814 | .orig_ty = manyptr_ty, |
| 16815 | .val = new_decl_val, |
| 16816 | } }, |
| 16817 | .byte_offset = 0, |
| 16818 | } }), |
| 16819 | .len = (try pt.intValue(.usize, union_field_attrs.len)).toIntern(), |
| 16820 | } }); |
| 16821 | }; |
| 16822 | |
| 16823 | const decl_names_val = try sema.typeInfoDecls(ty.getNamespaceIndex(zcu).toOptional()); |
| 16824 | |
| 16825 | const enum_tag_ty_val = try pt.intern(.{ .opt = .{ |
| 16826 | .ty = (try pt.optionalType(.type_type)).toIntern(), |
| 16827 | .val = if (ty.unionTagType(zcu)) |tag_ty| tag_ty.toIntern() else .none, |
| 16828 | } }); |
| 16829 | |
| 16830 | const container_layout_ty = try sema.getStdLangType(src, .@"Type.ContainerLayout"); |
| 16831 | |
| 16832 | const backing_integer_val = try pt.intern(.{ .opt = .{ |
| 16833 | .ty = (try pt.optionalType(.type_type)).toIntern(), |
| 16834 | .val = if (layout == .@"packed") val: { |
| 16835 | assert(Type.fromInterned(union_obj.packed_backing_int_type).isInt(zcu)); |
| 16836 | break :val union_obj.packed_backing_int_type; |
| 16837 | } else .none, |
| 16838 | } }); |
| 16839 | |
| 16840 | const field_values = .{ |
| 16841 | // layout: ContainerLayout, |
| 16842 | (try pt.enumValueFieldIndex(container_layout_ty, @backingInt(layout))).toIntern(), |
| 16843 | |
| 16844 | // tag_type: ?type, |
| 16845 | enum_tag_ty_val, |
| 16846 | // backing_integer: ?type, |
| 16847 | backing_integer_val, |
| 16848 | |
| 16849 | // field_names: []const [:0]const u8, |
| 16850 | field_names_val, |
| 16851 | // field_types: []const type, |
| 16852 | field_types_val, |
| 16853 | // field_attrs: []const FieldAttributes, |
| 16854 | field_attrs_val, |
| 16855 | |
| 16856 | // decl_names: []const [:0]const u8, |
| 16857 | decl_names_val, |
| 16858 | }; |
| 16859 | return Air.internedToRef((try pt.internUnion(.{ |
| 16860 | .ty = type_info_ty.toIntern(), |
| 16861 | .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.@"union"))).toIntern(), |
| 16862 | .val = (try pt.aggregateValue(type_union_ty, &field_values)).toIntern(), |
| 16863 | }))); |
| 16864 | }, |
| 16865 | .@"struct" => { |
| 16866 | const type_struct_ty = try sema.getStdLangType(src, .@"Type.Struct"); |
| 16867 | const struct_field_attr_ty = try sema.getStdLangType(src, .@"Type.Struct.FieldAttributes"); |
| 16868 | |
| 16869 | var struct_field_name_vals: []InternPool.Index = &.{}; |
| 16870 | defer gpa.free(struct_field_name_vals); |
| 16871 | var struct_field_attr_vals: []InternPool.Index = &.{}; |
| 16872 | defer gpa.free(struct_field_attr_vals); |
| 16873 | fv: { |
| 16874 | const struct_type = switch (ip.indexToKey(ty.toIntern())) { |
| 16875 | .tuple_type => |tuple_type| { |
| 16876 | struct_field_name_vals = try gpa.alloc(InternPool.Index, tuple_type.types.len); |
| 16877 | struct_field_attr_vals = try gpa.alloc(InternPool.Index, tuple_type.types.len); |
| 16878 | for ( |
| 16879 | struct_field_name_vals, |
| 16880 | struct_field_attr_vals, |
| 16881 | 0.., |
| 16882 | ) | |
| 16883 | *struct_field_name_val, |
| 16884 | *struct_field_attr_val, |
| 16885 | field_index, |
| 16886 | | { |
| 16887 | const field_val = tuple_type.values.get(ip)[field_index]; |
| 16888 | const name_val = v: { |
| 16889 | const field_name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{d}", .{field_index}, .no_embedded_nulls); |
| 16890 | const field_name_len = field_name.length(ip); |
| 16891 | const new_decl_ty = try pt.arrayType(.{ |
| 16892 | .len = field_name_len, |
| 16893 | .sentinel = .zero_u8, |
| 16894 | .child = .u8_type, |
| 16895 | }); |
| 16896 | const new_decl_val = try pt.intern(.{ .aggregate = .{ |
| 16897 | .ty = new_decl_ty.toIntern(), |
| 16898 | .storage = .{ .bytes = field_name.toString() }, |
| 16899 | } }); |
| 16900 | break :v try pt.intern(.{ .slice = .{ |
| 16901 | .ty = .slice_const_u8_sentinel_0_type, |
| 16902 | .ptr = try pt.intern(.{ .ptr = .{ |
| 16903 | .ty = .manyptr_const_u8_sentinel_0_type, |
| 16904 | .base_addr = .{ .uav = .{ |
| 16905 | .val = new_decl_val, |
| 16906 | .orig_ty = .slice_const_u8_sentinel_0_type, |
| 16907 | } }, |
| 16908 | .byte_offset = 0, |
| 16909 | } }), |
| 16910 | .len = (try pt.intValue(.usize, field_name_len)).toIntern(), |
| 16911 | } }); |
| 16912 | }; |
| 16913 | |
| 16914 | const is_comptime = field_val != .none; |
| 16915 | const opt_default_val = if (is_comptime) Value.fromInterned(field_val) else null; |
| 16916 | const default_val_ptr = try sema.optRefValue(opt_default_val); |
| 16917 | |
| 16918 | const struct_field_attr_fields = .{ |
| 16919 | // @"comptime": bool, |
| 16920 | Value.makeBool(is_comptime).toIntern(), |
| 16921 | // @"align": ?usize, |
| 16922 | (try pt.nullValue(try pt.optionalType(.usize_type))).toIntern(), |
| 16923 | // default_value_ptr: ?*const anyopaque, |
| 16924 | default_val_ptr.toIntern(), |
| 16925 | }; |
| 16926 | |
| 16927 | struct_field_name_val.* = name_val; |
| 16928 | struct_field_attr_val.* = (try pt.aggregateValue(struct_field_attr_ty, &struct_field_attr_fields)).toIntern(); |
| 16929 | } |
| 16930 | break :fv; |
| 16931 | }, |
| 16932 | .struct_type => ip.loadStructType(ty.toIntern()), |
| 16933 | else => unreachable, |
| 16934 | }; |
| 16935 | try sema.ensureStructDefaultsResolved(ty, src); // can't do this sooner, since it's not allowed on tuples |
| 16936 | struct_field_name_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len); |
| 16937 | struct_field_attr_vals = try gpa.alloc(InternPool.Index, struct_type.field_types.len); |
| 16938 | |
| 16939 | for ( |
| 16940 | struct_field_name_vals, |
| 16941 | struct_field_attr_vals, |
| 16942 | 0.., |
| 16943 | ) | |
| 16944 | *field_name_val, |
| 16945 | *field_attr_val, |
| 16946 | field_index, |
| 16947 | | { |
| 16948 | const field_name = struct_type.field_names.get(ip)[field_index]; |
| 16949 | const field_name_len = field_name.length(ip); |
| 16950 | const field_default: InternPool.Index = if (struct_type.field_defaults.len > 0) d: { |
| 16951 | break :d struct_type.field_defaults.get(ip)[field_index]; |
| 16952 | } else .none; |
| 16953 | const field_is_comptime = struct_type.field_is_comptime_bits.get(ip, field_index); |
| 16954 | const name_val = v: { |
| 16955 | const new_decl_ty = try pt.arrayType(.{ |
| 16956 | .len = field_name_len, |
| 16957 | .sentinel = .zero_u8, |
| 16958 | .child = .u8_type, |
| 16959 | }); |
| 16960 | const new_decl_val = try pt.intern(.{ .aggregate = .{ |
| 16961 | .ty = new_decl_ty.toIntern(), |
| 16962 | .storage = .{ .bytes = field_name.toString() }, |
| 16963 | } }); |
| 16964 | break :v try pt.intern(.{ .slice = .{ |
| 16965 | .ty = .slice_const_u8_sentinel_0_type, |
| 16966 | .ptr = try pt.intern(.{ .ptr = .{ |
| 16967 | .ty = .manyptr_const_u8_sentinel_0_type, |
| 16968 | .base_addr = .{ .uav = .{ |
| 16969 | .val = new_decl_val, |
| 16970 | .orig_ty = .slice_const_u8_sentinel_0_type, |
| 16971 | } }, |
| 16972 | .byte_offset = 0, |
| 16973 | } }), |
| 16974 | .len = (try pt.intValue(.usize, field_name_len)).toIntern(), |
| 16975 | } }); |
| 16976 | }; |
| 16977 | |
| 16978 | const opt_default_val: ?Value = if (field_default == .none) null else .fromInterned(field_default); |
| 16979 | const default_val_ptr = try sema.optRefValue(opt_default_val); |
| 16980 | |
| 16981 | const alignment_ty = try pt.optionalType(.usize_type); |
| 16982 | const alignment_val: Value = val: { |
| 16983 | const a: Alignment = switch (struct_type.layout) { |
| 16984 | .auto, .@"extern" => ty.explicitFieldAlignment(field_index, zcu), |
| 16985 | .@"packed" => .none, |
| 16986 | }; |
| 16987 | const bytes = a.toByteUnits() orelse { |
| 16988 | break :val try pt.nullValue(alignment_ty); |
| 16989 | }; |
| 16990 | const int_val = try pt.intValue(.usize, bytes); |
| 16991 | break :val .fromInterned(try pt.intern(.{ .opt = .{ |
| 16992 | .ty = alignment_ty.toIntern(), |
| 16993 | .val = int_val.toIntern(), |
| 16994 | } })); |
| 16995 | }; |
| 16996 | |
| 16997 | const struct_field_attr_fields = .{ |
| 16998 | // @"comptime": bool, |
| 16999 | Value.makeBool(field_is_comptime).toIntern(), |
| 17000 | // @"align": ?usize, |
| 17001 | alignment_val.toIntern(), |
| 17002 | // default_value_ptr: ?*const anyopaque, |
| 17003 | default_val_ptr.toIntern(), |
| 17004 | }; |
| 17005 | field_name_val.* = name_val; |
| 17006 | field_attr_val.* = (try pt.aggregateValue(struct_field_attr_ty, &struct_field_attr_fields)).toIntern(); |
| 17007 | } |
| 17008 | } |
| 17009 | |
| 17010 | const field_names_val = v: { |
| 17011 | const array_fields_ty = try pt.arrayType(.{ |
| 17012 | .len = struct_field_name_vals.len, |
| 17013 | .child = .slice_const_u8_sentinel_0_type, |
| 17014 | }); |
| 17015 | const new_decl_val = (try pt.aggregateValue(array_fields_ty, struct_field_name_vals)).toIntern(); |
| 17016 | const slice_ty = (try pt.ptrType(.{ |
| 17017 | .child = .slice_const_u8_sentinel_0_type, |
| 17018 | .flags = .{ |
| 17019 | .size = .slice, |
| 17020 | .is_const = true, |
| 17021 | }, |
| 17022 | })).toIntern(); |
| 17023 | const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); |
| 17024 | break :v try pt.intern(.{ .slice = .{ |
| 17025 | .ty = slice_ty, |
| 17026 | .ptr = try pt.intern(.{ .ptr = .{ |
| 17027 | .ty = manyptr_ty, |
| 17028 | .base_addr = .{ .uav = .{ |
| 17029 | .orig_ty = manyptr_ty, |
| 17030 | .val = new_decl_val, |
| 17031 | } }, |
| 17032 | .byte_offset = 0, |
| 17033 | } }), |
| 17034 | .len = (try pt.intValue(.usize, struct_field_name_vals.len)).toIntern(), |
| 17035 | } }); |
| 17036 | }; |
| 17037 | |
| 17038 | const field_types_val = v: { |
| 17039 | const struct_field_type_vals = switch (ip.indexToKey(ty.toIntern())) { |
| 17040 | .tuple_type => |tt| tt.types.get(ip), |
| 17041 | .struct_type => blk: { |
| 17042 | const st = ip.loadStructType(ty.toIntern()); |
| 17043 | break :blk st.field_types.get(ip); |
| 17044 | }, |
| 17045 | else => unreachable, |
| 17046 | }; |
| 17047 | const array_fields_ty = try pt.arrayType(.{ |
| 17048 | .len = struct_field_type_vals.len, |
| 17049 | .child = .type_type, |
| 17050 | }); |
| 17051 | const new_decl_val = (try pt.aggregateValue(array_fields_ty, struct_field_type_vals)).toIntern(); |
| 17052 | const slice_ty = (try pt.ptrType(.{ |
| 17053 | .child = .type_type, |
| 17054 | .flags = .{ |
| 17055 | .size = .slice, |
| 17056 | .is_const = true, |
| 17057 | }, |
| 17058 | })).toIntern(); |
| 17059 | const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); |
| 17060 | break :v try pt.intern(.{ .slice = .{ |
| 17061 | .ty = slice_ty, |
| 17062 | .ptr = try pt.intern(.{ .ptr = .{ |
| 17063 | .ty = manyptr_ty, |
| 17064 | .base_addr = .{ .uav = .{ |
| 17065 | .orig_ty = manyptr_ty, |
| 17066 | .val = new_decl_val, |
| 17067 | } }, |
| 17068 | .byte_offset = 0, |
| 17069 | } }), |
| 17070 | .len = (try pt.intValue(.usize, struct_field_type_vals.len)).toIntern(), |
| 17071 | } }); |
| 17072 | }; |
| 17073 | const field_attrs_val = v: { |
| 17074 | const array_fields_ty = try pt.arrayType(.{ |
| 17075 | .len = struct_field_attr_vals.len, |
| 17076 | .child = struct_field_attr_ty.toIntern(), |
| 17077 | }); |
| 17078 | const new_decl_val = (try pt.aggregateValue(array_fields_ty, struct_field_attr_vals)).toIntern(); |
| 17079 | const slice_ty = (try pt.ptrType(.{ |
| 17080 | .child = struct_field_attr_ty.toIntern(), |
| 17081 | .flags = .{ |
| 17082 | .size = .slice, |
| 17083 | .is_const = true, |
| 17084 | }, |
| 17085 | })).toIntern(); |
| 17086 | const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); |
| 17087 | break :v try pt.intern(.{ .slice = .{ |
| 17088 | .ty = slice_ty, |
| 17089 | .ptr = try pt.intern(.{ .ptr = .{ |
| 17090 | .ty = manyptr_ty, |
| 17091 | .base_addr = .{ .uav = .{ |
| 17092 | .orig_ty = manyptr_ty, |
| 17093 | .val = new_decl_val, |
| 17094 | } }, |
| 17095 | .byte_offset = 0, |
| 17096 | } }), |
| 17097 | .len = (try pt.intValue(.usize, struct_field_attr_vals.len)).toIntern(), |
| 17098 | } }); |
| 17099 | }; |
| 17100 | |
| 17101 | const decl_names_val = try sema.typeInfoDecls(ty.getNamespace(zcu)); |
| 17102 | |
| 17103 | const backing_integer_val = try pt.intern(.{ .opt = .{ |
| 17104 | .ty = (try pt.optionalType(.type_type)).toIntern(), |
| 17105 | .val = if (zcu.typeToPackedStruct(ty)) |struct_obj| val: { |
| 17106 | assert(Type.fromInterned(struct_obj.packed_backing_int_type).isInt(zcu)); |
| 17107 | break :val struct_obj.packed_backing_int_type; |
| 17108 | } else .none, |
| 17109 | } }); |
| 17110 | |
| 17111 | const container_layout_ty = try sema.getStdLangType(src, .@"Type.ContainerLayout"); |
| 17112 | |
| 17113 | const layout = ty.containerLayout(zcu); |
| 17114 | |
| 17115 | const field_values = [_]InternPool.Index{ |
| 17116 | // is_tuple: bool, |
| 17117 | Value.makeBool(ty.isTuple(zcu)).toIntern(), |
| 17118 | // layout: ContainerLayout, |
| 17119 | (try pt.enumValueFieldIndex(container_layout_ty, @backingInt(layout))).toIntern(), |
| 17120 | // backing_integer: ?type, |
| 17121 | backing_integer_val, |
| 17122 | |
| 17123 | // field_names: []const [:0]const u8, |
| 17124 | field_names_val, |
| 17125 | // field_types: []const type, |
| 17126 | field_types_val, |
| 17127 | // field_attrs: []const FieldAttributes, |
| 17128 | field_attrs_val, |
| 17129 | |
| 17130 | // decl_names: []const [:0]const u8, |
| 17131 | decl_names_val, |
| 17132 | }; |
| 17133 | return Air.internedToRef((try pt.internUnion(.{ |
| 17134 | .ty = type_info_ty.toIntern(), |
| 17135 | .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.@"struct"))).toIntern(), |
| 17136 | .val = (try pt.aggregateValue(type_struct_ty, &field_values)).toIntern(), |
| 17137 | }))); |
| 17138 | }, |
| 17139 | .@"opaque" => { |
| 17140 | const type_opaque_ty = try sema.getStdLangType(src, .@"Type.Opaque"); |
| 17141 | |
| 17142 | const decl_names_val = try sema.typeInfoDecls(ty.getNamespace(zcu)); |
| 17143 | |
| 17144 | const field_values = .{ |
| 17145 | // decl_names: []const [:0]const u8, |
| 17146 | decl_names_val, |
| 17147 | }; |
| 17148 | return Air.internedToRef((try pt.internUnion(.{ |
| 17149 | .ty = type_info_ty.toIntern(), |
| 17150 | .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.@"opaque"))).toIntern(), |
| 17151 | .val = (try pt.aggregateValue(type_opaque_ty, &field_values)).toIntern(), |
| 17152 | }))); |
| 17153 | }, |
| 17154 | .spirv => { |
| 17155 | const spirv_info = ip.loadSpirvType(ty.toIntern()); |
| 17156 | const spirv_union_ty = try sema.getStdLangType(src, .@"Type.Spirv"); |
| 17157 | const spirv_tag_ty = spirv_union_ty.unionTagType(zcu).?; |
| 17158 | const spirv_tag_val = try pt.enumValueFieldIndex(spirv_tag_ty, @backingInt(spirv_info.flags.tag)); |
| 17159 | const spirv_payload_val: Value = switch (spirv_info.flags.tag) { |
| 17160 | .sampler => .void, |
| 17161 | .sampled_image, .runtime_array => .fromInterned(spirv_info.ty), |
| 17162 | .image => image: { |
| 17163 | const image_ty = try sema.getStdLangType(src, .@"Type.Spirv.Image"); |
| 17164 | const usage_union_ty = try sema.getStdLangType(src, .@"Type.Spirv.Image.Usage"); |
| 17165 | const format_ty = try sema.getStdLangType(src, .@"Type.Spirv.Image.Format"); |
| 17166 | const dim_ty = try sema.getStdLangType(src, .@"Type.Spirv.Image.Dimensionality"); |
| 17167 | const depth_ty = try sema.getStdLangType(src, .@"Type.Spirv.Image.Depth"); |
| 17168 | const access_ty = try sema.getStdLangType(src, .@"Type.Spirv.Image.Access"); |
| 17169 | const usage_tag_ty = usage_union_ty.unionTagType(zcu).?; |
| 17170 | const usage_tag_val = try pt.enumValueFieldIndex(usage_tag_ty, @backingInt(spirv_info.flags.usage)); |
| 17171 | const usage_val = try pt.unionValue(usage_union_ty, usage_tag_val, .fromInterned(spirv_info.ty)); |
| 17172 | const image_field_vals = [_]InternPool.Index{ |
| 17173 | usage_val.toIntern(), |
| 17174 | (try pt.enumValueFieldIndex(format_ty, @backingInt(spirv_info.flags.format))).toIntern(), |
| 17175 | (try pt.enumValueFieldIndex(dim_ty, @backingInt(spirv_info.flags.dim))).toIntern(), |
| 17176 | (try pt.enumValueFieldIndex(depth_ty, @backingInt(spirv_info.flags.depth))).toIntern(), |
| 17177 | (try pt.enumValueFieldIndex(access_ty, @backingInt(spirv_info.flags.access))).toIntern(), |
| 17178 | Value.makeBool(spirv_info.flags.is_arrayed).toIntern(), |
| 17179 | Value.makeBool(spirv_info.flags.is_multisampled).toIntern(), |
| 17180 | }; |
| 17181 | break :image try pt.aggregateValue(image_ty, &image_field_vals); |
| 17182 | }, |
| 17183 | }; |
| 17184 | const spirv_val = try pt.unionValue(spirv_union_ty, spirv_tag_val, spirv_payload_val); |
| 17185 | return Air.internedToRef((try pt.internUnion(.{ |
| 17186 | .ty = type_info_ty.toIntern(), |
| 17187 | .tag = (try pt.enumValueFieldIndex(type_info_tag_ty, @backingInt(std.lang.TypeId.spirv))).toIntern(), |
| 17188 | .val = spirv_val.toIntern(), |
| 17189 | }))); |
| 17190 | }, |
| 17191 | .frame => return sema.failWithUseOfAsync(block, src), |
| 17192 | .@"anyframe" => return sema.failWithUseOfAsync(block, src), |
| 17193 | } |
| 17194 | } |
| 17195 | |
| 17196 | fn typeInfoDecls( |
| 17197 | sema: *Sema, |
| 17198 | opt_namespace: InternPool.OptionalNamespaceIndex, |
| 17199 | ) CompileError!InternPool.Index { |
| 17200 | const pt = sema.pt; |
| 17201 | const zcu = pt.zcu; |
| 17202 | const gpa = sema.gpa; |
| 17203 | |
| 17204 | var decl_vals = std.array_list.Managed(InternPool.Index).init(gpa); |
| 17205 | defer decl_vals.deinit(); |
| 17206 | |
| 17207 | var seen_namespaces = std.AutoHashMap(*Namespace, void).init(gpa); |
| 17208 | defer seen_namespaces.deinit(); |
| 17209 | |
| 17210 | try sema.typeInfoNamespaceDecls(opt_namespace, &decl_vals, &seen_namespaces); |
| 17211 | |
| 17212 | const array_decl_ty = try pt.arrayType(.{ |
| 17213 | .len = decl_vals.items.len, |
| 17214 | .child = .slice_const_u8_sentinel_0_type, |
| 17215 | }); |
| 17216 | const new_decl_val = (try pt.aggregateValue(array_decl_ty, decl_vals.items)).toIntern(); |
| 17217 | const slice_ty = (try pt.ptrType(.{ |
| 17218 | .child = .slice_const_u8_sentinel_0_type, |
| 17219 | .flags = .{ |
| 17220 | .size = .slice, |
| 17221 | .is_const = true, |
| 17222 | }, |
| 17223 | })).toIntern(); |
| 17224 | const manyptr_ty = Type.fromInterned(slice_ty).slicePtrFieldType(zcu).toIntern(); |
| 17225 | return try pt.intern(.{ .slice = .{ |
| 17226 | .ty = slice_ty, |
| 17227 | .ptr = try pt.intern(.{ .ptr = .{ |
| 17228 | .ty = manyptr_ty, |
| 17229 | .base_addr = .{ .uav = .{ |
| 17230 | .orig_ty = manyptr_ty, |
| 17231 | .val = new_decl_val, |
| 17232 | } }, |
| 17233 | .byte_offset = 0, |
| 17234 | } }), |
| 17235 | .len = (try pt.intValue(.usize, decl_vals.items.len)).toIntern(), |
| 17236 | } }); |
| 17237 | } |
| 17238 | |
| 17239 | fn typeInfoNamespaceDecls( |
| 17240 | sema: *Sema, |
| 17241 | opt_namespace_index: InternPool.OptionalNamespaceIndex, |
| 17242 | decl_vals: *std.array_list.Managed(InternPool.Index), |
| 17243 | seen_namespaces: *std.AutoHashMap(*Namespace, void), |
| 17244 | ) !void { |
| 17245 | const pt = sema.pt; |
| 17246 | const zcu = pt.zcu; |
| 17247 | const ip = &zcu.intern_pool; |
| 17248 | |
| 17249 | const namespace_index = opt_namespace_index.unwrap() orelse return; |
| 17250 | pt.ensureNamespaceUpToDate(namespace_index) catch |err| switch (err) { |
| 17251 | error.LostZirContainerDecl => { |
| 17252 | const namespace = zcu.namespacePtr(namespace_index); |
| 17253 | const ns_ty: Type = .fromInterned(namespace.owner_type); |
| 17254 | return sema.failTransitive(.{ .lost_tracking = ns_ty.typeDeclInstAllowGeneratedTag(zcu).? }); |
| 17255 | }, |
| 17256 | else => |e| return e, |
| 17257 | }; |
| 17258 | const namespace = zcu.namespacePtr(namespace_index); |
| 17259 | |
| 17260 | const gop = try seen_namespaces.getOrPut(namespace); |
| 17261 | if (gop.found_existing) return; |
| 17262 | |
| 17263 | for (namespace.pub_decls.keys()) |nav| { |
| 17264 | const name = ip.getNav(nav).name; |
| 17265 | const name_val = name_val: { |
| 17266 | const name_len = name.length(ip); |
| 17267 | const array_ty = try pt.arrayType(.{ |
| 17268 | .len = name_len, |
| 17269 | .sentinel = .zero_u8, |
| 17270 | .child = .u8_type, |
| 17271 | }); |
| 17272 | const array_val = try pt.intern(.{ .aggregate = .{ |
| 17273 | .ty = array_ty.toIntern(), |
| 17274 | .storage = .{ .bytes = name.toString() }, |
| 17275 | } }); |
| 17276 | break :name_val try pt.intern(.{ |
| 17277 | .slice = .{ |
| 17278 | .ty = .slice_const_u8_sentinel_0_type, // [:0]const u8 |
| 17279 | .ptr = try pt.intern(.{ |
| 17280 | .ptr = .{ |
| 17281 | .ty = .manyptr_const_u8_sentinel_0_type, // [*:0]const u8 |
| 17282 | .base_addr = .{ .uav = .{ |
| 17283 | .orig_ty = .slice_const_u8_sentinel_0_type, |
| 17284 | .val = array_val, |
| 17285 | } }, |
| 17286 | .byte_offset = 0, |
| 17287 | }, |
| 17288 | }), |
| 17289 | .len = (try pt.intValue(.usize, name_len)).toIntern(), |
| 17290 | }, |
| 17291 | }); |
| 17292 | }; |
| 17293 | try decl_vals.append(name_val); |
| 17294 | } |
| 17295 | } |
| 17296 | |
| 17297 | fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 17298 | _ = block; |
| 17299 | const zir_datas = sema.code.instructions.items(.data); |
| 17300 | const inst_data = zir_datas[@backingInt(inst)].un_node; |
| 17301 | const operand = sema.resolveInst(inst_data.operand); |
| 17302 | const operand_ty = sema.typeOf(operand); |
| 17303 | return Air.internedToRef(operand_ty.toIntern()); |
| 17304 | } |
| 17305 | |
| 17306 | fn zirTypeofBuiltin(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 17307 | const pl_node = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 17308 | const extra = sema.code.extraData(Zir.Inst.Block, pl_node.payload_index); |
| 17309 | const body = sema.code.bodySlice(extra.end, extra.data.body_len); |
| 17310 | |
| 17311 | var child_block: Block = .{ |
| 17312 | .parent = block, |
| 17313 | .sema = sema, |
| 17314 | .namespace = block.namespace, |
| 17315 | .instructions = .empty, |
| 17316 | .inlining = block.inlining, |
| 17317 | .comptime_reason = null, |
| 17318 | .is_typeof = true, |
| 17319 | .want_safety = false, |
| 17320 | .error_return_trace_index = block.error_return_trace_index, |
| 17321 | .src_base_inst = block.src_base_inst, |
| 17322 | .type_name_ctx = block.type_name_ctx, |
| 17323 | .type_fqn_ctx = block.type_fqn_ctx, |
| 17324 | }; |
| 17325 | defer child_block.instructions.deinit(sema.gpa); |
| 17326 | |
| 17327 | const operand = try sema.resolveInlineBody(&child_block, body, inst); |
| 17328 | return Air.internedToRef(sema.typeOf(operand).toIntern()); |
| 17329 | } |
| 17330 | |
| 17331 | fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 17332 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 17333 | const src = block.nodeOffset(inst_data.src_node); |
| 17334 | const operand = sema.resolveInst(inst_data.operand); |
| 17335 | const operand_ty = sema.typeOf(operand); |
| 17336 | const res_ty = try sema.log2IntType(block, operand_ty, src); |
| 17337 | return Air.internedToRef(res_ty.toIntern()); |
| 17338 | } |
| 17339 | |
| 17340 | fn log2IntType(sema: *Sema, block: *Block, operand: Type, src: LazySrcLoc) CompileError!Type { |
| 17341 | const pt = sema.pt; |
| 17342 | const zcu = pt.zcu; |
| 17343 | switch (operand.zigTypeTag(zcu)) { |
| 17344 | .comptime_int => return .comptime_int, |
| 17345 | .int => return pt.intType(.unsigned, switch (operand.intInfo(zcu).bits) { |
| 17346 | 0 => 0, |
| 17347 | else => |b| std.math.log2_int_ceil(u16, b), |
| 17348 | }), |
| 17349 | .vector => { |
| 17350 | const elem_ty = operand.childType(zcu); |
| 17351 | const log2_elem_ty = try sema.log2IntType(block, elem_ty, src); |
| 17352 | return pt.vectorType(.{ |
| 17353 | .len = operand.vectorLen(zcu), |
| 17354 | .child = log2_elem_ty.toIntern(), |
| 17355 | }); |
| 17356 | }, |
| 17357 | else => {}, |
| 17358 | } |
| 17359 | return sema.fail( |
| 17360 | block, |
| 17361 | src, |
| 17362 | "bit shifting operation expected integer type, found '{f}'", |
| 17363 | .{operand.fmt(pt)}, |
| 17364 | ); |
| 17365 | } |
| 17366 | |
| 17367 | fn zirTypeofPeer( |
| 17368 | sema: *Sema, |
| 17369 | block: *Block, |
| 17370 | extended: Zir.Inst.Extended.InstData, |
| 17371 | inst: Zir.Inst.Index, |
| 17372 | ) CompileError!Air.Inst.Ref { |
| 17373 | const extra = sema.code.extraData(Zir.Inst.TypeOfPeer, extended.operand); |
| 17374 | const src = block.nodeOffset(extra.data.src_node); |
| 17375 | const body = sema.code.bodySlice(extra.data.body_index, extra.data.body_len); |
| 17376 | |
| 17377 | var child_block: Block = .{ |
| 17378 | .parent = block, |
| 17379 | .sema = sema, |
| 17380 | .namespace = block.namespace, |
| 17381 | .instructions = .empty, |
| 17382 | .inlining = block.inlining, |
| 17383 | .comptime_reason = null, |
| 17384 | .is_typeof = true, |
| 17385 | .runtime_cond = block.runtime_cond, |
| 17386 | .runtime_loop = block.runtime_loop, |
| 17387 | .runtime_index = block.runtime_index, |
| 17388 | .src_base_inst = block.src_base_inst, |
| 17389 | .type_name_ctx = block.type_name_ctx, |
| 17390 | .type_fqn_ctx = block.type_fqn_ctx, |
| 17391 | }; |
| 17392 | defer child_block.instructions.deinit(sema.gpa); |
| 17393 | // Ignore the result, we only care about the instructions in `args`. |
| 17394 | _ = try sema.analyzeInlineBody(&child_block, body, inst); |
| 17395 | |
| 17396 | const args = sema.code.refSlice(extra.end, extended.small); |
| 17397 | |
| 17398 | const inst_list = try sema.gpa.alloc(Air.Inst.Ref, args.len); |
| 17399 | defer sema.gpa.free(inst_list); |
| 17400 | |
| 17401 | for (args, 0..) |arg_ref, i| { |
| 17402 | inst_list[i] = sema.resolveInst(arg_ref); |
| 17403 | } |
| 17404 | |
| 17405 | const result_type = try sema.resolvePeerTypes(block, src, inst_list, .{ .typeof_builtin_call_node_offset = extra.data.src_node }); |
| 17406 | return Air.internedToRef(result_type.toIntern()); |
| 17407 | } |
| 17408 | |
| 17409 | fn zirBoolNot(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 17410 | const pt = sema.pt; |
| 17411 | const zcu = pt.zcu; |
| 17412 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 17413 | const src = block.nodeOffset(inst_data.src_node); |
| 17414 | const operand_src = block.src(.{ .node_offset_un_op = inst_data.src_node }); |
| 17415 | const uncasted_operand = sema.resolveInst(inst_data.operand); |
| 17416 | const uncasted_ty = sema.typeOf(uncasted_operand); |
| 17417 | if (uncasted_ty.isVector(zcu)) { |
| 17418 | if (uncasted_ty.scalarType(zcu).zigTypeTag(zcu) != .bool) { |
| 17419 | return sema.fail(block, operand_src, "boolean not operation on type '{f}'", .{ |
| 17420 | uncasted_ty.fmt(pt), |
| 17421 | }); |
| 17422 | } |
| 17423 | return analyzeBitNot(sema, block, uncasted_operand, src); |
| 17424 | } |
| 17425 | const operand = try sema.coerce(block, .bool, uncasted_operand, operand_src); |
| 17426 | if (sema.resolveValue(operand)) |val| { |
| 17427 | return if (val.isUndef(zcu)) .undef_bool else if (val.toBool()) .bool_false else .bool_true; |
| 17428 | } |
| 17429 | try sema.requireRuntimeBlock(block, src, null); |
| 17430 | return block.addTyOp(.not, .bool, operand); |
| 17431 | } |
| 17432 | |
| 17433 | fn zirBoolBr( |
| 17434 | sema: *Sema, |
| 17435 | parent_block: *Block, |
| 17436 | inst: Zir.Inst.Index, |
| 17437 | is_bool_or: bool, |
| 17438 | ) CompileError!Air.Inst.Ref { |
| 17439 | const pt = sema.pt; |
| 17440 | const zcu = pt.zcu; |
| 17441 | const gpa = sema.gpa; |
| 17442 | |
| 17443 | const datas = sema.code.instructions.items(.data); |
| 17444 | const inst_data = datas[@backingInt(inst)].pl_node; |
| 17445 | const extra = sema.code.extraData(Zir.Inst.BoolBr, inst_data.payload_index); |
| 17446 | |
| 17447 | const uncoerced_lhs = sema.resolveInst(extra.data.lhs); |
| 17448 | const body = sema.code.bodySlice(extra.end, extra.data.body_len); |
| 17449 | const lhs_src = parent_block.src(.{ .node_offset_bin_lhs = inst_data.src_node }); |
| 17450 | const rhs_src = parent_block.src(.{ .node_offset_bin_rhs = inst_data.src_node }); |
| 17451 | |
| 17452 | const lhs = try sema.coerce(parent_block, .bool, uncoerced_lhs, lhs_src); |
| 17453 | |
| 17454 | if (try sema.resolveDefinedValue(parent_block, lhs_src, lhs)) |lhs_val| { |
| 17455 | if (is_bool_or and lhs_val.toBool()) { |
| 17456 | return .bool_true; |
| 17457 | } else if (!is_bool_or and !lhs_val.toBool()) { |
| 17458 | return .bool_false; |
| 17459 | } |
| 17460 | // comptime-known left-hand side. No need for a block here; the result |
| 17461 | // is simply the rhs expression. Here we rely on there only being 1 |
| 17462 | // break instruction (`break_inline`). |
| 17463 | const rhs_result = try sema.resolveInlineBody(parent_block, body, inst); |
| 17464 | if (sema.typeOf(rhs_result).isNoReturn(zcu)) { |
| 17465 | return rhs_result; |
| 17466 | } |
| 17467 | return sema.coerce(parent_block, .bool, rhs_result, rhs_src); |
| 17468 | } |
| 17469 | |
| 17470 | const block_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len)); |
| 17471 | try sema.air_instructions.append(gpa, .{ |
| 17472 | .tag = .block, |
| 17473 | .data = .{ .ty_pl = .{ |
| 17474 | .ty = .bool, |
| 17475 | .payload = undefined, |
| 17476 | } }, |
| 17477 | }); |
| 17478 | |
| 17479 | var child_block = parent_block.makeSubBlock(); |
| 17480 | child_block.runtime_loop = null; |
| 17481 | child_block.runtime_cond = lhs_src; |
| 17482 | child_block.runtime_index.increment(); |
| 17483 | defer child_block.instructions.deinit(gpa); |
| 17484 | |
| 17485 | var then_block = child_block.makeSubBlock(); |
| 17486 | defer then_block.instructions.deinit(gpa); |
| 17487 | |
| 17488 | var else_block = child_block.makeSubBlock(); |
| 17489 | defer else_block.instructions.deinit(gpa); |
| 17490 | |
| 17491 | const lhs_block = if (is_bool_or) &then_block else &else_block; |
| 17492 | const rhs_block = if (is_bool_or) &else_block else &then_block; |
| 17493 | |
| 17494 | const lhs_result: Air.Inst.Ref = if (is_bool_or) .bool_true else .bool_false; |
| 17495 | _ = try lhs_block.addBr(block_inst, lhs_result); |
| 17496 | |
| 17497 | const parent_hint = sema.branch_hint; |
| 17498 | defer sema.branch_hint = parent_hint; |
| 17499 | sema.branch_hint = null; |
| 17500 | |
| 17501 | const rhs_result = try sema.resolveInlineBody(rhs_block, body, inst); |
| 17502 | const rhs_noret = sema.typeOf(rhs_result).isNoReturn(zcu); |
| 17503 | const coerced_rhs_result = if (!rhs_noret) rhs: { |
| 17504 | const coerced_result = try sema.coerce(rhs_block, .bool, rhs_result, rhs_src); |
| 17505 | _ = try rhs_block.addBr(block_inst, coerced_result); |
| 17506 | break :rhs coerced_result; |
| 17507 | } else rhs_result; |
| 17508 | |
| 17509 | const rhs_hint = sema.branch_hint orelse .none; |
| 17510 | |
| 17511 | const result = try sema.finishCondBr( |
| 17512 | parent_block, |
| 17513 | &child_block, |
| 17514 | &then_block, |
| 17515 | &else_block, |
| 17516 | lhs, |
| 17517 | block_inst, |
| 17518 | if (is_bool_or) .{ |
| 17519 | .true = .none, |
| 17520 | .false = rhs_hint, |
| 17521 | .then_cov = .poi, |
| 17522 | .else_cov = .poi, |
| 17523 | } else .{ |
| 17524 | .true = rhs_hint, |
| 17525 | .false = .none, |
| 17526 | .then_cov = .poi, |
| 17527 | .else_cov = .poi, |
| 17528 | }, |
| 17529 | ); |
| 17530 | if (!rhs_noret) { |
| 17531 | if (try sema.resolveDefinedValue(rhs_block, rhs_src, coerced_rhs_result)) |rhs_val| { |
| 17532 | if (is_bool_or and rhs_val.toBool()) { |
| 17533 | return .bool_true; |
| 17534 | } else if (!is_bool_or and !rhs_val.toBool()) { |
| 17535 | return .bool_false; |
| 17536 | } |
| 17537 | } |
| 17538 | } |
| 17539 | |
| 17540 | return result; |
| 17541 | } |
| 17542 | |
| 17543 | fn finishCondBr( |
| 17544 | sema: *Sema, |
| 17545 | parent_block: *Block, |
| 17546 | child_block: *Block, |
| 17547 | then_block: *Block, |
| 17548 | else_block: *Block, |
| 17549 | cond: Air.Inst.Ref, |
| 17550 | block_inst: Air.Inst.Index, |
| 17551 | branch_hints: Air.CondBr.BranchHints, |
| 17552 | ) !Air.Inst.Ref { |
| 17553 | const gpa = sema.gpa; |
| 17554 | |
| 17555 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".field_names.len + |
| 17556 | then_block.instructions.items.len + else_block.instructions.items.len + |
| 17557 | @typeInfo(Air.Block).@"struct".field_names.len + child_block.instructions.items.len + 1); |
| 17558 | |
| 17559 | const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{ |
| 17560 | .then_body_len = @intCast(then_block.instructions.items.len), |
| 17561 | .else_body_len = @intCast(else_block.instructions.items.len), |
| 17562 | .branch_hints = branch_hints, |
| 17563 | }); |
| 17564 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(then_block.instructions.items)); |
| 17565 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(else_block.instructions.items)); |
| 17566 | |
| 17567 | _ = try child_block.addInst(.{ .tag = .cond_br, .data = .{ .pl_op = .{ |
| 17568 | .operand = cond, |
| 17569 | .payload = cond_br_payload, |
| 17570 | } } }); |
| 17571 | |
| 17572 | sema.air_instructions.items(.data)[@backingInt(block_inst)].ty_pl.payload = sema.addExtraAssumeCapacity( |
| 17573 | Air.Block{ .body_len = @intCast(child_block.instructions.items.len) }, |
| 17574 | ); |
| 17575 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(child_block.instructions.items)); |
| 17576 | |
| 17577 | try parent_block.instructions.append(gpa, block_inst); |
| 17578 | return block_inst.toRef(); |
| 17579 | } |
| 17580 | |
| 17581 | fn checkNullableType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { |
| 17582 | const pt = sema.pt; |
| 17583 | const zcu = pt.zcu; |
| 17584 | switch (ty.zigTypeTag(zcu)) { |
| 17585 | .optional, .null, .undefined => return, |
| 17586 | .pointer => if (ty.isPtrLikeOptional(zcu)) return, |
| 17587 | else => {}, |
| 17588 | } |
| 17589 | return sema.failWithExpectedOptionalType(block, src, ty); |
| 17590 | } |
| 17591 | |
| 17592 | fn checkSentinelType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { |
| 17593 | const pt = sema.pt; |
| 17594 | const zcu = pt.zcu; |
| 17595 | if (!ty.isSelfComparable(zcu, true)) { |
| 17596 | return sema.fail(block, src, "non-scalar sentinel type '{f}'", .{ty.fmt(pt)}); |
| 17597 | } |
| 17598 | } |
| 17599 | |
| 17600 | fn zirIsNonNull( |
| 17601 | sema: *Sema, |
| 17602 | block: *Block, |
| 17603 | inst: Zir.Inst.Index, |
| 17604 | ) CompileError!Air.Inst.Ref { |
| 17605 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 17606 | const src = block.nodeOffset(inst_data.src_node); |
| 17607 | const operand = sema.resolveInst(inst_data.operand); |
| 17608 | try sema.checkNullableType(block, src, sema.typeOf(operand)); |
| 17609 | return sema.analyzeIsNull(block, src, operand, true); |
| 17610 | } |
| 17611 | |
| 17612 | fn zirIsNonNullPtr( |
| 17613 | sema: *Sema, |
| 17614 | block: *Block, |
| 17615 | inst: Zir.Inst.Index, |
| 17616 | ) CompileError!Air.Inst.Ref { |
| 17617 | const pt = sema.pt; |
| 17618 | const zcu = pt.zcu; |
| 17619 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 17620 | const src = block.nodeOffset(inst_data.src_node); |
| 17621 | const ptr = sema.resolveInst(inst_data.operand); |
| 17622 | const ptr_ty = sema.typeOf(ptr); |
| 17623 | assert(ptr_ty.zigTypeTag(zcu) == .pointer); |
| 17624 | const nullable_ty = ptr_ty.childType(zcu); |
| 17625 | |
| 17626 | try sema.checkNullableType(block, src, nullable_ty); |
| 17627 | try sema.ensureLayoutResolved(nullable_ty, src, .ptr_access); |
| 17628 | |
| 17629 | if (try sema.resolveIsNullFromType(block, src, nullable_ty)) |is_null| { |
| 17630 | return .fromValue(.makeBool(!is_null)); |
| 17631 | } |
| 17632 | |
| 17633 | if (sema.resolveValue(ptr)) |ptr_val| { |
| 17634 | if (try sema.pointerDeref(block, src, ptr_val, ptr_ty)) |nullable_val| { |
| 17635 | return sema.analyzeIsNull(block, src, .fromValue(nullable_val), true); |
| 17636 | } |
| 17637 | } |
| 17638 | |
| 17639 | return block.addUnOp(.is_non_null_ptr, ptr); |
| 17640 | } |
| 17641 | |
| 17642 | fn checkErrorType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { |
| 17643 | const pt = sema.pt; |
| 17644 | const zcu = pt.zcu; |
| 17645 | switch (ty.zigTypeTag(zcu)) { |
| 17646 | .error_set, .error_union, .undefined => return, |
| 17647 | else => return sema.fail(block, src, "expected error union type, found '{f}'", .{ |
| 17648 | ty.fmt(pt), |
| 17649 | }), |
| 17650 | } |
| 17651 | } |
| 17652 | |
| 17653 | fn zirIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 17654 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 17655 | const src = block.nodeOffset(inst_data.src_node); |
| 17656 | const operand = sema.resolveInst(inst_data.operand); |
| 17657 | try sema.checkErrorType(block, src, sema.typeOf(operand)); |
| 17658 | return sema.analyzeIsNonErr(block, src, operand); |
| 17659 | } |
| 17660 | |
| 17661 | fn zirIsNonErrPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 17662 | const pt = sema.pt; |
| 17663 | const zcu = pt.zcu; |
| 17664 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 17665 | const src = block.nodeOffset(inst_data.src_node); |
| 17666 | const ptr = sema.resolveInst(inst_data.operand); |
| 17667 | const ptr_ty = sema.typeOf(ptr); |
| 17668 | assert(ptr_ty.zigTypeTag(zcu) == .pointer); |
| 17669 | const error_ty = ptr_ty.childType(zcu); |
| 17670 | try sema.checkErrorType(block, src, error_ty); |
| 17671 | const loaded = try sema.analyzeLoad(block, src, ptr, src); |
| 17672 | return sema.analyzeIsNonErr(block, src, loaded); |
| 17673 | } |
| 17674 | |
| 17675 | fn zirRetIsNonErr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 17676 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 17677 | const src = block.nodeOffset(inst_data.src_node); |
| 17678 | const operand = sema.resolveInst(inst_data.operand); |
| 17679 | return sema.analyzeIsNonErr(block, src, operand); |
| 17680 | } |
| 17681 | |
| 17682 | fn zirCondbr( |
| 17683 | sema: *Sema, |
| 17684 | parent_block: *Block, |
| 17685 | inst: Zir.Inst.Index, |
| 17686 | ) CompileError!void { |
| 17687 | const pt = sema.pt; |
| 17688 | const zcu = pt.zcu; |
| 17689 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 17690 | const cond_src = parent_block.src(.{ .node_offset_if_cond = inst_data.src_node }); |
| 17691 | const extra = sema.code.extraData(Zir.Inst.CondBr, inst_data.payload_index); |
| 17692 | |
| 17693 | const then_body = sema.code.bodySlice(extra.end, extra.data.then_body_len); |
| 17694 | const else_body = sema.code.bodySlice(extra.end + then_body.len, extra.data.else_body_len); |
| 17695 | |
| 17696 | const uncasted_cond = sema.resolveInst(extra.data.condition); |
| 17697 | const cond = try sema.coerce(parent_block, .bool, uncasted_cond, cond_src); |
| 17698 | |
| 17699 | if (try sema.resolveDefinedValue(parent_block, cond_src, cond)) |cond_val| { |
| 17700 | const body = if (cond_val.toBool()) then_body else else_body; |
| 17701 | |
| 17702 | // We can propagate `.cold` hints from this branch since it's comptime-known |
| 17703 | // to be taken from the parent branch. |
| 17704 | const parent_hint = sema.branch_hint; |
| 17705 | defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null; |
| 17706 | |
| 17707 | try sema.maybeErrorUnwrapCondbr(parent_block, body, extra.data.condition, cond_src); |
| 17708 | // We use `analyzeBodyInner` since we want to propagate any comptime control flow to the caller. |
| 17709 | return sema.analyzeBodyInner(parent_block, body); |
| 17710 | } |
| 17711 | |
| 17712 | const gpa = sema.gpa; |
| 17713 | |
| 17714 | // We'll re-use the sub block to save on memory bandwidth, and yank out the |
| 17715 | // instructions array in between using it for the then block and else block. |
| 17716 | var sub_block = parent_block.makeSubBlock(); |
| 17717 | sub_block.runtime_loop = null; |
| 17718 | sub_block.runtime_cond = cond_src; |
| 17719 | sub_block.runtime_index.increment(); |
| 17720 | sub_block.need_debug_scope = null; // this body is emitted regardless |
| 17721 | defer sub_block.instructions.deinit(gpa); |
| 17722 | |
| 17723 | const true_hint = try sema.analyzeBodyRuntimeBreak(&sub_block, then_body); |
| 17724 | const true_instructions = try sub_block.instructions.toOwnedSlice(gpa); |
| 17725 | defer gpa.free(true_instructions); |
| 17726 | |
| 17727 | const err_cond = blk: { |
| 17728 | const index = extra.data.condition.toIndex() orelse break :blk null; |
| 17729 | if (sema.code.instructions.items(.tag)[@backingInt(index)] != .is_non_err) break :blk null; |
| 17730 | |
| 17731 | const err_inst_data = sema.code.instructions.items(.data)[@backingInt(index)].un_node; |
| 17732 | const err_operand = sema.resolveInst(err_inst_data.operand); |
| 17733 | const operand_ty = sema.typeOf(err_operand); |
| 17734 | assert(operand_ty.zigTypeTag(zcu) == .error_union); |
| 17735 | const result_ty = operand_ty.errorUnionSet(zcu); |
| 17736 | break :blk try sub_block.addTyOp(.unwrap_errunion_err, result_ty, err_operand); |
| 17737 | }; |
| 17738 | |
| 17739 | // Reset, this may have been updated by the then block analysis |
| 17740 | sub_block.error_return_trace_index = parent_block.error_return_trace_index; |
| 17741 | |
| 17742 | const false_hint: std.lang.BranchHint = if (err_cond != null and |
| 17743 | try sema.maybeErrorUnwrap(&sub_block, else_body, err_cond.?, cond_src, false)) |
| 17744 | h: { |
| 17745 | // nothing to do here. weight against error branch |
| 17746 | break :h .unlikely; |
| 17747 | } else try sema.analyzeBodyRuntimeBreak(&sub_block, else_body); |
| 17748 | |
| 17749 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.CondBr).@"struct".field_names.len + |
| 17750 | true_instructions.len + sub_block.instructions.items.len); |
| 17751 | _ = try parent_block.addInst(.{ |
| 17752 | .tag = .cond_br, |
| 17753 | .data = .{ |
| 17754 | .pl_op = .{ |
| 17755 | .operand = cond, |
| 17756 | .payload = sema.addExtraAssumeCapacity(Air.CondBr{ |
| 17757 | .then_body_len = @intCast(true_instructions.len), |
| 17758 | .else_body_len = @intCast(sub_block.instructions.items.len), |
| 17759 | .branch_hints = .{ |
| 17760 | .true = true_hint, |
| 17761 | .false = false_hint, |
| 17762 | // Code coverage is desired for error handling. |
| 17763 | .then_cov = .poi, |
| 17764 | .else_cov = .poi, |
| 17765 | }, |
| 17766 | }), |
| 17767 | }, |
| 17768 | }, |
| 17769 | }); |
| 17770 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(true_instructions)); |
| 17771 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items)); |
| 17772 | } |
| 17773 | |
| 17774 | fn zirTry(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 17775 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 17776 | const src = parent_block.nodeOffset(inst_data.src_node); |
| 17777 | const operand_src = parent_block.src(.{ .node_offset_try_operand = inst_data.src_node }); |
| 17778 | const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); |
| 17779 | const body = sema.code.bodySlice(extra.end, extra.data.body_len); |
| 17780 | const err_union = sema.resolveInst(extra.data.operand); |
| 17781 | const err_union_ty = sema.typeOf(err_union); |
| 17782 | const pt = sema.pt; |
| 17783 | const zcu = pt.zcu; |
| 17784 | if (err_union_ty.zigTypeTag(zcu) != .error_union) { |
| 17785 | return sema.failWithOwnedErrorMsg(parent_block, msg: { |
| 17786 | const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)}); |
| 17787 | errdefer msg.destroy(sema.gpa); |
| 17788 | try sema.addDeclaredHereNote(msg, err_union_ty); |
| 17789 | try sema.errNote(operand_src, msg, "consider omitting 'try'", .{}); |
| 17790 | break :msg msg; |
| 17791 | }); |
| 17792 | } |
| 17793 | if (try sema.resolveIsNonErrVal(parent_block, operand_src, err_union)) |is_non_err_val| { |
| 17794 | // We can propagate `.cold` hints from this branch since it's comptime-known |
| 17795 | // to be taken from the parent branch. |
| 17796 | const parent_hint = sema.branch_hint; |
| 17797 | defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null; |
| 17798 | |
| 17799 | if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(parent_block, operand_src, null); |
| 17800 | if (is_non_err_val.toBool()) { |
| 17801 | return sema.analyzeErrUnionPayload(parent_block, src, err_union_ty, err_union, operand_src, false); |
| 17802 | } |
| 17803 | // We can analyze the body directly in the parent block because we know there are |
| 17804 | // no breaks from the body possible, and that the body is noreturn. |
| 17805 | try sema.analyzeBodyInner(parent_block, body); |
| 17806 | return .unreachable_value; |
| 17807 | } |
| 17808 | |
| 17809 | var sub_block = parent_block.makeSubBlock(); |
| 17810 | defer sub_block.instructions.deinit(sema.gpa); |
| 17811 | |
| 17812 | const parent_hint = sema.branch_hint; |
| 17813 | defer sema.branch_hint = parent_hint; |
| 17814 | |
| 17815 | // This body is guaranteed to end with noreturn and has no breaks. |
| 17816 | try sema.analyzeBodyInner(&sub_block, body); |
| 17817 | |
| 17818 | // The only interesting hint here is `.cold`, which can come from e.g. `errdefer @panic`. |
| 17819 | const is_cold = sema.branch_hint == .cold; |
| 17820 | |
| 17821 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.Try).@"struct".field_names.len + |
| 17822 | sub_block.instructions.items.len); |
| 17823 | const try_inst = try parent_block.addInst(.{ |
| 17824 | .tag = if (is_cold) .try_cold else .@"try", |
| 17825 | .data = .{ .pl_op = .{ |
| 17826 | .operand = err_union, |
| 17827 | .payload = sema.addExtraAssumeCapacity(Air.Try{ |
| 17828 | .body_len = @intCast(sub_block.instructions.items.len), |
| 17829 | }), |
| 17830 | } }, |
| 17831 | }); |
| 17832 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items)); |
| 17833 | |
| 17834 | // The payload type might still be OPV, in which case `try_inst` is just there for the runtime |
| 17835 | // control flow and we should return a comptime-known result. |
| 17836 | if (try err_union_ty.errorUnionPayload(zcu).onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 17837 | |
| 17838 | return try_inst; |
| 17839 | } |
| 17840 | |
| 17841 | fn zirTryPtr(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 17842 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 17843 | const src = parent_block.nodeOffset(inst_data.src_node); |
| 17844 | const operand_src = parent_block.src(.{ .node_offset_try_operand = inst_data.src_node }); |
| 17845 | const extra = sema.code.extraData(Zir.Inst.Try, inst_data.payload_index); |
| 17846 | const body = sema.code.bodySlice(extra.end, extra.data.body_len); |
| 17847 | const operand = sema.resolveInst(extra.data.operand); |
| 17848 | const err_union = try sema.analyzeLoad(parent_block, src, operand, operand_src); |
| 17849 | const err_union_ty = sema.typeOf(err_union); |
| 17850 | const pt = sema.pt; |
| 17851 | const zcu = pt.zcu; |
| 17852 | if (err_union_ty.zigTypeTag(zcu) != .error_union) { |
| 17853 | return sema.failWithOwnedErrorMsg(parent_block, msg: { |
| 17854 | const msg = try sema.errMsg(operand_src, "expected error union type, found '{f}'", .{err_union_ty.fmt(pt)}); |
| 17855 | errdefer msg.destroy(sema.gpa); |
| 17856 | try sema.addDeclaredHereNote(msg, err_union_ty); |
| 17857 | try sema.errNote(operand_src, msg, "consider omitting 'try'", .{}); |
| 17858 | break :msg msg; |
| 17859 | }); |
| 17860 | } |
| 17861 | if (try sema.resolveIsNonErrVal(parent_block, operand_src, err_union)) |is_non_err_val| { |
| 17862 | // We can propagate `.cold` hints from this branch since it's comptime-known |
| 17863 | // to be taken from the parent branch. |
| 17864 | const parent_hint = sema.branch_hint; |
| 17865 | defer sema.branch_hint = parent_hint orelse if (sema.branch_hint == .cold) .cold else null; |
| 17866 | |
| 17867 | if (is_non_err_val.isUndef(zcu)) return sema.failWithUseOfUndef(parent_block, operand_src, null); |
| 17868 | if (is_non_err_val.toBool()) { |
| 17869 | return sema.analyzeErrUnionPayloadPtr(parent_block, src, operand, false, false); |
| 17870 | } |
| 17871 | // We can analyze the body directly in the parent block because we know there are |
| 17872 | // no breaks from the body possible, and that the body is noreturn. |
| 17873 | try sema.analyzeBodyInner(parent_block, body); |
| 17874 | return .unreachable_value; |
| 17875 | } |
| 17876 | |
| 17877 | var sub_block = parent_block.makeSubBlock(); |
| 17878 | defer sub_block.instructions.deinit(sema.gpa); |
| 17879 | |
| 17880 | const parent_hint = sema.branch_hint; |
| 17881 | defer sema.branch_hint = parent_hint; |
| 17882 | |
| 17883 | // This body is guaranteed to end with noreturn and has no breaks. |
| 17884 | try sema.analyzeBodyInner(&sub_block, body); |
| 17885 | |
| 17886 | // The only interesting hint here is `.cold`, which can come from e.g. `errdefer @panic`. |
| 17887 | const is_cold = sema.branch_hint == .cold; |
| 17888 | |
| 17889 | const operand_ty = sema.typeOf(operand); |
| 17890 | const res_ty = try pt.ptrType(info: { |
| 17891 | var new = operand_ty.ptrInfo(zcu); |
| 17892 | new.child = err_union_ty.errorUnionPayload(zcu).toIntern(); |
| 17893 | break :info new; |
| 17894 | }); |
| 17895 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, @typeInfo(Air.TryPtr).@"struct".field_names.len + |
| 17896 | sub_block.instructions.items.len); |
| 17897 | const try_inst = try parent_block.addInst(.{ |
| 17898 | .tag = if (is_cold) .try_ptr_cold else .try_ptr, |
| 17899 | .data = .{ .ty_pl = .{ |
| 17900 | .ty = res_ty, |
| 17901 | .payload = sema.addExtraAssumeCapacity(Air.TryPtr{ |
| 17902 | .ptr = operand, |
| 17903 | .body_len = @intCast(sub_block.instructions.items.len), |
| 17904 | }), |
| 17905 | } }, |
| 17906 | }); |
| 17907 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(sub_block.instructions.items)); |
| 17908 | return try_inst; |
| 17909 | } |
| 17910 | |
| 17911 | fn ensurePostHoc(sema: *Sema, block: *Block, dest_block: Zir.Inst.Index) !*LabeledBlock { |
| 17912 | const gop = sema.inst_map.getOrPutAssumeCapacity(dest_block); |
| 17913 | if (gop.found_existing) existing: { |
| 17914 | // This may be a *result* from an earlier iteration of an inline loop. |
| 17915 | // In this case, there will not be a post-hoc block entry, and we can |
| 17916 | // continue with the logic below. |
| 17917 | const new_block_inst = gop.value_ptr.*.toIndex() orelse break :existing; |
| 17918 | return sema.post_hoc_blocks.get(new_block_inst) orelse break :existing; |
| 17919 | } |
| 17920 | |
| 17921 | try sema.post_hoc_blocks.ensureUnusedCapacity(sema.gpa, 1); |
| 17922 | |
| 17923 | const new_block_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len)); |
| 17924 | gop.value_ptr.* = new_block_inst.toRef(); |
| 17925 | try sema.air_instructions.append(sema.gpa, .{ |
| 17926 | .tag = .block, |
| 17927 | .data = undefined, |
| 17928 | }); |
| 17929 | const labeled_block = try sema.gpa.create(LabeledBlock); |
| 17930 | labeled_block.* = .{ |
| 17931 | .label = .{ |
| 17932 | .zir_block = dest_block, |
| 17933 | .merges = .{ |
| 17934 | .src_locs = .empty, |
| 17935 | .results = .empty, |
| 17936 | .br_list = .empty, |
| 17937 | .block_inst = new_block_inst, |
| 17938 | }, |
| 17939 | }, |
| 17940 | .block = .{ |
| 17941 | .parent = block, |
| 17942 | .sema = sema, |
| 17943 | .namespace = block.namespace, |
| 17944 | .instructions = .empty, |
| 17945 | .label = &labeled_block.label, |
| 17946 | .inlining = block.inlining, |
| 17947 | .comptime_reason = block.comptime_reason, |
| 17948 | .src_base_inst = block.src_base_inst, |
| 17949 | .type_name_ctx = block.type_name_ctx, |
| 17950 | .type_fqn_ctx = block.type_fqn_ctx, |
| 17951 | }, |
| 17952 | }; |
| 17953 | sema.post_hoc_blocks.putAssumeCapacityNoClobber(new_block_inst, labeled_block); |
| 17954 | return labeled_block; |
| 17955 | } |
| 17956 | |
| 17957 | /// A `break` statement is inside a runtime condition, but trying to |
| 17958 | /// break from an inline loop. In such case we must convert it to |
| 17959 | /// a runtime break. |
| 17960 | fn addRuntimeBreak(sema: *Sema, child_block: *Block, block_inst: Zir.Inst.Index, break_operand: Zir.Inst.Ref) !void { |
| 17961 | const labeled_block = try sema.ensurePostHoc(child_block, block_inst); |
| 17962 | |
| 17963 | const operand = sema.resolveInst(break_operand); |
| 17964 | const br_ref = try child_block.addBr(labeled_block.label.merges.block_inst, operand); |
| 17965 | |
| 17966 | try labeled_block.label.merges.results.append(sema.gpa, operand); |
| 17967 | try labeled_block.label.merges.br_list.append(sema.gpa, br_ref.toIndex().?); |
| 17968 | try labeled_block.label.merges.src_locs.append(sema.gpa, null); |
| 17969 | |
| 17970 | labeled_block.block.runtime_index.increment(); |
| 17971 | if (labeled_block.block.runtime_cond == null and labeled_block.block.runtime_loop == null) { |
| 17972 | labeled_block.block.runtime_cond = child_block.runtime_cond orelse child_block.runtime_loop; |
| 17973 | labeled_block.block.runtime_loop = child_block.runtime_loop; |
| 17974 | } |
| 17975 | } |
| 17976 | |
| 17977 | fn zirUnreachable(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 17978 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].@"unreachable"; |
| 17979 | const src = block.nodeOffset(inst_data.src_node); |
| 17980 | |
| 17981 | if (block.isComptime()) { |
| 17982 | return sema.fail(block, src, "reached unreachable code", .{}); |
| 17983 | } |
| 17984 | // TODO Add compile error for @optimizeFor occurring too late in a scope. |
| 17985 | sema.analyzeUnreachable(block, src, true) catch |err| switch (err) { |
| 17986 | error.AlreadyReported => |e| { |
| 17987 | if (sema.err) |msg| { |
| 17988 | if (!mem.eql(u8, msg.msg, "runtime safety check not allowed in naked function")) return err; |
| 17989 | try sema.errNote(src, msg, "the end of a naked function is implicitly unreachable", .{}); |
| 17990 | } |
| 17991 | return e; |
| 17992 | }, |
| 17993 | else => |e| return e, |
| 17994 | }; |
| 17995 | } |
| 17996 | |
| 17997 | fn zirRetErrValue( |
| 17998 | sema: *Sema, |
| 17999 | block: *Block, |
| 18000 | inst: Zir.Inst.Index, |
| 18001 | ) CompileError!void { |
| 18002 | const pt = sema.pt; |
| 18003 | const zcu = pt.zcu; |
| 18004 | const comp = zcu.comp; |
| 18005 | const gpa = comp.gpa; |
| 18006 | const io = comp.io; |
| 18007 | |
| 18008 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].str_tok; |
| 18009 | const src = block.tokenOffset(inst_data.src_tok); |
| 18010 | const err_name = try zcu.intern_pool.getOrPutString( |
| 18011 | gpa, |
| 18012 | io, |
| 18013 | pt.tid, |
| 18014 | inst_data.get(sema.code), |
| 18015 | .no_embedded_nulls, |
| 18016 | ); |
| 18017 | _ = try pt.getErrorValue(err_name); |
| 18018 | // Return the error code from the function. |
| 18019 | const error_set_type = try pt.singleErrorSetType(err_name); |
| 18020 | const result_inst = Air.internedToRef((try pt.intern(.{ .err = .{ |
| 18021 | .ty = error_set_type.toIntern(), |
| 18022 | .name = err_name, |
| 18023 | } }))); |
| 18024 | return sema.analyzeRet(block, result_inst, src, src); |
| 18025 | } |
| 18026 | |
| 18027 | fn zirRetImplicit( |
| 18028 | sema: *Sema, |
| 18029 | block: *Block, |
| 18030 | inst: Zir.Inst.Index, |
| 18031 | ) CompileError!void { |
| 18032 | const pt = sema.pt; |
| 18033 | const zcu = pt.zcu; |
| 18034 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_tok; |
| 18035 | const r_brace_src = block.tokenOffset(inst_data.src_tok); |
| 18036 | if (block.inlining == null and sema.func_is_naked) { |
| 18037 | assert(!block.isComptime()); |
| 18038 | if (block.wantSafety()) { |
| 18039 | // Calling a safety function from a naked function would not be legal. |
| 18040 | _ = try block.addNoOp(.trap); |
| 18041 | } else { |
| 18042 | try sema.analyzeUnreachable(block, r_brace_src, false); |
| 18043 | } |
| 18044 | return; |
| 18045 | } |
| 18046 | |
| 18047 | const operand = sema.resolveInst(inst_data.operand); |
| 18048 | const ret_ty_src = block.src(.{ .node_offset_fn_type_ret_ty = .zero }); |
| 18049 | const base_tag = sema.fn_ret_ty.optEuBaseType(zcu).zigTypeTag(zcu); |
| 18050 | if (base_tag == .noreturn) { |
| 18051 | const msg = msg: { |
| 18052 | const msg = try sema.errMsg(ret_ty_src, "function declared '{f}' implicitly returns", .{ |
| 18053 | sema.fn_ret_ty.fmt(pt), |
| 18054 | }); |
| 18055 | errdefer msg.destroy(sema.gpa); |
| 18056 | try sema.errNote(r_brace_src, msg, "control flow reaches end of body here", .{}); |
| 18057 | break :msg msg; |
| 18058 | }; |
| 18059 | return sema.failWithOwnedErrorMsg(block, msg); |
| 18060 | } else if (base_tag != .void) { |
| 18061 | const msg = msg: { |
| 18062 | const msg = try sema.errMsg(ret_ty_src, "function with non-void return type '{f}' implicitly returns", .{ |
| 18063 | sema.fn_ret_ty.fmt(pt), |
| 18064 | }); |
| 18065 | errdefer msg.destroy(sema.gpa); |
| 18066 | try sema.errNote(r_brace_src, msg, "control flow reaches end of body here", .{}); |
| 18067 | break :msg msg; |
| 18068 | }; |
| 18069 | return sema.failWithOwnedErrorMsg(block, msg); |
| 18070 | } |
| 18071 | |
| 18072 | return sema.analyzeRet(block, operand, r_brace_src, r_brace_src); |
| 18073 | } |
| 18074 | |
| 18075 | fn zirRetNode(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 18076 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 18077 | const operand = sema.resolveInst(inst_data.operand); |
| 18078 | const src = block.nodeOffset(inst_data.src_node); |
| 18079 | |
| 18080 | return sema.analyzeRet(block, operand, src, block.src(.{ .node_offset_return_operand = inst_data.src_node })); |
| 18081 | } |
| 18082 | |
| 18083 | fn zirRetLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 18084 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 18085 | const src = block.nodeOffset(inst_data.src_node); |
| 18086 | const ret_ptr = sema.resolveInst(inst_data.operand); |
| 18087 | |
| 18088 | if (block.isComptime() or block.inlining != null or sema.func_is_naked) { |
| 18089 | const operand = try sema.analyzeLoad(block, src, ret_ptr, src); |
| 18090 | return sema.analyzeRet(block, operand, src, block.src(.{ .node_offset_return_operand = inst_data.src_node })); |
| 18091 | } |
| 18092 | |
| 18093 | if (sema.wantErrorReturnTracing()) { |
| 18094 | const is_non_err = try sema.analyzePtrIsNonErr(block, src, ret_ptr); |
| 18095 | try sema.maybePushErrorTrace(block, src, is_non_err); |
| 18096 | } |
| 18097 | |
| 18098 | _ = try block.addUnOp(.ret_load, ret_ptr); |
| 18099 | } |
| 18100 | |
| 18101 | fn maybePushErrorTrace( |
| 18102 | sema: *Sema, |
| 18103 | parent_block: *Block, |
| 18104 | src: LazySrcLoc, |
| 18105 | is_non_err: Air.Inst.Ref, |
| 18106 | ) CompileError!void { |
| 18107 | const pt = sema.pt; |
| 18108 | |
| 18109 | const need_check = switch (is_non_err) { |
| 18110 | .bool_true => return, |
| 18111 | .bool_false => false, |
| 18112 | else => true, |
| 18113 | }; |
| 18114 | |
| 18115 | // This means we're returning something that might be an error! |
| 18116 | // This should only be possible with the `auto` cc, so we definitely have an error trace. |
| 18117 | assert(pt.zcu.intern_pool.funcAnalysisUnordered(sema.owner.unwrap().func).has_error_trace); |
| 18118 | |
| 18119 | const gpa = sema.gpa; |
| 18120 | const return_err_fn = Air.internedToRef(try sema.getStdLangValue(src, .returnError)); |
| 18121 | |
| 18122 | if (!need_check) { |
| 18123 | try sema.callBuiltin(parent_block, src, return_err_fn, .never_tail, &.{}, .@"error return"); |
| 18124 | return; |
| 18125 | } |
| 18126 | |
| 18127 | var err_block = parent_block.makeSubBlock(); |
| 18128 | defer err_block.instructions.deinit(gpa); |
| 18129 | try sema.callBuiltin(&err_block, src, return_err_fn, .never_tail, &.{}, .@"error return"); |
| 18130 | |
| 18131 | try parent_block.instructions.ensureUnusedCapacity(gpa, 1); |
| 18132 | |
| 18133 | try sema.air_instructions.ensureUnusedCapacity(gpa, 4); |
| 18134 | try sema.air_extra.ensureUnusedCapacity( |
| 18135 | gpa, |
| 18136 | @typeInfo(Air.Block).@"struct".field_names.len + |
| 18137 | 1 + // the main block contains only the `cond_br` |
| 18138 | @typeInfo(Air.CondBr).@"struct".field_names.len + |
| 18139 | 1 + // the non-error branch contains only a `br` |
| 18140 | err_block.instructions.items.len + 1, // the error branch contains the `returnError` call and a `br` |
| 18141 | ); |
| 18142 | |
| 18143 | const block_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len)); |
| 18144 | const cond_br_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len + 1)); |
| 18145 | const then_br_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len + 2)); |
| 18146 | const else_br_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len + 3)); |
| 18147 | |
| 18148 | const block_payload = sema.addExtraAssumeCapacity(Air.Block{ .body_len = 1 }); |
| 18149 | sema.air_extra.appendAssumeCapacity(@backingInt(cond_br_inst)); |
| 18150 | sema.air_instructions.appendAssumeCapacity(.{ |
| 18151 | .tag = .block, |
| 18152 | .data = .{ .ty_pl = .{ |
| 18153 | .ty = .void, |
| 18154 | .payload = block_payload, |
| 18155 | } }, |
| 18156 | }); |
| 18157 | |
| 18158 | const cond_br_payload = sema.addExtraAssumeCapacity(Air.CondBr{ |
| 18159 | .then_body_len = 1, |
| 18160 | .else_body_len = @intCast(err_block.instructions.items.len + 1), |
| 18161 | .branch_hints = .{ |
| 18162 | // Weight against error branch. |
| 18163 | .true = .likely, |
| 18164 | .false = .unlikely, |
| 18165 | // Code coverage is not valuable on either branch. |
| 18166 | .then_cov = .none, |
| 18167 | .else_cov = .none, |
| 18168 | }, |
| 18169 | }); |
| 18170 | sema.air_extra.appendAssumeCapacity(@backingInt(then_br_inst)); |
| 18171 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(err_block.instructions.items)); |
| 18172 | sema.air_extra.appendAssumeCapacity(@backingInt(else_br_inst)); |
| 18173 | sema.air_instructions.appendAssumeCapacity(.{ |
| 18174 | .tag = .cond_br, |
| 18175 | .data = .{ .pl_op = .{ |
| 18176 | .operand = is_non_err, |
| 18177 | .payload = cond_br_payload, |
| 18178 | } }, |
| 18179 | }); |
| 18180 | |
| 18181 | const br_inst_data: Air.Inst = .{ |
| 18182 | .tag = .br, |
| 18183 | .data = .{ .br = .{ |
| 18184 | .block_inst = block_inst, |
| 18185 | .operand = .void_value, |
| 18186 | } }, |
| 18187 | }; |
| 18188 | sema.air_instructions.appendAssumeCapacity(br_inst_data); // then_br_inst |
| 18189 | sema.air_instructions.appendAssumeCapacity(br_inst_data); // else_br_inst |
| 18190 | |
| 18191 | parent_block.instructions.appendAssumeCapacity(block_inst); |
| 18192 | } |
| 18193 | |
| 18194 | fn wantErrorReturnTracing(sema: *Sema) bool { |
| 18195 | const zcu = sema.pt.zcu; |
| 18196 | return sema.fn_ret_ty.isError(zcu) and zcu.comp.config.any_error_tracing; |
| 18197 | } |
| 18198 | |
| 18199 | fn zirSaveErrRetIndex(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 18200 | const pt = sema.pt; |
| 18201 | const zcu = pt.zcu; |
| 18202 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].save_err_ret_index; |
| 18203 | |
| 18204 | if (!block.ownerModule().error_tracing) return; |
| 18205 | |
| 18206 | // This is only relevant at runtime. |
| 18207 | if (block.isComptime() or block.is_typeof) return; |
| 18208 | |
| 18209 | const save_index = inst_data.operand == .none or b: { |
| 18210 | const operand = sema.resolveInst(inst_data.operand); |
| 18211 | const operand_ty = sema.typeOf(operand); |
| 18212 | break :b operand_ty.isError(zcu); |
| 18213 | }; |
| 18214 | |
| 18215 | if (save_index) |
| 18216 | block.error_return_trace_index = try sema.analyzeSaveErrRetIndex(block); |
| 18217 | } |
| 18218 | |
| 18219 | fn zirRestoreErrRetIndex(sema: *Sema, start_block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { |
| 18220 | const extra = sema.code.extraData(Zir.Inst.RestoreErrRetIndex, extended.operand).data; |
| 18221 | return sema.restoreErrRetIndex(start_block, start_block.nodeOffset(extra.src_node), extra.block, extra.operand); |
| 18222 | } |
| 18223 | |
| 18224 | /// If `operand` is non-error (or is `none`), restores the error return trace to |
| 18225 | /// its state at the point `block` was reached (or, if `block` is `none`, the |
| 18226 | /// point this function began execution). |
| 18227 | fn restoreErrRetIndex(sema: *Sema, start_block: *Block, src: LazySrcLoc, target_block: Zir.Inst.Ref, operand_zir: Zir.Inst.Ref) CompileError!void { |
| 18228 | const pt = sema.pt; |
| 18229 | const zcu = pt.zcu; |
| 18230 | |
| 18231 | const saved_index = if (target_block.toIndexAllowNone()) |zir_block| b: { |
| 18232 | var block = start_block; |
| 18233 | while (true) { |
| 18234 | if (block.label) |label| { |
| 18235 | if (label.zir_block == zir_block) { |
| 18236 | const target_trace_index = if (block.parent) |parent_block| |
| 18237 | parent_block.error_return_trace_index |
| 18238 | else |
| 18239 | sema.error_return_trace_index_on_fn_entry; |
| 18240 | |
| 18241 | if (start_block.error_return_trace_index != target_trace_index) |
| 18242 | break :b target_trace_index; |
| 18243 | |
| 18244 | return; // No need to restore |
| 18245 | } |
| 18246 | } |
| 18247 | block = block.parent.?; |
| 18248 | } |
| 18249 | } else b: { |
| 18250 | if (start_block.error_return_trace_index != sema.error_return_trace_index_on_fn_entry) |
| 18251 | break :b sema.error_return_trace_index_on_fn_entry; |
| 18252 | |
| 18253 | return; // No need to restore |
| 18254 | }; |
| 18255 | |
| 18256 | const operand = sema.resolveInstAllowNone(operand_zir); |
| 18257 | |
| 18258 | if (start_block.isComptime() or start_block.is_typeof) { |
| 18259 | const is_non_error = if (operand != .none) blk: { |
| 18260 | const is_non_error_inst = try sema.analyzeIsNonErr(start_block, src, operand); |
| 18261 | const cond_val = try sema.resolveDefinedValue(start_block, src, is_non_error_inst); |
| 18262 | break :blk cond_val.?.toBool(); |
| 18263 | } else true; // no operand means pop unconditionally |
| 18264 | |
| 18265 | if (is_non_error) return; |
| 18266 | |
| 18267 | const saved_index_val = try sema.resolveDefinedValue(start_block, src, saved_index); |
| 18268 | const saved_index_int = saved_index_val.?.toUnsignedInt(zcu); |
| 18269 | assert(saved_index_int <= sema.comptime_err_ret_trace.items.len); |
| 18270 | sema.comptime_err_ret_trace.items.len = @intCast(saved_index_int); |
| 18271 | return; |
| 18272 | } |
| 18273 | |
| 18274 | if (!zcu.intern_pool.funcAnalysisUnordered(sema.owner.unwrap().func).has_error_trace) return; |
| 18275 | if (!start_block.ownerModule().error_tracing) return; |
| 18276 | |
| 18277 | assert(saved_index != .none); // The .error_return_trace_index field was dropped somewhere |
| 18278 | |
| 18279 | return sema.popErrorReturnTrace(start_block, src, operand, saved_index); |
| 18280 | } |
| 18281 | |
| 18282 | fn addToInferredErrorSet(sema: *Sema, uncasted_operand: Air.Inst.Ref) !void { |
| 18283 | const pt = sema.pt; |
| 18284 | const zcu = pt.zcu; |
| 18285 | const ip = &zcu.intern_pool; |
| 18286 | assert(sema.fn_ret_ty.zigTypeTag(zcu) == .error_union); |
| 18287 | const err_set_ty = sema.fn_ret_ty.errorUnionSet(zcu).toIntern(); |
| 18288 | switch (err_set_ty) { |
| 18289 | .adhoc_inferred_error_set_type => { |
| 18290 | const ies = sema.fn_ret_ty_ies.?; |
| 18291 | assert(ies.func == .none); |
| 18292 | try sema.addToInferredErrorSetPtr(ies, sema.typeOf(uncasted_operand)); |
| 18293 | }, |
| 18294 | else => if (ip.isInferredErrorSetType(err_set_ty)) { |
| 18295 | const ies = sema.fn_ret_ty_ies.?; |
| 18296 | assert(ies.func == sema.owner.unwrap().func); |
| 18297 | try sema.addToInferredErrorSetPtr(ies, sema.typeOf(uncasted_operand)); |
| 18298 | }, |
| 18299 | } |
| 18300 | } |
| 18301 | |
| 18302 | fn addToInferredErrorSetPtr(sema: *Sema, ies: *InferredErrorSet, op_ty: Type) !void { |
| 18303 | const arena = sema.arena; |
| 18304 | const pt = sema.pt; |
| 18305 | const zcu = pt.zcu; |
| 18306 | const ip = &zcu.intern_pool; |
| 18307 | switch (op_ty.zigTypeTag(zcu)) { |
| 18308 | .error_set => try ies.addErrorSet(op_ty, ip, arena), |
| 18309 | .error_union => try ies.addErrorSet(op_ty.errorUnionSet(zcu), ip, arena), |
| 18310 | else => {}, |
| 18311 | } |
| 18312 | } |
| 18313 | |
| 18314 | fn analyzeRet( |
| 18315 | sema: *Sema, |
| 18316 | block: *Block, |
| 18317 | uncasted_operand: Air.Inst.Ref, |
| 18318 | src: LazySrcLoc, |
| 18319 | operand_src: LazySrcLoc, |
| 18320 | ) CompileError!void { |
| 18321 | // Special case for returning an error to an inferred error set; we need to |
| 18322 | // add the error tag to the inferred error set of the in-scope function, so |
| 18323 | // that the coercion below works correctly. |
| 18324 | const pt = sema.pt; |
| 18325 | const zcu = pt.zcu; |
| 18326 | if (sema.fn_ret_ty_ies != null and sema.fn_ret_ty.zigTypeTag(zcu) == .error_union) { |
| 18327 | try sema.addToInferredErrorSet(uncasted_operand); |
| 18328 | } |
| 18329 | const operand = sema.coerceExtra(block, sema.fn_ret_ty, uncasted_operand, operand_src, .{ .is_ret = true }) catch |err| switch (err) { |
| 18330 | error.NotCoercible => unreachable, |
| 18331 | else => |e| return e, |
| 18332 | }; |
| 18333 | |
| 18334 | if (block.isComptime()) { |
| 18335 | const inlining = block.inlining orelse { |
| 18336 | return sema.fail(block, src, "function called at runtime cannot return value at comptime", .{}); |
| 18337 | }; |
| 18338 | assert(!inlining.is_generic_instantiation); // can't `return` in a generic param/ret ty expr |
| 18339 | const ret_val = try sema.resolveConstValue(block, operand_src, operand, null); |
| 18340 | inlining.comptime_result = operand; |
| 18341 | |
| 18342 | if (sema.fn_ret_ty.isError(zcu) and ret_val.getErrorName(zcu) != .none) { |
| 18343 | try sema.comptime_err_ret_trace.append(src); |
| 18344 | } |
| 18345 | return error.ComptimeReturn; |
| 18346 | } |
| 18347 | |
| 18348 | if (block.inlining == null and sema.func_is_naked) return sema.failWithOwnedErrorMsg(block, msg: { |
| 18349 | const msg = try sema.errMsg(src, "cannot return from naked function", .{}); |
| 18350 | errdefer msg.destroy(sema.gpa); |
| 18351 | |
| 18352 | try sema.errNote(src, msg, "can only return using assembly", .{}); |
| 18353 | break :msg msg; |
| 18354 | }); |
| 18355 | |
| 18356 | if (sema.wantErrorReturnTracing()) { |
| 18357 | const is_non_err = try sema.analyzeIsNonErr(block, operand_src, operand); |
| 18358 | try sema.maybePushErrorTrace(block, src, is_non_err); |
| 18359 | } |
| 18360 | |
| 18361 | if (block.inlining) |inlining| { |
| 18362 | assert(!inlining.is_generic_instantiation); // can't `return` in a generic param/ret ty expr |
| 18363 | const br_inst = try block.addBr(inlining.merges.block_inst, operand); |
| 18364 | try inlining.merges.results.append(sema.gpa, operand); |
| 18365 | try inlining.merges.br_list.append(sema.gpa, br_inst.toIndex().?); |
| 18366 | try inlining.merges.src_locs.append(sema.gpa, operand_src); |
| 18367 | var body_block = block; |
| 18368 | while (body_block.parent) |parent| body_block = parent; |
| 18369 | if (body_block.runtime_cond == null and body_block.runtime_loop == null) { |
| 18370 | body_block.runtime_cond = block.runtime_cond orelse block.runtime_loop; |
| 18371 | body_block.runtime_loop = block.runtime_loop; |
| 18372 | } |
| 18373 | } else { |
| 18374 | try sema.validateRuntimeValue(block, operand_src, operand); |
| 18375 | const ret_tag: Air.Inst.Tag = if (block.wantSafety()) .ret_safe else .ret; |
| 18376 | _ = try block.addUnOp(ret_tag, operand); |
| 18377 | } |
| 18378 | } |
| 18379 | |
| 18380 | fn floatOpAllowed(tag: Zir.Inst.Tag) bool { |
| 18381 | // extend this swich as additional operators are implemented |
| 18382 | return switch (tag) { |
| 18383 | .add, .sub, .mul, .div, .div_exact, .div_trunc, .div_floor, .div_ceil, .mod, .rem, .mod_rem => true, |
| 18384 | else => false, |
| 18385 | }; |
| 18386 | } |
| 18387 | |
| 18388 | fn zirPtrType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 18389 | const pt = sema.pt; |
| 18390 | const zcu = pt.zcu; |
| 18391 | const comp = zcu.comp; |
| 18392 | const gpa = comp.gpa; |
| 18393 | const io = comp.io; |
| 18394 | const ip = &zcu.intern_pool; |
| 18395 | |
| 18396 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].ptr_type; |
| 18397 | const extra = sema.code.extraData(Zir.Inst.PtrType, inst_data.payload_index); |
| 18398 | const elem_ty_src = block.src(.{ .node_offset_ptr_elem = extra.data.src_node }); |
| 18399 | const sentinel_src = block.src(.{ .node_offset_ptr_sentinel = extra.data.src_node }); |
| 18400 | const align_src = block.src(.{ .node_offset_ptr_align = extra.data.src_node }); |
| 18401 | const addrspace_src = block.src(.{ .node_offset_ptr_addrspace = extra.data.src_node }); |
| 18402 | const bitoffset_src = block.src(.{ .node_offset_ptr_bitoffset = extra.data.src_node }); |
| 18403 | const hostsize_src = block.src(.{ .node_offset_ptr_hostsize = extra.data.src_node }); |
| 18404 | |
| 18405 | const elem_ty = blk: { |
| 18406 | const air_inst = sema.resolveInst(extra.data.elem_type); |
| 18407 | const ty = sema.analyzeAsType(block, elem_ty_src, .type, air_inst) catch |err| switch (err) { |
| 18408 | error.AlreadyReported => |e| { |
| 18409 | if (sema.err) |msg| { |
| 18410 | if (sema.typeOf(air_inst).isSinglePointer(zcu)) { |
| 18411 | try sema.errNote(elem_ty_src, msg, "use '.*' to dereference pointer", .{}); |
| 18412 | } |
| 18413 | } |
| 18414 | return e; |
| 18415 | }, |
| 18416 | else => |e| return e, |
| 18417 | }; |
| 18418 | assert(!ty.isGenericPoison()); |
| 18419 | break :blk ty; |
| 18420 | }; |
| 18421 | |
| 18422 | if (elem_ty.zigTypeTag(zcu) == .noreturn) |
| 18423 | return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{}); |
| 18424 | |
| 18425 | const target = zcu.getTarget(); |
| 18426 | |
| 18427 | var extra_i = extra.end; |
| 18428 | |
| 18429 | const sentinel = if (inst_data.flags.has_sentinel) blk: { |
| 18430 | const ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_i])); |
| 18431 | extra_i += 1; |
| 18432 | const coerced = try sema.coerce(block, elem_ty, sema.resolveInst(ref), sentinel_src); |
| 18433 | const val = try sema.resolveConstDefinedValue(block, sentinel_src, coerced, .{ .simple = .pointer_sentinel }); |
| 18434 | try checkSentinelType(sema, block, sentinel_src, elem_ty); |
| 18435 | if (val.canMutateComptimeVarState(zcu)) { |
| 18436 | const sentinel_name = try ip.getOrPutString(gpa, io, pt.tid, "sentinel", .no_embedded_nulls); |
| 18437 | return sema.failWithContainsReferenceToComptimeVar(block, sentinel_src, sentinel_name, "sentinel", val); |
| 18438 | } |
| 18439 | break :blk val.toIntern(); |
| 18440 | } else .none; |
| 18441 | |
| 18442 | const abi_align: Alignment = if (inst_data.flags.has_align) blk: { |
| 18443 | const ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_i])); |
| 18444 | extra_i += 1; |
| 18445 | const coerced = try sema.coerce(block, align_ty, sema.resolveInst(ref), align_src); |
| 18446 | const val = try sema.resolveConstDefinedValue(block, align_src, coerced, .{ .simple = .@"align" }); |
| 18447 | const align_bytes = val.toUnsignedInt(zcu); |
| 18448 | break :blk try sema.validateAlign(block, align_src, align_bytes); |
| 18449 | } else .none; |
| 18450 | |
| 18451 | const address_space: std.lang.AddressSpace = if (inst_data.flags.has_addrspace) blk: { |
| 18452 | const ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_i])); |
| 18453 | extra_i += 1; |
| 18454 | break :blk try sema.resolveAddressSpace(block, addrspace_src, ref, .pointer); |
| 18455 | } else if (elem_ty.zigTypeTag(zcu) == .@"fn" and target.cpu.arch == .avr) .flash else .generic; |
| 18456 | |
| 18457 | const bit_offset: u16 = if (inst_data.flags.has_bit_range) blk: { |
| 18458 | const ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_i])); |
| 18459 | extra_i += 1; |
| 18460 | const bit_offset = try sema.resolveInt(block, bitoffset_src, ref, .u16, .{ .simple = .type }); |
| 18461 | break :blk @intCast(bit_offset); |
| 18462 | } else 0; |
| 18463 | |
| 18464 | const host_size: u16 = if (inst_data.flags.has_bit_range) blk: { |
| 18465 | const ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_i])); |
| 18466 | extra_i += 1; |
| 18467 | const host_size = try sema.resolveInt(block, hostsize_src, ref, .u16, .{ .simple = .type }); |
| 18468 | break :blk @intCast(host_size); |
| 18469 | } else 0; |
| 18470 | |
| 18471 | if (host_size != 0) { |
| 18472 | try sema.ensureLayoutResolved(elem_ty, elem_ty_src, .bit_ptr_child); |
| 18473 | if (elem_ty.unpackable(zcu)) |reason| return sema.failWithOwnedErrorMsg(block, msg: { |
| 18474 | const msg = try sema.errMsg(elem_ty_src, "bit-pointer cannot refer to value of type '{f}'", .{elem_ty.fmt(pt)}); |
| 18475 | errdefer msg.destroy(sema.gpa); |
| 18476 | try sema.explainWhyTypeIsUnpackable(msg, elem_ty_src, reason); |
| 18477 | break :msg msg; |
| 18478 | }); |
| 18479 | const elem_bit_size = elem_ty.bitSize(zcu); |
| 18480 | if (bit_offset >= host_size * 8) { |
| 18481 | return sema.fail(block, bitoffset_src, "packed type '{f}' at bit offset {d} starts {d} bits after the end of a {d} byte host integer", .{ |
| 18482 | elem_ty.fmt(pt), bit_offset, bit_offset - host_size * 8, host_size, |
| 18483 | }); |
| 18484 | } |
| 18485 | if (elem_bit_size > host_size * 8 - bit_offset) { |
| 18486 | return sema.fail(block, bitoffset_src, "packed type '{f}' at bit offset {d} ends {d} bits after the end of a {d} byte host integer", .{ |
| 18487 | elem_ty.fmt(pt), bit_offset, elem_bit_size - (host_size * 8 - bit_offset), host_size, |
| 18488 | }); |
| 18489 | } |
| 18490 | } |
| 18491 | |
| 18492 | if (elem_ty.zigTypeTag(zcu) == .@"fn") { |
| 18493 | if (inst_data.size != .one) { |
| 18494 | return sema.fail(block, elem_ty_src, "function pointers must be single pointers", .{}); |
| 18495 | } |
| 18496 | } else if (inst_data.size != .one and elem_ty.zigTypeTag(zcu) == .@"opaque") { |
| 18497 | return sema.fail(block, elem_ty_src, "indexable pointer to opaque type '{f}' not allowed", .{elem_ty.fmt(pt)}); |
| 18498 | } |
| 18499 | |
| 18500 | const ty = try pt.ptrType(.{ |
| 18501 | .child = elem_ty.toIntern(), |
| 18502 | .sentinel = sentinel, |
| 18503 | .flags = .{ |
| 18504 | .alignment = abi_align, |
| 18505 | .address_space = address_space, |
| 18506 | .is_const = !inst_data.flags.is_mutable, |
| 18507 | .is_allowzero = inst_data.flags.is_allowzero, |
| 18508 | .is_volatile = inst_data.flags.is_volatile, |
| 18509 | .size = inst_data.size, |
| 18510 | }, |
| 18511 | .packed_offset = .{ |
| 18512 | .bit_offset = bit_offset, |
| 18513 | .host_size = host_size, |
| 18514 | }, |
| 18515 | }); |
| 18516 | return Air.internedToRef(ty.toIntern()); |
| 18517 | } |
| 18518 | |
| 18519 | fn zirStructInitEmpty(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 18520 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 18521 | const src = block.nodeOffset(inst_data.src_node); |
| 18522 | const ty_src = block.src(.{ .node_offset_init_ty = inst_data.src_node }); |
| 18523 | const obj_ty = try sema.resolveType(block, ty_src, inst_data.operand); |
| 18524 | const pt = sema.pt; |
| 18525 | const zcu = pt.zcu; |
| 18526 | |
| 18527 | try sema.ensureLayoutResolved(obj_ty, ty_src, .init); |
| 18528 | |
| 18529 | switch (obj_ty.zigTypeTag(zcu)) { |
| 18530 | .@"struct" => return sema.structInitEmpty(block, obj_ty, src, src), |
| 18531 | .array, .vector => return sema.arrayInitEmpty(block, src, obj_ty), |
| 18532 | .@"union" => return sema.fail(block, src, "union initializer must initialize one field", .{}), |
| 18533 | else => return sema.failWithArrayInitNotSupported(block, src, obj_ty), |
| 18534 | } |
| 18535 | } |
| 18536 | |
| 18537 | fn zirStructInitEmptyResult(sema: *Sema, block: *Block, inst: Zir.Inst.Index, is_byref: bool) CompileError!Air.Inst.Ref { |
| 18538 | const pt = sema.pt; |
| 18539 | const zcu = pt.zcu; |
| 18540 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 18541 | const src = block.nodeOffset(inst_data.src_node); |
| 18542 | |
| 18543 | // Generic poison means this is an untyped anonymous empty struct/array init |
| 18544 | const ty_operand = try sema.resolveTypeOrPoison(block, src, inst_data.operand) orelse { |
| 18545 | if (is_byref) { |
| 18546 | return sema.uavRef(.empty_tuple); |
| 18547 | } else { |
| 18548 | return .empty_tuple; |
| 18549 | } |
| 18550 | }; |
| 18551 | |
| 18552 | const init_ty = if (is_byref) ty: { |
| 18553 | const ptr_ty = ty_operand.optEuBaseType(zcu); |
| 18554 | assert(ptr_ty.zigTypeTag(zcu) == .pointer); // validated by a previous instruction |
| 18555 | switch (ptr_ty.ptrSize(zcu)) { |
| 18556 | // Use a zero-length array for a slice or many-ptr result |
| 18557 | .slice, .many => break :ty try pt.arrayType(.{ |
| 18558 | .len = 0, |
| 18559 | .child = ptr_ty.childType(zcu).toIntern(), |
| 18560 | .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none, |
| 18561 | }), |
| 18562 | // Just use the child type for a single-pointer or C-pointer result |
| 18563 | .one, .c => { |
| 18564 | const child = ptr_ty.childType(zcu); |
| 18565 | if (child.toIntern() == .anyopaque_type) { |
| 18566 | // ...unless that child is anyopaque, in which case this is equivalent to an untyped init. |
| 18567 | // `.{}` is an empty tuple. |
| 18568 | if (is_byref) { |
| 18569 | return sema.uavRef(.empty_tuple); |
| 18570 | } else { |
| 18571 | return .empty_tuple; |
| 18572 | } |
| 18573 | } |
| 18574 | break :ty child; |
| 18575 | }, |
| 18576 | } |
| 18577 | if (!ptr_ty.isSlice(zcu)) { |
| 18578 | break :ty ptr_ty.childType(zcu); |
| 18579 | } |
| 18580 | // To make `&.{}` a `[:s]T`, the init should be a `[0:s]T`. |
| 18581 | break :ty try pt.arrayType(.{ |
| 18582 | .len = 0, |
| 18583 | .sentinel = if (ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none, |
| 18584 | .child = ptr_ty.childType(zcu).toIntern(), |
| 18585 | }); |
| 18586 | } else ty_operand; |
| 18587 | |
| 18588 | try sema.ensureLayoutResolved(init_ty, src, .init); |
| 18589 | |
| 18590 | const obj_ty = init_ty.optEuBaseType(zcu); |
| 18591 | |
| 18592 | const empty_ref = switch (obj_ty.zigTypeTag(zcu)) { |
| 18593 | .@"struct" => try sema.structInitEmpty(block, obj_ty, src, src), |
| 18594 | .array, .vector => try sema.arrayInitEmpty(block, src, obj_ty), |
| 18595 | .@"union" => return sema.fail(block, src, "union initializer must initialize one field", .{}), |
| 18596 | else => return sema.failWithArrayInitNotSupported(block, src, obj_ty), |
| 18597 | }; |
| 18598 | const init_ref = try sema.coerce(block, init_ty, empty_ref, src); |
| 18599 | |
| 18600 | if (is_byref) { |
| 18601 | return sema.uavRef(sema.resolveValue(init_ref).?); |
| 18602 | } else { |
| 18603 | return init_ref; |
| 18604 | } |
| 18605 | } |
| 18606 | |
| 18607 | /// Asserts that the layout of `struct_ty` is already resolved. |
| 18608 | fn structInitEmpty( |
| 18609 | sema: *Sema, |
| 18610 | block: *Block, |
| 18611 | struct_ty: Type, |
| 18612 | dest_src: LazySrcLoc, |
| 18613 | init_src: LazySrcLoc, |
| 18614 | ) CompileError!Air.Inst.Ref { |
| 18615 | const pt = sema.pt; |
| 18616 | const zcu = pt.zcu; |
| 18617 | const gpa = sema.gpa; |
| 18618 | // This logic must be synchronized with that in `zirStructInit`. |
| 18619 | struct_ty.assertHasLayout(zcu); |
| 18620 | |
| 18621 | // The init values to use for the struct instance. |
| 18622 | const field_inits = try gpa.alloc(Air.Inst.Ref, struct_ty.structFieldCount(zcu)); |
| 18623 | defer gpa.free(field_inits); |
| 18624 | @memset(field_inits, .none); |
| 18625 | |
| 18626 | // Maps field index in the struct declaration to the field index in the initialization expression. |
| 18627 | const field_assign_idxs = try gpa.alloc(?usize, struct_ty.structFieldCount(zcu)); |
| 18628 | defer gpa.free(field_assign_idxs); |
| 18629 | @memset(field_assign_idxs, null); |
| 18630 | |
| 18631 | return sema.finishStructInit(block, init_src, dest_src, field_inits, field_assign_idxs, struct_ty, struct_ty, false); |
| 18632 | } |
| 18633 | |
| 18634 | fn arrayInitEmpty(sema: *Sema, block: *Block, src: LazySrcLoc, obj_ty: Type) CompileError!Air.Inst.Ref { |
| 18635 | const pt = sema.pt; |
| 18636 | const zcu = pt.zcu; |
| 18637 | const arr_len = obj_ty.arrayLen(zcu); |
| 18638 | if (arr_len != 0) { |
| 18639 | if (obj_ty.zigTypeTag(zcu) == .array) { |
| 18640 | return sema.fail(block, src, "expected {d} array elements; found 0", .{arr_len}); |
| 18641 | } else { |
| 18642 | return sema.fail(block, src, "expected {d} vector elements; found 0", .{arr_len}); |
| 18643 | } |
| 18644 | } |
| 18645 | return .fromValue(try pt.aggregateValue(obj_ty, &.{})); |
| 18646 | } |
| 18647 | |
| 18648 | fn zirUnionInit(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 18649 | const pt = sema.pt; |
| 18650 | const zcu = pt.zcu; |
| 18651 | const ip = &zcu.intern_pool; |
| 18652 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 18653 | const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 18654 | const field_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 18655 | const payload_src = block.builtinCallArgSrc(inst_data.src_node, 2); |
| 18656 | const extra = sema.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data; |
| 18657 | const union_ty = try sema.resolveType(block, ty_src, extra.union_type); |
| 18658 | if (union_ty.zigTypeTag(pt.zcu) != .@"union") { |
| 18659 | return sema.fail(block, ty_src, "expected union type, found '{f}'", .{union_ty.fmt(pt)}); |
| 18660 | } |
| 18661 | union_ty.assertHasLayout(zcu); // from a previous `field_type_ref` instruction |
| 18662 | const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .union_field_names }); |
| 18663 | const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_src); |
| 18664 | const field_ty: Type = .fromInterned(zcu.typeToUnion(union_ty).?.field_types.get(ip)[field_index]); |
| 18665 | |
| 18666 | const payload = try sema.coerce(block, field_ty, sema.resolveInst(extra.init), payload_src); |
| 18667 | |
| 18668 | if (union_ty.containerLayout(zcu) == .@"packed") { |
| 18669 | return sema.bitCastUnchecked(block, union_ty, payload); |
| 18670 | } |
| 18671 | |
| 18672 | if (sema.resolveValue(payload)) |payload_val| { |
| 18673 | const tag_ty = union_ty.unionTagTypeHypothetical(zcu); |
| 18674 | const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index); |
| 18675 | return .fromValue(try pt.unionValue(union_ty, tag_val, payload_val)); |
| 18676 | } |
| 18677 | |
| 18678 | try sema.requireRuntimeBlock(block, payload_src, null); |
| 18679 | return block.addUnionInit(union_ty, field_index, payload); |
| 18680 | } |
| 18681 | |
| 18682 | fn zirStructInit( |
| 18683 | sema: *Sema, |
| 18684 | block: *Block, |
| 18685 | inst: Zir.Inst.Index, |
| 18686 | is_ref: bool, |
| 18687 | ) CompileError!Air.Inst.Ref { |
| 18688 | const pt = sema.pt; |
| 18689 | const zcu = pt.zcu; |
| 18690 | const comp = zcu.comp; |
| 18691 | const gpa = comp.gpa; |
| 18692 | const io = comp.io; |
| 18693 | const ip = &zcu.intern_pool; |
| 18694 | |
| 18695 | const zir_datas = sema.code.instructions.items(.data); |
| 18696 | const inst_data = zir_datas[@backingInt(inst)].pl_node; |
| 18697 | const extra = sema.code.extraData(Zir.Inst.StructInit, inst_data.payload_index); |
| 18698 | const src = block.nodeOffset(inst_data.src_node); |
| 18699 | |
| 18700 | const first_item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end).data; |
| 18701 | const first_field_type_data = zir_datas[@backingInt(first_item.field_type)].pl_node; |
| 18702 | const first_field_type_extra = sema.code.extraData(Zir.Inst.FieldType, first_field_type_data.payload_index).data; |
| 18703 | const result_ty = try sema.resolveTypeOrPoison(block, src, first_field_type_extra.container_type) orelse { |
| 18704 | // The type wasn't actually known, so treat this as an anon struct init. |
| 18705 | return sema.structInitAnon(block, src, inst, .typed_init, extra.data, extra.end, is_ref); |
| 18706 | }; |
| 18707 | try sema.ensureLayoutResolved(result_ty, src, .init); |
| 18708 | const resolved_ty = result_ty.optEuBaseType(zcu); |
| 18709 | |
| 18710 | if (resolved_ty.zigTypeTag(zcu) == .@"struct") { |
| 18711 | // This logic must be synchronized with that in `zirStructInitEmpty`. |
| 18712 | |
| 18713 | // Maps field index to field_type index of where it was already initialized. |
| 18714 | // For making sure all fields are accounted for and no fields are duplicated. |
| 18715 | const found_fields = try gpa.alloc(Zir.Inst.Index, resolved_ty.structFieldCount(zcu)); |
| 18716 | defer gpa.free(found_fields); |
| 18717 | |
| 18718 | // The init values to use for the struct instance. |
| 18719 | const field_inits = try gpa.alloc(Air.Inst.Ref, resolved_ty.structFieldCount(zcu)); |
| 18720 | defer gpa.free(field_inits); |
| 18721 | @memset(field_inits, .none); |
| 18722 | |
| 18723 | // Maps field index in the struct declaration to the field index in the initialization expression. |
| 18724 | const field_assign_idxs = try gpa.alloc(?usize, resolved_ty.structFieldCount(zcu)); |
| 18725 | defer gpa.free(field_assign_idxs); |
| 18726 | @memset(field_assign_idxs, null); |
| 18727 | |
| 18728 | var field_i: u32 = 0; |
| 18729 | var extra_index = extra.end; |
| 18730 | |
| 18731 | while (field_i < extra.data.fields_len) : (field_i += 1) { |
| 18732 | const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra_index); |
| 18733 | extra_index = item.end; |
| 18734 | |
| 18735 | const field_type_data = zir_datas[@backingInt(item.data.field_type)].pl_node; |
| 18736 | const field_src = block.src(.{ .node_offset_initializer = field_type_data.src_node }); |
| 18737 | const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; |
| 18738 | const field_name = try ip.getOrPutString( |
| 18739 | gpa, |
| 18740 | io, |
| 18741 | pt.tid, |
| 18742 | sema.code.nullTerminatedString(field_type_extra.name_start), |
| 18743 | .no_embedded_nulls, |
| 18744 | ); |
| 18745 | const field_index = if (resolved_ty.isTuple(zcu)) |
| 18746 | try sema.tupleFieldIndex(block, resolved_ty, field_name, field_src) |
| 18747 | else |
| 18748 | try sema.structFieldIndex(block, resolved_ty, field_name, field_src); |
| 18749 | assert(field_inits[field_index] == .none); |
| 18750 | field_assign_idxs[field_index] = field_i; |
| 18751 | found_fields[field_index] = item.data.field_type; |
| 18752 | const uncoerced_init = sema.resolveInst(item.data.init); |
| 18753 | const field_ty = resolved_ty.fieldType(field_index, zcu); |
| 18754 | field_inits[field_index] = try sema.coerce(block, field_ty, uncoerced_init, field_src); |
| 18755 | if (resolved_ty.structFieldIsComptime(field_index, zcu)) { |
| 18756 | const default_value = (try resolved_ty.structFieldValueComptime(pt, field_index)).?; |
| 18757 | const init_val = sema.resolveValue(field_inits[field_index]) orelse { |
| 18758 | return sema.failWithNeededComptime(block, field_src, .{ .simple = .stored_to_comptime_field }); |
| 18759 | }; |
| 18760 | if (!init_val.eql(default_value, resolved_ty.fieldType(field_index, zcu), zcu)) { |
| 18761 | return sema.failWithInvalidComptimeFieldStore(block, field_src, resolved_ty, field_index); |
| 18762 | } |
| 18763 | } |
| 18764 | } |
| 18765 | |
| 18766 | return sema.finishStructInit(block, src, src, field_inits, field_assign_idxs, resolved_ty, result_ty, is_ref); |
| 18767 | } else if (resolved_ty.zigTypeTag(zcu) == .@"union") { |
| 18768 | if (extra.data.fields_len != 1) { |
| 18769 | return sema.fail(block, src, "union initialization expects exactly one field", .{}); |
| 18770 | } |
| 18771 | |
| 18772 | const item = sema.code.extraData(Zir.Inst.StructInit.Item, extra.end); |
| 18773 | |
| 18774 | const field_type_data = zir_datas[@backingInt(item.data.field_type)].pl_node; |
| 18775 | const field_src = block.src(.{ .node_offset_initializer = field_type_data.src_node }); |
| 18776 | const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index).data; |
| 18777 | const field_name = try ip.getOrPutString( |
| 18778 | gpa, |
| 18779 | io, |
| 18780 | pt.tid, |
| 18781 | sema.code.nullTerminatedString(field_type_extra.name_start), |
| 18782 | .no_embedded_nulls, |
| 18783 | ); |
| 18784 | const field_index = try sema.unionFieldIndex(block, resolved_ty, field_name, field_src); |
| 18785 | const tag_ty = resolved_ty.unionTagTypeHypothetical(zcu); |
| 18786 | const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index); |
| 18787 | const field_ty: Type = .fromInterned(zcu.typeToUnion(resolved_ty).?.field_types.get(ip)[field_index]); |
| 18788 | |
| 18789 | if (field_ty.classify(zcu) == .no_possible_value) { |
| 18790 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 18791 | const msg = try sema.errMsg(src, "cannot initialize union field with uninstantiable type '{f}'", .{field_ty.fmt(pt)}); |
| 18792 | errdefer msg.destroy(sema.gpa); |
| 18793 | |
| 18794 | try sema.addFieldErrNote(resolved_ty, field_index, msg, "field '{f}' declared here", .{ |
| 18795 | field_name.fmt(ip), |
| 18796 | }); |
| 18797 | try sema.addDeclaredHereNote(msg, resolved_ty); |
| 18798 | break :msg msg; |
| 18799 | }); |
| 18800 | } |
| 18801 | |
| 18802 | const uncoerced_init_inst = sema.resolveInst(item.data.init); |
| 18803 | const init_inst = try sema.coerce(block, field_ty, uncoerced_init_inst, field_src); |
| 18804 | |
| 18805 | if (resolved_ty.containerLayout(zcu) == .@"packed") { |
| 18806 | const union_val = try sema.bitCastUnchecked(block, resolved_ty, init_inst); |
| 18807 | const result_val = try sema.coerce(block, result_ty, union_val, src); |
| 18808 | if (is_ref) { |
| 18809 | return sema.analyzeRef(block, src, result_val, .none); |
| 18810 | } else { |
| 18811 | return result_val; |
| 18812 | } |
| 18813 | } |
| 18814 | |
| 18815 | if (sema.resolveValue(init_inst)) |val| { |
| 18816 | const struct_val = Value.fromInterned(try pt.internUnion(.{ |
| 18817 | .ty = resolved_ty.toIntern(), |
| 18818 | .tag = tag_val.toIntern(), |
| 18819 | .val = val.toIntern(), |
| 18820 | })); |
| 18821 | const final_val_inst = try sema.coerce(block, result_ty, Air.internedToRef(struct_val.toIntern()), src); |
| 18822 | const final_val = sema.resolveValue(final_val_inst).?; |
| 18823 | return sema.addConstantMaybeRef(final_val, is_ref); |
| 18824 | } |
| 18825 | |
| 18826 | if (resolved_ty.comptimeOnly(zcu)) { |
| 18827 | return sema.failWithNeededComptime(block, field_src, .{ .comptime_only = .{ |
| 18828 | .ty = resolved_ty, |
| 18829 | .msg = .union_init, |
| 18830 | } }); |
| 18831 | } |
| 18832 | |
| 18833 | try sema.validateRuntimeValue(block, field_src, init_inst); |
| 18834 | |
| 18835 | if (is_ref) { |
| 18836 | const target = zcu.getTarget(); |
| 18837 | const alloc_ty = try pt.ptrType(.{ |
| 18838 | .child = result_ty.toIntern(), |
| 18839 | .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, |
| 18840 | }); |
| 18841 | const alloc = try block.addTy(.alloc, alloc_ty); |
| 18842 | const base_ptr = try sema.optEuBasePtrInit(block, alloc, src); |
| 18843 | const field_ptr = try sema.unionFieldPtr(block, field_src, base_ptr, field_name, field_src, resolved_ty, true); |
| 18844 | try sema.storePtr(block, src, field_ptr, init_inst); |
| 18845 | return sema.makePtrConst(block, alloc); |
| 18846 | } |
| 18847 | |
| 18848 | try sema.requireRuntimeBlock(block, src, null); |
| 18849 | const union_val = try block.addUnionInit(resolved_ty, field_index, init_inst); |
| 18850 | return sema.coerce(block, result_ty, union_val, src); |
| 18851 | } |
| 18852 | unreachable; |
| 18853 | } |
| 18854 | |
| 18855 | fn finishStructInit( |
| 18856 | sema: *Sema, |
| 18857 | block: *Block, |
| 18858 | init_src: LazySrcLoc, |
| 18859 | dest_src: LazySrcLoc, |
| 18860 | field_inits: []Air.Inst.Ref, |
| 18861 | field_assign_idxs: []?usize, |
| 18862 | struct_ty: Type, |
| 18863 | result_ty: Type, |
| 18864 | is_ref: bool, |
| 18865 | ) CompileError!Air.Inst.Ref { |
| 18866 | const pt = sema.pt; |
| 18867 | const zcu = pt.zcu; |
| 18868 | const ip = &zcu.intern_pool; |
| 18869 | |
| 18870 | var root_msg: ?*Zcu.ErrorMsg = null; |
| 18871 | errdefer if (root_msg) |msg| msg.destroy(sema.gpa); |
| 18872 | |
| 18873 | switch (ip.indexToKey(struct_ty.toIntern())) { |
| 18874 | .tuple_type => |tuple| { |
| 18875 | // We can't get the slices, as the coercion may invalidate them. |
| 18876 | for (0..tuple.types.len) |i| { |
| 18877 | if (field_inits[i] != .none) { |
| 18878 | // Coerce the init value to the field type. |
| 18879 | const field_src = block.src(.{ .init_elem = .{ |
| 18880 | .init_node_offset = init_src.offset.node_offset.x, |
| 18881 | .elem_index = @intCast(i), |
| 18882 | } }); |
| 18883 | const field_ty: Type = .fromInterned(tuple.types.get(ip)[i]); |
| 18884 | field_inits[i] = try sema.coerce(block, field_ty, field_inits[i], field_src); |
| 18885 | continue; |
| 18886 | } |
| 18887 | |
| 18888 | const default_val = tuple.values.get(ip)[i]; |
| 18889 | |
| 18890 | if (default_val == .none) { |
| 18891 | const template = "missing tuple field with index {d}"; |
| 18892 | if (root_msg) |msg| { |
| 18893 | try sema.errNote(init_src, msg, template, .{i}); |
| 18894 | } else { |
| 18895 | root_msg = try sema.errMsg(init_src, template, .{i}); |
| 18896 | } |
| 18897 | } else { |
| 18898 | field_inits[i] = Air.internedToRef(default_val); |
| 18899 | } |
| 18900 | } |
| 18901 | }, |
| 18902 | .struct_type => { |
| 18903 | const struct_type = ip.loadStructType(struct_ty.toIntern()); |
| 18904 | for (0..struct_type.field_types.len) |i| { |
| 18905 | if (field_inits[i] != .none) { |
| 18906 | // Coerce the init value to the field type. |
| 18907 | const field_src = block.src(.{ .init_elem = .{ |
| 18908 | .init_node_offset = init_src.offset.node_offset.x, |
| 18909 | .elem_index = @intCast(i), |
| 18910 | } }); |
| 18911 | const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); |
| 18912 | field_inits[i] = try sema.coerce(block, field_ty, field_inits[i], field_src); |
| 18913 | continue; |
| 18914 | } |
| 18915 | |
| 18916 | if (struct_type.field_is_comptime_bits.get(ip, i)) { |
| 18917 | field_inits[i] = .fromIntern(struct_type.field_defaults.get(ip)[i]); |
| 18918 | continue; |
| 18919 | } |
| 18920 | |
| 18921 | try sema.ensureStructDefaultsResolved(struct_ty, init_src); |
| 18922 | |
| 18923 | const field_default: InternPool.Index = d: { |
| 18924 | if (struct_type.field_defaults.len == 0) break :d .none; |
| 18925 | break :d struct_type.field_defaults.get(ip)[i]; |
| 18926 | }; |
| 18927 | if (field_default != .none) { |
| 18928 | field_inits[i] = .fromIntern(field_default); |
| 18929 | continue; |
| 18930 | } |
| 18931 | |
| 18932 | const field_name = struct_type.field_names.get(ip)[i]; |
| 18933 | const template = "missing struct field: {f}"; |
| 18934 | const args = .{field_name.fmt(ip)}; |
| 18935 | if (root_msg) |msg| { |
| 18936 | try sema.errNote(init_src, msg, template, args); |
| 18937 | } else { |
| 18938 | root_msg = try sema.errMsg(init_src, template, args); |
| 18939 | } |
| 18940 | } |
| 18941 | }, |
| 18942 | else => unreachable, |
| 18943 | } |
| 18944 | |
| 18945 | if (root_msg) |msg| { |
| 18946 | try sema.addDeclaredHereNote(msg, struct_ty); |
| 18947 | root_msg = null; |
| 18948 | return sema.failWithOwnedErrorMsg(block, msg); |
| 18949 | } |
| 18950 | |
| 18951 | // Find which field forces the expression to be runtime, if any. |
| 18952 | const opt_runtime_index = for (field_inits, field_assign_idxs) |field_init, field_assign| { |
| 18953 | if (!(try sema.isComptimeKnown(field_init))) { |
| 18954 | break field_assign; |
| 18955 | } |
| 18956 | } else null; |
| 18957 | |
| 18958 | const runtime_index = opt_runtime_index orelse switch (struct_ty.containerLayout(zcu)) { |
| 18959 | .auto, .@"extern" => { |
| 18960 | const elems = try sema.arena.alloc(InternPool.Index, field_inits.len); |
| 18961 | for (elems, field_inits) |*elem, field_init| { |
| 18962 | elem.* = sema.resolveValue(field_init).?.toIntern(); |
| 18963 | } |
| 18964 | const struct_val = try pt.aggregateValue(struct_ty, elems); |
| 18965 | const final_val_ref = try sema.coerce(block, result_ty, .fromValue(struct_val), init_src); |
| 18966 | return sema.addConstantMaybeRef(sema.resolveValue(final_val_ref).?, is_ref); |
| 18967 | }, |
| 18968 | .@"packed" => { |
| 18969 | const buf = try sema.arena.alloc(u8, @intCast(@divCeil(struct_ty.bitSize(zcu), 8))); |
| 18970 | @memset(buf, 0); |
| 18971 | var bit_offset: u16 = 0; |
| 18972 | for (field_inits) |field_init| { |
| 18973 | const field_val = sema.resolveValue(field_init).?; |
| 18974 | field_val.writeToPackedMemory(zcu, buf, bit_offset); |
| 18975 | bit_offset += @intCast(field_val.typeOf(zcu).bitSize(zcu)); |
| 18976 | } |
| 18977 | assert(bit_offset == struct_ty.bitSize(zcu)); |
| 18978 | const struct_val: Value = try .readFromPackedMemory(struct_ty, pt, buf, 0); |
| 18979 | const final_val_ref = try sema.coerce(block, result_ty, .fromValue(struct_val), init_src); |
| 18980 | return sema.addConstantMaybeRef(sema.resolveValue(final_val_ref).?, is_ref); |
| 18981 | }, |
| 18982 | }; |
| 18983 | |
| 18984 | if (struct_ty.comptimeOnly(zcu)) { |
| 18985 | return sema.failWithNeededComptime(block, block.src(.{ .init_elem = .{ |
| 18986 | .init_node_offset = init_src.offset.node_offset.x, |
| 18987 | .elem_index = @intCast(runtime_index), |
| 18988 | } }), .{ .comptime_only = .{ |
| 18989 | .ty = struct_ty, |
| 18990 | .msg = .struct_init, |
| 18991 | } }); |
| 18992 | } |
| 18993 | |
| 18994 | for (field_inits) |field_init| { |
| 18995 | try sema.validateRuntimeValue(block, dest_src, field_init); |
| 18996 | } |
| 18997 | |
| 18998 | if (is_ref) { |
| 18999 | const target = zcu.getTarget(); |
| 19000 | const alloc_ty = try pt.ptrType(.{ |
| 19001 | .child = result_ty.toIntern(), |
| 19002 | .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, |
| 19003 | }); |
| 19004 | const alloc = try block.addTy(.alloc, alloc_ty); |
| 19005 | const base_ptr = try sema.optEuBasePtrInit(block, alloc, init_src); |
| 19006 | for (field_inits, 0..) |field_init, i_usize| { |
| 19007 | const i: u32 = @intCast(i_usize); |
| 19008 | const field_ptr = try sema.structFieldPtrByIndex(block, dest_src, base_ptr, i, struct_ty); |
| 19009 | try sema.storePtr(block, dest_src, field_ptr, field_init); |
| 19010 | } |
| 19011 | |
| 19012 | return sema.makePtrConst(block, alloc); |
| 19013 | } |
| 19014 | |
| 19015 | try sema.requireRuntimeBlock(block, dest_src, block.src(.{ .init_elem = .{ |
| 19016 | .init_node_offset = init_src.offset.node_offset.x, |
| 19017 | .elem_index = @intCast(runtime_index), |
| 19018 | } })); |
| 19019 | const struct_val = try block.addAggregateInit(struct_ty, field_inits); |
| 19020 | return sema.coerce(block, result_ty, struct_val, init_src); |
| 19021 | } |
| 19022 | |
| 19023 | fn zirStructInitAnon( |
| 19024 | sema: *Sema, |
| 19025 | block: *Block, |
| 19026 | inst: Zir.Inst.Index, |
| 19027 | ) CompileError!Air.Inst.Ref { |
| 19028 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 19029 | const src = block.nodeOffset(inst_data.src_node); |
| 19030 | const extra = sema.code.extraData(Zir.Inst.StructInitAnon, inst_data.payload_index); |
| 19031 | return sema.structInitAnon(block, src, inst, .anon_init, extra.data, extra.end, false); |
| 19032 | } |
| 19033 | |
| 19034 | fn structInitAnon( |
| 19035 | sema: *Sema, |
| 19036 | block: *Block, |
| 19037 | src: LazySrcLoc, |
| 19038 | inst: Zir.Inst.Index, |
| 19039 | /// It is possible for a typed struct_init to be downgraded to an anonymous init due to a |
| 19040 | /// generic poison type. In this case, we need to know to interpret the extra data differently. |
| 19041 | comptime kind: enum { anon_init, typed_init }, |
| 19042 | extra_data: switch (kind) { |
| 19043 | .anon_init => Zir.Inst.StructInitAnon, |
| 19044 | .typed_init => Zir.Inst.StructInit, |
| 19045 | }, |
| 19046 | extra_end: usize, |
| 19047 | is_ref: bool, |
| 19048 | ) CompileError!Air.Inst.Ref { |
| 19049 | const pt = sema.pt; |
| 19050 | const zcu = pt.zcu; |
| 19051 | const comp = zcu.comp; |
| 19052 | const gpa = comp.gpa; |
| 19053 | const io = comp.io; |
| 19054 | const ip = &zcu.intern_pool; |
| 19055 | |
| 19056 | const zir_datas = sema.code.instructions.items(.data); |
| 19057 | |
| 19058 | const types = try sema.arena.alloc(InternPool.Index, extra_data.fields_len); |
| 19059 | const values = try sema.arena.alloc(InternPool.Index, types.len); |
| 19060 | const names = try sema.arena.alloc(InternPool.NullTerminatedString, types.len); |
| 19061 | |
| 19062 | var any_values = false; |
| 19063 | |
| 19064 | // Find which field forces the expression to be runtime, if any. |
| 19065 | const opt_runtime_index = rs: { |
| 19066 | var runtime_index: ?usize = null; |
| 19067 | var extra_index = extra_end; |
| 19068 | for (types, values, names, 0..) |*field_ty, *field_val, *field_name, i_usize| { |
| 19069 | const item = switch (kind) { |
| 19070 | .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index), |
| 19071 | .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index), |
| 19072 | }; |
| 19073 | extra_index = item.end; |
| 19074 | |
| 19075 | const name = switch (kind) { |
| 19076 | .anon_init => sema.code.nullTerminatedString(item.data.field_name), |
| 19077 | .typed_init => name: { |
| 19078 | // `item.data.field_type` references a `field_type` instruction |
| 19079 | const field_type_data = zir_datas[@backingInt(item.data.field_type)].pl_node; |
| 19080 | const field_type_extra = sema.code.extraData(Zir.Inst.FieldType, field_type_data.payload_index); |
| 19081 | break :name sema.code.nullTerminatedString(field_type_extra.data.name_start); |
| 19082 | }, |
| 19083 | }; |
| 19084 | |
| 19085 | field_name.* = try zcu.intern_pool.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls); |
| 19086 | |
| 19087 | const init = sema.resolveInst(item.data.init); |
| 19088 | field_ty.* = sema.typeOf(init).toIntern(); |
| 19089 | if (Type.fromInterned(field_ty.*).zigTypeTag(zcu) == .@"opaque") { |
| 19090 | const msg = msg: { |
| 19091 | const field_src = block.src(.{ .init_elem = .{ |
| 19092 | .init_node_offset = src.offset.node_offset.x, |
| 19093 | .elem_index = @intCast(i_usize), |
| 19094 | } }); |
| 19095 | const msg = try sema.errMsg(field_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); |
| 19096 | errdefer msg.destroy(sema.gpa); |
| 19097 | |
| 19098 | try sema.addDeclaredHereNote(msg, .fromInterned(field_ty.*)); |
| 19099 | break :msg msg; |
| 19100 | }; |
| 19101 | return sema.failWithOwnedErrorMsg(block, msg); |
| 19102 | } |
| 19103 | if (sema.resolveValue(init)) |init_val| { |
| 19104 | field_val.* = init_val.toIntern(); |
| 19105 | any_values = true; |
| 19106 | } else { |
| 19107 | field_val.* = .none; |
| 19108 | runtime_index = @intCast(i_usize); |
| 19109 | } |
| 19110 | } |
| 19111 | break :rs runtime_index; |
| 19112 | }; |
| 19113 | |
| 19114 | // A field can't be `comptime` if it references a `comptime var` but the aggregate can still be comptime-known. |
| 19115 | // Replace these fields with `.none` only for generating the type. |
| 19116 | const values_no_comptime = if (!any_values) values else blk: { |
| 19117 | const new_values = try sema.arena.alloc(InternPool.Index, types.len); |
| 19118 | for (values, new_values) |val, *new_val| { |
| 19119 | if (val != .none and Value.fromInterned(val).canMutateComptimeVarState(zcu)) { |
| 19120 | new_val.* = .none; |
| 19121 | } else new_val.* = val; |
| 19122 | } |
| 19123 | break :blk new_values; |
| 19124 | }; |
| 19125 | |
| 19126 | // We treat anonymous struct types as reified types, because there are similarities: they have |
| 19127 | // no captures, and instead use a form of structural equivalence which we can easy represent by |
| 19128 | // hashing the field names/types/values. They also perform layout resolution immediately. These |
| 19129 | // similarities mean that other code should actually treat anon struct types and reified struct |
| 19130 | // types identically anyway, so sharing the representation makes everything simpler. |
| 19131 | const type_hash: u64 = hash: { |
| 19132 | var hasher = std.hash.Wyhash.init(0); |
| 19133 | hasher.update(std.mem.sliceAsBytes(types)); |
| 19134 | hasher.update(std.mem.sliceAsBytes(values_no_comptime)); |
| 19135 | hasher.update(std.mem.sliceAsBytes(names)); |
| 19136 | break :hash hasher.final(); |
| 19137 | }; |
| 19138 | const tracked_inst = try block.trackZir(inst); |
| 19139 | const struct_ty: Type = switch (try ip.getReifiedStructType(gpa, io, pt.tid, .{ |
| 19140 | .zir_index = tracked_inst, |
| 19141 | .type_hash = type_hash, |
| 19142 | .fields_len = extra_data.fields_len, |
| 19143 | .layout = .auto, |
| 19144 | .any_comptime_fields = any_values, |
| 19145 | .any_field_defaults = any_values, |
| 19146 | .any_field_aligns = false, |
| 19147 | .packed_backing_int_type = .none, |
| 19148 | })) { |
| 19149 | .existing => |ty| .fromInterned(ty), |
| 19150 | .wip => |wip| ty: { |
| 19151 | errdefer wip.cancel(ip, pt.tid); |
| 19152 | try sema.setTypeName(block, &wip, .anon, "struct", inst); |
| 19153 | |
| 19154 | // Reified structs have field information populated immediately. |
| 19155 | @memcpy(wip.field_names.get(ip), names); |
| 19156 | @memcpy(wip.field_types.get(ip), types); |
| 19157 | if (any_values) { |
| 19158 | @memcpy(wip.field_values.get(ip), values_no_comptime); |
| 19159 | @memset(wip.field_is_comptime_bits.getAll(ip), 0); |
| 19160 | for (values_no_comptime, 0..) |val, field_index| { |
| 19161 | if (val == .none) continue; |
| 19162 | const bit_bag_index = field_index / 32; |
| 19163 | const mask = @as(u32, 1) << @intCast(field_index % 32); |
| 19164 | wip.field_is_comptime_bits.getAll(ip)[bit_bag_index] |= mask; |
| 19165 | } |
| 19166 | } |
| 19167 | |
| 19168 | const new_namespace_index = try pt.createNamespace(.{ |
| 19169 | .parent = block.namespace.toOptional(), |
| 19170 | .owner_type = wip.index, |
| 19171 | .file_scope = block.getFileScopeIndex(zcu), |
| 19172 | .generation = zcu.generation, |
| 19173 | }); |
| 19174 | errdefer pt.destroyNamespace(new_namespace_index); |
| 19175 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); |
| 19176 | break :ty .fromInterned(wip.finish(ip, new_namespace_index)); |
| 19177 | }, |
| 19178 | }; |
| 19179 | try sema.addTypeReferenceEntry(src, struct_ty); |
| 19180 | // No need for `ensureNamespaceUpToDate` because this type's namespace is always empty. |
| 19181 | try sema.ensureLayoutResolved(struct_ty, src, .init); |
| 19182 | |
| 19183 | _ = opt_runtime_index orelse { |
| 19184 | const struct_val = try pt.aggregateValue(struct_ty, values); |
| 19185 | return sema.addConstantMaybeRef(struct_val, is_ref); |
| 19186 | }; |
| 19187 | |
| 19188 | for (values, 0..) |field_val, i| { |
| 19189 | if (field_val == .none) continue; // runtime-known |
| 19190 | const field_src = block.src(.{ .init_elem = .{ |
| 19191 | .init_node_offset = src.offset.node_offset.x, |
| 19192 | .elem_index = @intCast(i), |
| 19193 | } }); |
| 19194 | try sema.validateRuntimeValue(block, field_src, .fromIntern(field_val)); |
| 19195 | } |
| 19196 | |
| 19197 | if (is_ref) { |
| 19198 | const target = zcu.getTarget(); |
| 19199 | const alloc_ty = try pt.ptrType(.{ |
| 19200 | .child = struct_ty.toIntern(), |
| 19201 | .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, |
| 19202 | }); |
| 19203 | const alloc = try block.addTy(.alloc, alloc_ty); |
| 19204 | var extra_index = extra_end; |
| 19205 | for (types, 0..) |field_ty, i_usize| { |
| 19206 | const i: u32 = @intCast(i_usize); |
| 19207 | const item = switch (kind) { |
| 19208 | .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index), |
| 19209 | .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index), |
| 19210 | }; |
| 19211 | extra_index = item.end; |
| 19212 | |
| 19213 | const field_ptr_ty = try pt.ptrType(.{ |
| 19214 | .child = field_ty, |
| 19215 | .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, |
| 19216 | }); |
| 19217 | if (values[i] == .none) { |
| 19218 | const init = sema.resolveInst(item.data.init); |
| 19219 | const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); |
| 19220 | _ = try block.addBinOp(.store, field_ptr, init); |
| 19221 | } |
| 19222 | } |
| 19223 | |
| 19224 | return sema.makePtrConst(block, alloc); |
| 19225 | } |
| 19226 | |
| 19227 | const element_refs = try sema.arena.alloc(Air.Inst.Ref, types.len); |
| 19228 | var extra_index = extra_end; |
| 19229 | for (types, 0..) |_, i| { |
| 19230 | const item = switch (kind) { |
| 19231 | .anon_init => sema.code.extraData(Zir.Inst.StructInitAnon.Item, extra_index), |
| 19232 | .typed_init => sema.code.extraData(Zir.Inst.StructInit.Item, extra_index), |
| 19233 | }; |
| 19234 | extra_index = item.end; |
| 19235 | element_refs[i] = sema.resolveInst(item.data.init); |
| 19236 | } |
| 19237 | |
| 19238 | return block.addAggregateInit(struct_ty, element_refs); |
| 19239 | } |
| 19240 | |
| 19241 | fn zirArrayInit( |
| 19242 | sema: *Sema, |
| 19243 | block: *Block, |
| 19244 | inst: Zir.Inst.Index, |
| 19245 | is_ref: bool, |
| 19246 | ) CompileError!Air.Inst.Ref { |
| 19247 | const pt = sema.pt; |
| 19248 | const zcu = pt.zcu; |
| 19249 | const gpa = sema.gpa; |
| 19250 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 19251 | const src = block.nodeOffset(inst_data.src_node); |
| 19252 | |
| 19253 | const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); |
| 19254 | const args = sema.code.refSlice(extra.end, extra.data.operands_len); |
| 19255 | assert(args.len >= 2); // array_ty + at least one element |
| 19256 | |
| 19257 | const result_ty = try sema.resolveTypeOrPoison(block, src, args[0]) orelse { |
| 19258 | // The type wasn't actually known, so treat this as an anon array init. |
| 19259 | return sema.arrayInitAnon(block, src, args[1..], is_ref); |
| 19260 | }; |
| 19261 | const array_ty = result_ty.optEuBaseType(zcu); |
| 19262 | const is_tuple = array_ty.zigTypeTag(zcu) == .@"struct"; |
| 19263 | const sentinel_val = array_ty.sentinel(zcu); |
| 19264 | |
| 19265 | var root_msg: ?*Zcu.ErrorMsg = null; |
| 19266 | errdefer if (root_msg) |msg| msg.destroy(sema.gpa); |
| 19267 | |
| 19268 | const final_len = try sema.usizeCast(block, src, array_ty.arrayLenIncludingSentinel(zcu)); |
| 19269 | const resolved_args = try gpa.alloc(Air.Inst.Ref, final_len); |
| 19270 | defer gpa.free(resolved_args); |
| 19271 | for (resolved_args, 0..) |*dest, i| { |
| 19272 | const elem_src = block.src(.{ .init_elem = .{ |
| 19273 | .init_node_offset = src.offset.node_offset.x, |
| 19274 | .elem_index = @intCast(i), |
| 19275 | } }); |
| 19276 | // Less inits than needed. |
| 19277 | if (i + 2 > args.len) if (is_tuple) { |
| 19278 | const default_val = array_ty.structFieldDefaultValue(i, zcu) orelse { |
| 19279 | const template = "missing tuple field with index {d}"; |
| 19280 | if (root_msg) |msg| { |
| 19281 | try sema.errNote(src, msg, template, .{i}); |
| 19282 | } else { |
| 19283 | root_msg = try sema.errMsg(src, template, .{i}); |
| 19284 | } |
| 19285 | continue; |
| 19286 | }; |
| 19287 | dest.* = .fromValue(default_val); |
| 19288 | continue; |
| 19289 | } else { |
| 19290 | dest.* = Air.internedToRef(sentinel_val.?.toIntern()); |
| 19291 | break; |
| 19292 | }; |
| 19293 | |
| 19294 | const arg = args[i + 1]; |
| 19295 | const resolved_arg = sema.resolveInst(arg); |
| 19296 | const elem_ty = if (is_tuple) |
| 19297 | array_ty.fieldType(i, zcu) |
| 19298 | else |
| 19299 | array_ty.childType(zcu); |
| 19300 | dest.* = try sema.coerce(block, elem_ty, resolved_arg, elem_src); |
| 19301 | if (is_tuple) { |
| 19302 | if (try array_ty.structFieldValueComptime(pt, i)) |field_val| { |
| 19303 | const init_val = try sema.resolveConstValue(block, elem_src, dest.*, .{ .simple = .stored_to_comptime_field }); |
| 19304 | if (!field_val.eql(init_val, elem_ty, zcu)) { |
| 19305 | return sema.failWithInvalidComptimeFieldStore(block, elem_src, array_ty, i); |
| 19306 | } |
| 19307 | } |
| 19308 | } |
| 19309 | } |
| 19310 | |
| 19311 | if (root_msg) |msg| { |
| 19312 | try sema.addDeclaredHereNote(msg, array_ty); |
| 19313 | root_msg = null; |
| 19314 | return sema.failWithOwnedErrorMsg(block, msg); |
| 19315 | } |
| 19316 | |
| 19317 | const opt_runtime_index: ?u32 = for (resolved_args, 0..) |arg, i| { |
| 19318 | const comptime_known = try sema.isComptimeKnown(arg); |
| 19319 | if (!comptime_known) break @intCast(i); |
| 19320 | } else null; |
| 19321 | |
| 19322 | _ = opt_runtime_index orelse { |
| 19323 | const elem_vals = try sema.arena.alloc(InternPool.Index, resolved_args.len); |
| 19324 | for (elem_vals, resolved_args) |*val, arg| { |
| 19325 | // We checked that all args are comptime above. |
| 19326 | val.* = sema.resolveValue(arg).?.toIntern(); |
| 19327 | } |
| 19328 | const arr_val = try pt.aggregateValue(array_ty, elem_vals); |
| 19329 | const result_ref = try sema.coerce(block, result_ty, Air.internedToRef(arr_val.toIntern()), src); |
| 19330 | const result_val = (sema.resolveValue(result_ref)).?; |
| 19331 | return sema.addConstantMaybeRef(result_val, is_ref); |
| 19332 | }; |
| 19333 | |
| 19334 | if (is_ref) { |
| 19335 | const target = zcu.getTarget(); |
| 19336 | const alloc_ty = try pt.ptrType(.{ |
| 19337 | .child = result_ty.toIntern(), |
| 19338 | .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, |
| 19339 | }); |
| 19340 | const alloc = try block.addTy(.alloc, alloc_ty); |
| 19341 | const base_ptr = try sema.optEuBasePtrInit(block, alloc, src); |
| 19342 | |
| 19343 | if (is_tuple) { |
| 19344 | for (resolved_args, 0..) |arg, i| { |
| 19345 | const elem_ptr_ty = try pt.ptrType(.{ |
| 19346 | .child = array_ty.fieldType(i, zcu).toIntern(), |
| 19347 | .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, |
| 19348 | }); |
| 19349 | |
| 19350 | const index = try pt.intRef(.usize, i); |
| 19351 | const elem_ptr = try block.addPtrElemPtr(base_ptr, index, elem_ptr_ty); |
| 19352 | _ = try block.addBinOp(.store, elem_ptr, arg); |
| 19353 | } |
| 19354 | return sema.makePtrConst(block, alloc); |
| 19355 | } |
| 19356 | |
| 19357 | const elem_ptr_ty = try pt.ptrType(.{ |
| 19358 | .child = array_ty.childType(zcu).toIntern(), |
| 19359 | .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, |
| 19360 | }); |
| 19361 | |
| 19362 | for (resolved_args, 0..) |arg, i| { |
| 19363 | const index = try pt.intRef(.usize, i); |
| 19364 | const elem_ptr = try block.addPtrElemPtr(base_ptr, index, elem_ptr_ty); |
| 19365 | _ = try block.addBinOp(.store, elem_ptr, arg); |
| 19366 | } |
| 19367 | return sema.makePtrConst(block, alloc); |
| 19368 | } |
| 19369 | |
| 19370 | const arr_ref = try block.addAggregateInit(array_ty, resolved_args); |
| 19371 | return sema.coerce(block, result_ty, arr_ref, src); |
| 19372 | } |
| 19373 | |
| 19374 | fn zirArrayInitAnon( |
| 19375 | sema: *Sema, |
| 19376 | block: *Block, |
| 19377 | inst: Zir.Inst.Index, |
| 19378 | ) CompileError!Air.Inst.Ref { |
| 19379 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 19380 | const src = block.nodeOffset(inst_data.src_node); |
| 19381 | const extra = sema.code.extraData(Zir.Inst.MultiOp, inst_data.payload_index); |
| 19382 | const operands = sema.code.refSlice(extra.end, extra.data.operands_len); |
| 19383 | return sema.arrayInitAnon(block, src, operands, false); |
| 19384 | } |
| 19385 | |
| 19386 | fn arrayInitAnon( |
| 19387 | sema: *Sema, |
| 19388 | block: *Block, |
| 19389 | src: LazySrcLoc, |
| 19390 | operands: []const Zir.Inst.Ref, |
| 19391 | is_ref: bool, |
| 19392 | ) CompileError!Air.Inst.Ref { |
| 19393 | const pt = sema.pt; |
| 19394 | const zcu = pt.zcu; |
| 19395 | const comp = zcu.comp; |
| 19396 | const gpa = comp.gpa; |
| 19397 | const io = comp.io; |
| 19398 | const ip = &zcu.intern_pool; |
| 19399 | |
| 19400 | const types = try sema.arena.alloc(InternPool.Index, operands.len); |
| 19401 | const values = try sema.arena.alloc(InternPool.Index, operands.len); |
| 19402 | |
| 19403 | var any_comptime = false; |
| 19404 | const opt_runtime_src = rs: { |
| 19405 | var runtime_src: ?LazySrcLoc = null; |
| 19406 | for (operands, 0..) |operand, i| { |
| 19407 | const operand_src = block.src(.{ .init_elem = .{ |
| 19408 | .init_node_offset = src.offset.node_offset.x, |
| 19409 | .elem_index = @intCast(i), |
| 19410 | } }); |
| 19411 | const elem = sema.resolveInst(operand); |
| 19412 | types[i] = sema.typeOf(elem).toIntern(); |
| 19413 | if (Type.fromInterned(types[i]).zigTypeTag(zcu) == .@"opaque") { |
| 19414 | const msg = msg: { |
| 19415 | const msg = try sema.errMsg(operand_src, "opaque types have unknown size and therefore cannot be directly embedded in structs", .{}); |
| 19416 | errdefer msg.destroy(gpa); |
| 19417 | |
| 19418 | try sema.addDeclaredHereNote(msg, .fromInterned(types[i])); |
| 19419 | break :msg msg; |
| 19420 | }; |
| 19421 | return sema.failWithOwnedErrorMsg(block, msg); |
| 19422 | } |
| 19423 | if (sema.resolveValue(elem)) |val| { |
| 19424 | values[i] = val.toIntern(); |
| 19425 | any_comptime = true; |
| 19426 | } else { |
| 19427 | values[i] = .none; |
| 19428 | runtime_src = operand_src; |
| 19429 | } |
| 19430 | } |
| 19431 | break :rs runtime_src; |
| 19432 | }; |
| 19433 | |
| 19434 | // A field can't be `comptime` if it references a `comptime var` but the aggregate can still be comptime-known. |
| 19435 | // Replace these fields with `.none` only for generating the type. |
| 19436 | const values_no_comptime = if (!any_comptime) values else blk: { |
| 19437 | const new_values = try sema.arena.alloc(InternPool.Index, operands.len); |
| 19438 | for (values, new_values) |val, *new_val| { |
| 19439 | if (val != .none and Value.fromInterned(val).canMutateComptimeVarState(zcu)) { |
| 19440 | new_val.* = .none; |
| 19441 | } else new_val.* = val; |
| 19442 | } |
| 19443 | break :blk new_values; |
| 19444 | }; |
| 19445 | |
| 19446 | const tuple_ty: Type = .fromInterned(try ip.getTupleType(gpa, io, pt.tid, .{ |
| 19447 | .types = types, |
| 19448 | .values = values_no_comptime, |
| 19449 | })); |
| 19450 | |
| 19451 | _ = opt_runtime_src orelse { |
| 19452 | const tuple_val = try pt.aggregateValue(tuple_ty, values); |
| 19453 | return sema.addConstantMaybeRef(tuple_val, is_ref); |
| 19454 | }; |
| 19455 | |
| 19456 | for (operands, 0..) |operand, i| { |
| 19457 | const operand_src = block.src(.{ .init_elem = .{ |
| 19458 | .init_node_offset = src.offset.node_offset.x, |
| 19459 | .elem_index = @intCast(i), |
| 19460 | } }); |
| 19461 | try sema.validateRuntimeValue(block, operand_src, sema.resolveInst(operand)); |
| 19462 | } |
| 19463 | |
| 19464 | if (is_ref) { |
| 19465 | const target = sema.pt.zcu.getTarget(); |
| 19466 | const alloc_ty = try pt.ptrType(.{ |
| 19467 | .child = tuple_ty.toIntern(), |
| 19468 | .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, |
| 19469 | }); |
| 19470 | const alloc = try block.addTy(.alloc, alloc_ty); |
| 19471 | for (operands, 0..) |operand, i_usize| { |
| 19472 | const i: u32 = @intCast(i_usize); |
| 19473 | const field_ptr_ty = try pt.ptrType(.{ |
| 19474 | .child = types[i], |
| 19475 | .flags = .{ .address_space = target_util.defaultAddressSpace(target, .local) }, |
| 19476 | }); |
| 19477 | if (values[i] == .none) { |
| 19478 | const field_ptr = try block.addStructFieldPtr(alloc, i, field_ptr_ty); |
| 19479 | _ = try block.addBinOp(.store, field_ptr, sema.resolveInst(operand)); |
| 19480 | } |
| 19481 | } |
| 19482 | |
| 19483 | return sema.makePtrConst(block, alloc); |
| 19484 | } |
| 19485 | |
| 19486 | const element_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len); |
| 19487 | for (operands, 0..) |operand, i| { |
| 19488 | element_refs[i] = sema.resolveInst(operand); |
| 19489 | } |
| 19490 | |
| 19491 | return block.addAggregateInit(tuple_ty, element_refs); |
| 19492 | } |
| 19493 | |
| 19494 | fn addConstantMaybeRef(sema: *Sema, val: Value, is_ref: bool) !Air.Inst.Ref { |
| 19495 | return if (is_ref) sema.uavRef(val) else .fromValue(val); |
| 19496 | } |
| 19497 | |
| 19498 | fn zirFieldTypeRef(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 19499 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 19500 | const extra = sema.code.extraData(Zir.Inst.FieldTypeRef, inst_data.payload_index).data; |
| 19501 | const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 19502 | const field_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 19503 | const aggregate_ty = try sema.resolveType(block, ty_src, extra.container_type); |
| 19504 | const field_name = try sema.resolveConstStringIntern(block, field_src, extra.field_name, .{ .simple = .field_name }); |
| 19505 | try sema.ensureLayoutResolved(aggregate_ty, ty_src, .field_queried); |
| 19506 | return sema.fieldType(block, aggregate_ty, field_name, field_src, ty_src); |
| 19507 | } |
| 19508 | |
| 19509 | fn zirStructInitFieldType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 19510 | const pt = sema.pt; |
| 19511 | const zcu = pt.zcu; |
| 19512 | const comp = zcu.comp; |
| 19513 | const gpa = comp.gpa; |
| 19514 | const io = comp.io; |
| 19515 | const ip = &zcu.intern_pool; |
| 19516 | |
| 19517 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 19518 | const extra = sema.code.extraData(Zir.Inst.FieldType, inst_data.payload_index).data; |
| 19519 | const ty_src = block.nodeOffset(inst_data.src_node); |
| 19520 | const field_name_src = block.src(.{ .node_offset_field_name_init = inst_data.src_node }); |
| 19521 | const wrapped_aggregate_ty = try sema.resolveTypeOrPoison(block, ty_src, extra.container_type) orelse return .generic_poison_type; |
| 19522 | const aggregate_ty = wrapped_aggregate_ty.optEuBaseType(zcu); |
| 19523 | const zir_field_name = sema.code.nullTerminatedString(extra.name_start); |
| 19524 | const field_name = try ip.getOrPutString(gpa, io, pt.tid, zir_field_name, .no_embedded_nulls); |
| 19525 | try sema.ensureLayoutResolved(aggregate_ty, ty_src, .init); |
| 19526 | return sema.fieldType(block, aggregate_ty, field_name, field_name_src, ty_src); |
| 19527 | } |
| 19528 | |
| 19529 | /// Asserts that the layout of `aggregate_ty` is resolved. |
| 19530 | fn fieldType( |
| 19531 | sema: *Sema, |
| 19532 | block: *Block, |
| 19533 | aggregate_ty: Type, |
| 19534 | field_name: InternPool.NullTerminatedString, |
| 19535 | field_src: LazySrcLoc, |
| 19536 | ty_src: LazySrcLoc, |
| 19537 | ) CompileError!Air.Inst.Ref { |
| 19538 | const pt = sema.pt; |
| 19539 | const zcu = pt.zcu; |
| 19540 | const ip = &zcu.intern_pool; |
| 19541 | aggregate_ty.assertHasLayout(zcu); |
| 19542 | var cur_ty = aggregate_ty; |
| 19543 | while (true) { |
| 19544 | switch (cur_ty.zigTypeTag(zcu)) { |
| 19545 | .@"struct" => switch (ip.indexToKey(cur_ty.toIntern())) { |
| 19546 | .tuple_type => |tuple| { |
| 19547 | const field_index = try sema.tupleFieldIndex(block, cur_ty, field_name, field_src); |
| 19548 | return Air.internedToRef(tuple.types.get(ip)[field_index]); |
| 19549 | }, |
| 19550 | .struct_type => { |
| 19551 | const struct_type = ip.loadStructType(cur_ty.toIntern()); |
| 19552 | const field_index = struct_type.nameIndex(ip, field_name) orelse |
| 19553 | return sema.failWithBadStructFieldAccess(block, cur_ty, struct_type, field_src, field_name); |
| 19554 | const field_ty = struct_type.field_types.get(ip)[field_index]; |
| 19555 | return Air.internedToRef(field_ty); |
| 19556 | }, |
| 19557 | else => unreachable, |
| 19558 | }, |
| 19559 | .@"union" => { |
| 19560 | const union_obj = zcu.typeToUnion(cur_ty).?; |
| 19561 | const enum_obj = ip.loadEnumType(union_obj.enum_tag_type); |
| 19562 | const field_index = enum_obj.nameIndex(ip, field_name) orelse |
| 19563 | return sema.failWithBadUnionFieldAccess(block, cur_ty, union_obj, field_src, field_name); |
| 19564 | const field_ty = union_obj.field_types.get(ip)[field_index]; |
| 19565 | return .fromIntern(field_ty); |
| 19566 | }, |
| 19567 | .optional => { |
| 19568 | // Struct/array init through optional requires the child type to not be a pointer. |
| 19569 | // If the child of .optional is a pointer it'll error on the next loop. |
| 19570 | cur_ty = .fromInterned(ip.indexToKey(cur_ty.toIntern()).opt_type); |
| 19571 | continue; |
| 19572 | }, |
| 19573 | .error_union => { |
| 19574 | cur_ty = cur_ty.errorUnionPayload(zcu); |
| 19575 | continue; |
| 19576 | }, |
| 19577 | else => {}, |
| 19578 | } |
| 19579 | return sema.fail(block, ty_src, "expected struct or union; found '{f}'", .{ |
| 19580 | cur_ty.fmt(pt), |
| 19581 | }); |
| 19582 | } |
| 19583 | } |
| 19584 | |
| 19585 | fn zirErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { |
| 19586 | return sema.getErrorReturnTrace(block); |
| 19587 | } |
| 19588 | |
| 19589 | fn getErrorReturnTrace(sema: *Sema, block: *Block) CompileError!Air.Inst.Ref { |
| 19590 | const pt = sema.pt; |
| 19591 | const zcu = pt.zcu; |
| 19592 | const ip = &zcu.intern_pool; |
| 19593 | const stack_trace_ty = try sema.getStdLangType(block.nodeOffset(.zero), .StackTrace); |
| 19594 | const ptr_stack_trace_ty = try pt.singleMutPtrType(stack_trace_ty); |
| 19595 | const opt_ptr_stack_trace_ty = try pt.optionalType(ptr_stack_trace_ty.toIntern()); |
| 19596 | |
| 19597 | switch (sema.owner.unwrap()) { |
| 19598 | .func => |func| if (ip.funcAnalysisUnordered(func).has_error_trace and block.ownerModule().error_tracing) { |
| 19599 | return block.addTy(.err_return_trace, opt_ptr_stack_trace_ty); |
| 19600 | }, |
| 19601 | |
| 19602 | .@"comptime", |
| 19603 | .nav_ty, |
| 19604 | .nav_val, |
| 19605 | .type_layout, |
| 19606 | .struct_defaults, |
| 19607 | .memoized_state, |
| 19608 | => {}, |
| 19609 | } |
| 19610 | return Air.internedToRef(try pt.intern(.{ .opt = .{ |
| 19611 | .ty = opt_ptr_stack_trace_ty.toIntern(), |
| 19612 | .val = .none, |
| 19613 | } })); |
| 19614 | } |
| 19615 | |
| 19616 | fn zirFrame( |
| 19617 | sema: *Sema, |
| 19618 | block: *Block, |
| 19619 | extended: Zir.Inst.Extended.InstData, |
| 19620 | ) CompileError!Air.Inst.Ref { |
| 19621 | const src_node: std.zig.Ast.Node.Offset = @fromBackingInt(@intCast(@as(i32, @bitCast(extended.operand)))); |
| 19622 | const src = block.nodeOffset(src_node); |
| 19623 | return sema.failWithUseOfAsync(block, src); |
| 19624 | } |
| 19625 | |
| 19626 | fn zirAlignOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 19627 | const pt = sema.pt; |
| 19628 | const zcu = pt.zcu; |
| 19629 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 19630 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 19631 | const ty = try sema.resolveType(block, operand_src, inst_data.operand); |
| 19632 | try sema.ensureLayoutResolved(ty, operand_src, .align_of); |
| 19633 | if (ty.isNoReturn(zcu)) { |
| 19634 | return sema.fail(block, operand_src, "no align available for uninstantiable type '{f}'", .{ty.fmt(sema.pt)}); |
| 19635 | } |
| 19636 | return .fromValue(try pt.intValue(.comptime_int, ty.abiAlignment(zcu).toByteUnits().?)); |
| 19637 | } |
| 19638 | |
| 19639 | fn zirIntFromBool(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 19640 | const pt = sema.pt; |
| 19641 | const zcu = pt.zcu; |
| 19642 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 19643 | const src = block.nodeOffset(inst_data.src_node); |
| 19644 | const operand = sema.resolveInst(inst_data.operand); |
| 19645 | const operand_ty = sema.typeOf(operand); |
| 19646 | const is_vector = operand_ty.zigTypeTag(zcu) == .vector; |
| 19647 | const operand_scalar_ty = operand_ty.scalarType(zcu); |
| 19648 | if (operand_scalar_ty.toIntern() != .bool_type) { |
| 19649 | return sema.fail(block, src, "expected 'bool', found '{t}'", .{operand_scalar_ty.zigTypeTag(zcu)}); |
| 19650 | } |
| 19651 | const len = if (is_vector) operand_ty.vectorLen(zcu) else undefined; |
| 19652 | const dest_ty: Type = if (is_vector) try pt.vectorType(.{ .child = .u1_type, .len = len }) else .u1; |
| 19653 | if (sema.resolveValue(operand)) |val| { |
| 19654 | if (!is_vector) { |
| 19655 | return if (val.isUndef(zcu)) .undef_u1 else if (val.toBool()) .one_u1 else .zero_u1; |
| 19656 | } |
| 19657 | if (val.isUndef(zcu)) return pt.undefRef(dest_ty); |
| 19658 | const new_elems = try sema.arena.alloc(InternPool.Index, len); |
| 19659 | for (new_elems, 0..) |*new_elem, i| { |
| 19660 | const old_elem = try val.elemValue(pt, i); |
| 19661 | new_elem.* = if (old_elem.isUndef(zcu)) |
| 19662 | .undef_u1 |
| 19663 | else if (old_elem.toBool()) |
| 19664 | .one_u1 |
| 19665 | else |
| 19666 | .zero_u1; |
| 19667 | } |
| 19668 | return Air.internedToRef((try pt.aggregateValue(dest_ty, new_elems)).toIntern()); |
| 19669 | } |
| 19670 | return block.addTyOp(.bit_cast, dest_ty, operand); |
| 19671 | } |
| 19672 | |
| 19673 | fn zirErrorName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 19674 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 19675 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 19676 | const uncoerced_operand = sema.resolveInst(inst_data.operand); |
| 19677 | const operand = try sema.coerce(block, .anyerror, uncoerced_operand, operand_src); |
| 19678 | |
| 19679 | if (try sema.resolveDefinedValue(block, operand_src, operand)) |val| { |
| 19680 | const err_name = sema.pt.zcu.intern_pool.indexToKey(val.toIntern()).err.name; |
| 19681 | return sema.addNullTerminatedStrLit(err_name); |
| 19682 | } |
| 19683 | |
| 19684 | // Similar to zirTagName, we have special AIR instruction for the error name in case an optimimzation pass |
| 19685 | // might be able to resolve the result at compile time. |
| 19686 | return block.addUnOp(.error_name, operand); |
| 19687 | } |
| 19688 | |
| 19689 | fn zirAbs( |
| 19690 | sema: *Sema, |
| 19691 | block: *Block, |
| 19692 | inst: Zir.Inst.Index, |
| 19693 | ) CompileError!Air.Inst.Ref { |
| 19694 | const pt = sema.pt; |
| 19695 | const zcu = pt.zcu; |
| 19696 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 19697 | const operand = sema.resolveInst(inst_data.operand); |
| 19698 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 19699 | const operand_ty = sema.typeOf(operand); |
| 19700 | const scalar_ty = operand_ty.scalarType(zcu); |
| 19701 | |
| 19702 | const result_ty = switch (scalar_ty.zigTypeTag(zcu)) { |
| 19703 | .comptime_float, .float, .comptime_int => operand_ty, |
| 19704 | .int => if (scalar_ty.isSignedInt(zcu)) try operand_ty.toUnsigned(pt) else return operand, |
| 19705 | else => return sema.fail( |
| 19706 | block, |
| 19707 | operand_src, |
| 19708 | "expected integer, float, or vector of either integers or floats, found '{f}'", |
| 19709 | .{operand_ty.fmt(pt)}, |
| 19710 | ), |
| 19711 | }; |
| 19712 | |
| 19713 | return (try sema.maybeConstantUnaryMath(operand, result_ty, Value.abs)) orelse { |
| 19714 | try sema.requireRuntimeBlock(block, operand_src, null); |
| 19715 | return block.addTyOp(.abs, result_ty, operand); |
| 19716 | }; |
| 19717 | } |
| 19718 | |
| 19719 | fn maybeConstantUnaryMath( |
| 19720 | sema: *Sema, |
| 19721 | operand: Air.Inst.Ref, |
| 19722 | result_ty: Type, |
| 19723 | comptime eval: fn (Value, Type, Allocator, Zcu.PerThread) Allocator.Error!Value, |
| 19724 | ) CompileError!?Air.Inst.Ref { |
| 19725 | const pt = sema.pt; |
| 19726 | const zcu = pt.zcu; |
| 19727 | switch (result_ty.zigTypeTag(zcu)) { |
| 19728 | .vector => if (sema.resolveValue(operand)) |val| { |
| 19729 | const scalar_ty = result_ty.scalarType(zcu); |
| 19730 | const vec_len = result_ty.vectorLen(zcu); |
| 19731 | if (val.isUndef(zcu)) |
| 19732 | return try pt.undefRef(result_ty); |
| 19733 | |
| 19734 | const elems = try sema.arena.alloc(InternPool.Index, vec_len); |
| 19735 | for (elems, 0..) |*elem, i| { |
| 19736 | const elem_val = try val.elemValue(pt, i); |
| 19737 | elem.* = (try eval(elem_val, scalar_ty, sema.arena, pt)).toIntern(); |
| 19738 | } |
| 19739 | return Air.internedToRef((try pt.aggregateValue(result_ty, elems)).toIntern()); |
| 19740 | }, |
| 19741 | else => if (sema.resolveValue(operand)) |operand_val| { |
| 19742 | if (operand_val.isUndef(zcu)) |
| 19743 | return try pt.undefRef(result_ty); |
| 19744 | const result_val = try eval(operand_val, result_ty, sema.arena, pt); |
| 19745 | return Air.internedToRef(result_val.toIntern()); |
| 19746 | }, |
| 19747 | } |
| 19748 | return null; |
| 19749 | } |
| 19750 | |
| 19751 | fn unaryMath( |
| 19752 | sema: *Sema, |
| 19753 | block: *Block, |
| 19754 | operand_src: LazySrcLoc, |
| 19755 | operand: Air.Inst.Ref, |
| 19756 | air_tag: Air.Inst.Tag, |
| 19757 | comptime eval: fn (Value, Type, Allocator, Zcu.PerThread) Allocator.Error!Value, |
| 19758 | ) CompileError!Air.Inst.Ref { |
| 19759 | const pt = sema.pt; |
| 19760 | const zcu = pt.zcu; |
| 19761 | const operand_ty = sema.typeOf(operand); |
| 19762 | const scalar_ty = operand_ty.scalarType(zcu); |
| 19763 | |
| 19764 | switch (scalar_ty.zigTypeTag(zcu)) { |
| 19765 | .comptime_float, .float => {}, |
| 19766 | else => return sema.fail( |
| 19767 | block, |
| 19768 | operand_src, |
| 19769 | "expected vector of floats or float type, found '{f}'", |
| 19770 | .{operand_ty.fmt(pt)}, |
| 19771 | ), |
| 19772 | } |
| 19773 | |
| 19774 | return (try sema.maybeConstantUnaryMath(operand, operand_ty, eval)) orelse { |
| 19775 | try sema.requireRuntimeBlock(block, operand_src, null); |
| 19776 | return block.addUnOp(air_tag, operand); |
| 19777 | }; |
| 19778 | } |
| 19779 | |
| 19780 | fn zirUnaryMath( |
| 19781 | sema: *Sema, |
| 19782 | block: *Block, |
| 19783 | inst: Zir.Inst.Index, |
| 19784 | air_tag: Air.Inst.Tag, |
| 19785 | comptime eval: fn (Value, Type, Allocator, Zcu.PerThread) Allocator.Error!Value, |
| 19786 | ) CompileError!Air.Inst.Ref { |
| 19787 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 19788 | const operand = sema.resolveInst(inst_data.operand); |
| 19789 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 19790 | |
| 19791 | return sema.unaryMath(block, operand_src, operand, air_tag, eval); |
| 19792 | } |
| 19793 | |
| 19794 | fn zirTagName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 19795 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 19796 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 19797 | const src = block.nodeOffset(inst_data.src_node); |
| 19798 | const operand = sema.resolveInst(inst_data.operand); |
| 19799 | const operand_ty = sema.typeOf(operand); |
| 19800 | const pt = sema.pt; |
| 19801 | const zcu = pt.zcu; |
| 19802 | const ip = &zcu.intern_pool; |
| 19803 | const enum_ty = switch (operand_ty.zigTypeTag(zcu)) { |
| 19804 | .enum_literal => { |
| 19805 | const val = (try sema.resolveDefinedValue(block, operand_src, operand)).?; |
| 19806 | const tag_name = ip.indexToKey(val.toIntern()).enum_literal; |
| 19807 | return sema.addNullTerminatedStrLit(tag_name); |
| 19808 | }, |
| 19809 | .@"enum" => operand_ty, |
| 19810 | .@"union" => operand_ty.unionTagType(zcu) orelse |
| 19811 | return sema.fail(block, src, "union '{f}' is untagged", .{operand_ty.fmt(pt)}), |
| 19812 | else => return sema.fail(block, operand_src, "expected enum or union; found '{f}'", .{ |
| 19813 | operand_ty.fmt(pt), |
| 19814 | }), |
| 19815 | }; |
| 19816 | const casted_operand = try sema.coerce(block, enum_ty, operand, operand_src); |
| 19817 | if (try sema.resolveDefinedValue(block, operand_src, casted_operand)) |val| { |
| 19818 | const field_index = enum_ty.enumTagFieldIndex(val, zcu) orelse { |
| 19819 | const msg = msg: { |
| 19820 | const msg = try sema.errMsg(src, "no field with value '{f}' in enum '{f}'", .{ |
| 19821 | val.fmtValueSema(pt, sema), enum_ty.fmt(pt), |
| 19822 | }); |
| 19823 | errdefer msg.destroy(sema.gpa); |
| 19824 | try sema.errNote(enum_ty.srcLoc(zcu), msg, "declared here", .{}); |
| 19825 | break :msg msg; |
| 19826 | }; |
| 19827 | return sema.failWithOwnedErrorMsg(block, msg); |
| 19828 | }; |
| 19829 | // TODO: write something like getCoercedInts to avoid needing to dupe |
| 19830 | const field_name = enum_ty.enumFieldName(field_index, zcu); |
| 19831 | return sema.addNullTerminatedStrLit(field_name); |
| 19832 | } |
| 19833 | try sema.requireRuntimeBlock(block, src, operand_src); |
| 19834 | if (block.wantSafety() and zcu.backendSupportsFeature(.is_named_enum_value)) { |
| 19835 | const ok = try block.addUnOp(.is_named_enum_value, casted_operand); |
| 19836 | try sema.addSafetyCheck(block, src, ok, .invalid_enum_value); |
| 19837 | } |
| 19838 | // In case the value is runtime-known, we have an AIR instruction for this instead |
| 19839 | // of trying to lower it in Sema because an optimization pass may result in the operand |
| 19840 | // being comptime-known, which would let us elide the `tag_name` AIR instruction. |
| 19841 | return block.addUnOp(.tag_name, casted_operand); |
| 19842 | } |
| 19843 | |
| 19844 | fn zirReifyInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 19845 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 19846 | const signedness_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 19847 | const bits_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 19848 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 19849 | const signedness = try sema.resolveStdLangEnum(block, signedness_src, extra.lhs, .Signedness, .{ .simple = .int_signedness }); |
| 19850 | const bits: u16 = @intCast(try sema.resolveInt(block, bits_src, extra.rhs, .u16, .{ .simple = .int_bit_width })); |
| 19851 | if (bits == 0 and signedness == .signed) { |
| 19852 | return sema.fail(block, bits_src, "signed integer cannot have bit width 0", .{}); |
| 19853 | } |
| 19854 | return .fromType(try sema.pt.intType(signedness, bits)); |
| 19855 | } |
| 19856 | |
| 19857 | fn zirReifySliceArgTy( |
| 19858 | sema: *Sema, |
| 19859 | block: *Block, |
| 19860 | extended: Zir.Inst.Extended.InstData, |
| 19861 | ) CompileError!Air.Inst.Ref { |
| 19862 | const pt = sema.pt; |
| 19863 | const zcu = pt.zcu; |
| 19864 | |
| 19865 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 19866 | const info: Zir.Inst.ReifySliceArgInfo = @fromBackingInt(@intCast(extended.small)); |
| 19867 | |
| 19868 | const src = block.nodeOffset(extra.node); |
| 19869 | |
| 19870 | const comptime_reason: std.zig.SimpleComptimeReason, const in_scalar_ty: Type, const out_scalar_ty: Type = switch (info) { |
| 19871 | // zig fmt: off |
| 19872 | .type_to_fn_param_attrs => .{ .fn_param_attrs, .type, try sema.getStdLangType(src, .@"Type.Fn.ParamAttributes") }, |
| 19873 | .string_to_struct_field_type => .{ .struct_field_types, .slice_const_u8, .type }, |
| 19874 | .string_to_union_field_type => .{ .union_field_types, .slice_const_u8, .type }, |
| 19875 | .string_to_struct_field_attrs => .{ .struct_field_attrs, .slice_const_u8, try sema.getStdLangType(src, .@"Type.Struct.FieldAttributes") }, |
| 19876 | .string_to_union_field_attrs => .{ .union_field_attrs, .slice_const_u8, try sema.getStdLangType(src, .@"Type.Union.FieldAttributes") }, |
| 19877 | // zig fmt: on |
| 19878 | }; |
| 19879 | |
| 19880 | const operand_ty = try pt.ptrType(.{ |
| 19881 | .child = in_scalar_ty.toIntern(), |
| 19882 | .flags = .{ .size = .slice, .is_const = true }, |
| 19883 | }); |
| 19884 | |
| 19885 | const operand_uncoerced = sema.resolveInst(extra.operand); |
| 19886 | const operand_coerced = try sema.coerce(block, operand_ty, operand_uncoerced, src); |
| 19887 | const operand_val = try sema.resolveConstDefinedValue(block, src, operand_coerced, .{ .simple = comptime_reason }); |
| 19888 | const len_val: Value = .fromInterned(zcu.intern_pool.indexToKey(operand_val.toIntern()).slice.len); |
| 19889 | if (len_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, null); |
| 19890 | const len = len_val.toUnsignedInt(zcu); |
| 19891 | |
| 19892 | return .fromType(try pt.singleConstPtrType(try pt.arrayType(.{ |
| 19893 | .len = len, |
| 19894 | .child = out_scalar_ty.toIntern(), |
| 19895 | }))); |
| 19896 | } |
| 19897 | |
| 19898 | fn zirReifyEnumValueSliceTy( |
| 19899 | sema: *Sema, |
| 19900 | block: *Block, |
| 19901 | extended: Zir.Inst.Extended.InstData, |
| 19902 | ) CompileError!Air.Inst.Ref { |
| 19903 | const pt = sema.pt; |
| 19904 | const zcu = pt.zcu; |
| 19905 | |
| 19906 | const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; |
| 19907 | |
| 19908 | const int_tag_ty_src = block.builtinCallArgSrc(extra.node, 0); |
| 19909 | const field_names_src = block.builtinCallArgSrc(extra.node, 2); |
| 19910 | |
| 19911 | const int_tag_ty = try sema.resolveType(block, int_tag_ty_src, extra.lhs); |
| 19912 | |
| 19913 | const operand_uncoerced = sema.resolveInst(extra.rhs); |
| 19914 | const operand_coerced = try sema.coerce(block, .slice_const_slice_const_u8, operand_uncoerced, field_names_src); |
| 19915 | const operand_val = try sema.resolveConstDefinedValue(block, field_names_src, operand_coerced, .{ .simple = .enum_field_names }); |
| 19916 | const len_val: Value = .fromInterned(zcu.intern_pool.indexToKey(operand_val.toIntern()).slice.len); |
| 19917 | if (len_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, field_names_src, null); |
| 19918 | const len = len_val.toUnsignedInt(zcu); |
| 19919 | |
| 19920 | return .fromType(try pt.singleConstPtrType(try pt.arrayType(.{ |
| 19921 | .len = len, |
| 19922 | .child = int_tag_ty.toIntern(), |
| 19923 | }))); |
| 19924 | } |
| 19925 | |
| 19926 | fn zirReifyPointerSentinelTy( |
| 19927 | sema: *Sema, |
| 19928 | block: *Block, |
| 19929 | extended: Zir.Inst.Extended.InstData, |
| 19930 | ) CompileError!Air.Inst.Ref { |
| 19931 | const pt = sema.pt; |
| 19932 | const zcu = pt.zcu; |
| 19933 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 19934 | const src = block.nodeOffset(extra.node); |
| 19935 | const elem_ty = try sema.resolveType(block, src, extra.operand); |
| 19936 | return .fromType(switch (elem_ty.zigTypeTag(zcu)) { |
| 19937 | else => try pt.optionalType(elem_ty.toIntern()), |
| 19938 | // These types cannot be the child of an optional. To allow reifying pointers to them still, |
| 19939 | // we treat the "sentinel" argument to `@Pointer` as `?noreturn` instead of `?T`. |
| 19940 | .@"opaque", .null => .optional_noreturn, |
| 19941 | }); |
| 19942 | } |
| 19943 | |
| 19944 | fn zirReifyTuple( |
| 19945 | sema: *Sema, |
| 19946 | block: *Block, |
| 19947 | extended: Zir.Inst.Extended.InstData, |
| 19948 | ) CompileError!Air.Inst.Ref { |
| 19949 | const pt = sema.pt; |
| 19950 | const zcu = pt.zcu; |
| 19951 | const comp = zcu.comp; |
| 19952 | const gpa = comp.gpa; |
| 19953 | const io = comp.io; |
| 19954 | |
| 19955 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 19956 | const operand_src = block.builtinCallArgSrc(extra.node, 0); |
| 19957 | |
| 19958 | const types_uncoerced = sema.resolveInst(extra.operand); |
| 19959 | const types_coerced = try sema.coerce(block, .slice_const_type, types_uncoerced, operand_src); |
| 19960 | const types_slice_val = try sema.resolveConstDefinedValue(block, operand_src, types_coerced, .{ .simple = .tuple_field_types }); |
| 19961 | const types_array_val = try sema.derefSliceAsArray(block, operand_src, types_slice_val, .{ .simple = .tuple_field_types }); |
| 19962 | const fields_len: u32 = @intCast(types_array_val.typeOf(zcu).arrayLen(zcu)); |
| 19963 | |
| 19964 | const field_types = try sema.arena.alloc(InternPool.Index, fields_len); |
| 19965 | for (field_types, 0..) |*field_ty, field_idx| { |
| 19966 | const field_ty_val = try types_array_val.elemValue(pt, field_idx); |
| 19967 | if (field_ty_val.isUndef(zcu)) { |
| 19968 | return sema.failWithUseOfUndef(block, operand_src, null); |
| 19969 | } |
| 19970 | try sema.validateTupleFieldType(block, field_ty_val.toType(), operand_src); |
| 19971 | field_ty.* = field_ty_val.toIntern(); |
| 19972 | } |
| 19973 | |
| 19974 | const field_values = try sema.arena.alloc(InternPool.Index, fields_len); |
| 19975 | @memset(field_values, .none); |
| 19976 | |
| 19977 | return .fromIntern(try zcu.intern_pool.getTupleType(gpa, io, pt.tid, .{ |
| 19978 | .types = field_types, |
| 19979 | .values = field_values, |
| 19980 | })); |
| 19981 | } |
| 19982 | |
| 19983 | fn zirReifyPointer( |
| 19984 | sema: *Sema, |
| 19985 | block: *Block, |
| 19986 | extended: Zir.Inst.Extended.InstData, |
| 19987 | ) CompileError!Air.Inst.Ref { |
| 19988 | const pt = sema.pt; |
| 19989 | const zcu = pt.zcu; |
| 19990 | const comp = zcu.comp; |
| 19991 | const gpa = comp.gpa; |
| 19992 | const io = comp.io; |
| 19993 | const ip = &zcu.intern_pool; |
| 19994 | |
| 19995 | const extra = sema.code.extraData(Zir.Inst.ReifyPointer, extended.operand).data; |
| 19996 | const src = block.nodeOffset(extra.node); |
| 19997 | const size_src = block.builtinCallArgSrc(extra.node, 0); |
| 19998 | const attrs_src = block.builtinCallArgSrc(extra.node, 1); |
| 19999 | const elem_ty_src = block.builtinCallArgSrc(extra.node, 2); |
| 20000 | const sentinel_src = block.builtinCallArgSrc(extra.node, 3); |
| 20001 | |
| 20002 | const size_ty = try sema.getStdLangType(size_src, .@"Type.Pointer.Size"); |
| 20003 | const attrs_ty = try sema.getStdLangType(attrs_src, .@"Type.Pointer.Attributes"); |
| 20004 | |
| 20005 | const size_uncoerced = sema.resolveInst(extra.size); |
| 20006 | const size_coerced = try sema.coerce(block, size_ty, size_uncoerced, size_src); |
| 20007 | const size_val = try sema.resolveConstDefinedValue(block, size_src, size_coerced, .{ .simple = .pointer_size }); |
| 20008 | const size = try sema.interpretStdLangType(block, size_src, size_val, std.lang.Type.Pointer.Size); |
| 20009 | |
| 20010 | const attrs_uncoerced = sema.resolveInst(extra.attrs); |
| 20011 | const attrs_coerced = try sema.coerce(block, attrs_ty, attrs_uncoerced, attrs_src); |
| 20012 | const attrs_val = try sema.resolveConstDefinedValue(block, attrs_src, attrs_coerced, .{ .simple = .pointer_attrs }); |
| 20013 | const attrs = try sema.interpretStdLangType(block, attrs_src, attrs_val, std.lang.Type.Pointer.Attributes); |
| 20014 | |
| 20015 | const @"align": Alignment = if (attrs.@"align") |bytes| a: { |
| 20016 | break :a try sema.validateAlign(block, attrs_src, bytes); |
| 20017 | } else .none; |
| 20018 | |
| 20019 | const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_ty); |
| 20020 | |
| 20021 | switch (elem_ty.zigTypeTag(zcu)) { |
| 20022 | .noreturn => return sema.fail(block, elem_ty_src, "pointer to noreturn not allowed", .{}), |
| 20023 | // This needs to be disallowed, because the sentinel parameter would otherwise have type |
| 20024 | // `?@TypeOf(null)`, which is not a valid type because you cannot differentiate between |
| 20025 | // constructing the "inner" null value and the "outer" null value. |
| 20026 | .null => return sema.fail(block, elem_ty_src, "cannot reify pointer to '@TypeOf(null)'", .{}), |
| 20027 | .@"fn" => switch (size) { |
| 20028 | .one => {}, |
| 20029 | .many, .c, .slice => return sema.fail(block, src, "function pointers must be single pointers", .{}), |
| 20030 | }, |
| 20031 | .@"opaque" => switch (size) { |
| 20032 | .one => {}, |
| 20033 | .many, .c, .slice => return sema.fail(block, src, "indexable pointer to opaque type '{f}' not allowed", .{elem_ty.fmt(pt)}), |
| 20034 | }, |
| 20035 | else => {}, |
| 20036 | } |
| 20037 | |
| 20038 | const sentinel_ty = try pt.optionalType(elem_ty.toIntern()); |
| 20039 | const sentinel_uncoerced = sema.resolveInst(extra.sentinel); |
| 20040 | const sentinel_coerced = try sema.coerce(block, sentinel_ty, sentinel_uncoerced, sentinel_src); |
| 20041 | const sentinel_val = try sema.resolveConstDefinedValue(block, sentinel_src, sentinel_coerced, .{ .simple = .pointer_sentinel }); |
| 20042 | const opt_sentinel = sentinel_val.optionalValue(zcu); |
| 20043 | if (opt_sentinel) |sentinel| { |
| 20044 | switch (size) { |
| 20045 | .many, .slice => {}, |
| 20046 | .one, .c => return sema.fail(block, sentinel_src, "sentinels are only allowed on slices and unknown-length pointers", .{}), |
| 20047 | } |
| 20048 | try checkSentinelType(sema, block, sentinel_src, elem_ty); |
| 20049 | if (sentinel.canMutateComptimeVarState(zcu)) { |
| 20050 | const sentinel_name = try ip.getOrPutString(gpa, io, pt.tid, "sentinel", .no_embedded_nulls); |
| 20051 | return sema.failWithContainsReferenceToComptimeVar(block, sentinel_src, sentinel_name, "sentinel", sentinel); |
| 20052 | } |
| 20053 | } |
| 20054 | |
| 20055 | return .fromType(try pt.ptrType(.{ |
| 20056 | .child = elem_ty.toIntern(), |
| 20057 | .sentinel = if (opt_sentinel) |s| s.toIntern() else .none, |
| 20058 | .flags = .{ |
| 20059 | .size = size, |
| 20060 | .is_const = attrs.@"const", |
| 20061 | .is_volatile = attrs.@"volatile", |
| 20062 | .is_allowzero = attrs.@"allowzero", |
| 20063 | .address_space = attrs.@"addrspace" orelse as: { |
| 20064 | if (elem_ty.zigTypeTag(zcu) == .@"fn" and zcu.getTarget().cpu.arch == .avr) break :as .flash; |
| 20065 | break :as .generic; |
| 20066 | }, |
| 20067 | .alignment = @"align", |
| 20068 | }, |
| 20069 | })); |
| 20070 | } |
| 20071 | |
| 20072 | fn zirReifyFn( |
| 20073 | sema: *Sema, |
| 20074 | block: *Block, |
| 20075 | extended: Zir.Inst.Extended.InstData, |
| 20076 | ) CompileError!Air.Inst.Ref { |
| 20077 | const pt = sema.pt; |
| 20078 | const zcu = pt.zcu; |
| 20079 | const comp = zcu.comp; |
| 20080 | const gpa = comp.gpa; |
| 20081 | const io = comp.io; |
| 20082 | const ip = &zcu.intern_pool; |
| 20083 | |
| 20084 | const extra = sema.code.extraData(Zir.Inst.ReifyFn, extended.operand).data; |
| 20085 | const param_types_src = block.builtinCallArgSrc(extra.node, 0); |
| 20086 | const param_attrs_src = block.builtinCallArgSrc(extra.node, 1); |
| 20087 | const ret_ty_src = block.builtinCallArgSrc(extra.node, 2); |
| 20088 | const fn_attrs_src = block.builtinCallArgSrc(extra.node, 3); |
| 20089 | |
| 20090 | const single_param_attrs_ty = try sema.getStdLangType(param_attrs_src, .@"Type.Fn.ParamAttributes"); |
| 20091 | const fn_attrs_ty = try sema.getStdLangType(fn_attrs_src, .@"Type.Fn.Attributes"); |
| 20092 | |
| 20093 | const param_types_uncoerced = sema.resolveInst(extra.param_types); |
| 20094 | const param_types_coerced = try sema.coerce(block, .slice_const_type, param_types_uncoerced, param_types_src); |
| 20095 | const param_types_slice = try sema.resolveConstDefinedValue(block, param_types_src, param_types_coerced, .{ .simple = .fn_param_types }); |
| 20096 | const param_types_arr = try sema.derefSliceAsArray(block, param_types_src, param_types_slice, .{ .simple = .fn_param_types }); |
| 20097 | |
| 20098 | const params_len = param_types_arr.typeOf(zcu).arrayLen(zcu); |
| 20099 | |
| 20100 | const param_attrs_ty = try pt.singleConstPtrType(try pt.arrayType(.{ |
| 20101 | .len = params_len, |
| 20102 | .child = single_param_attrs_ty.toIntern(), |
| 20103 | })); |
| 20104 | const param_attrs_uncoerced = sema.resolveInst(extra.param_attrs); |
| 20105 | const param_attrs_coerced = try sema.coerce(block, param_attrs_ty, param_attrs_uncoerced, param_attrs_src); |
| 20106 | const param_attrs_slice = try sema.resolveConstDefinedValue(block, param_attrs_src, param_attrs_coerced, .{ .simple = .fn_param_attrs }); |
| 20107 | const param_attrs_arr = try sema.derefSliceAsArray(block, param_attrs_src, param_attrs_slice, .{ .simple = .fn_param_attrs }); |
| 20108 | |
| 20109 | const ret_ty = try sema.resolveType(block, ret_ty_src, extra.ret_ty); |
| 20110 | |
| 20111 | const fn_attrs_uncoerced = sema.resolveInst(extra.fn_attrs); |
| 20112 | const fn_attrs_coerced = try sema.coerce(block, fn_attrs_ty, fn_attrs_uncoerced, fn_attrs_src); |
| 20113 | const fn_attrs_val = try sema.resolveConstDefinedValue(block, fn_attrs_src, fn_attrs_coerced, .{ .simple = .fn_attrs }); |
| 20114 | const fn_attrs = try sema.interpretStdLangType(block, fn_attrs_src, fn_attrs_val, std.lang.Type.Fn.Attributes); |
| 20115 | |
| 20116 | var noalias_bits: u32 = 0; |
| 20117 | const param_types_ip = try sema.arena.alloc(InternPool.Index, @intCast(params_len)); |
| 20118 | for (param_types_ip, 0..@intCast(params_len)) |*param_ty_ip, param_idx| { |
| 20119 | const param_ty: Type = (try param_types_arr.elemValue(pt, param_idx)).toType(); |
| 20120 | const param_attrs = try sema.interpretStdLangType( |
| 20121 | block, |
| 20122 | param_attrs_src, |
| 20123 | try param_attrs_arr.elemValue(pt, param_idx), |
| 20124 | std.lang.Type.Fn.ParamAttributes, |
| 20125 | ); |
| 20126 | try sema.checkParamType( |
| 20127 | block, |
| 20128 | @intCast(param_idx), |
| 20129 | param_ty, |
| 20130 | false, |
| 20131 | param_attrs.@"noalias", |
| 20132 | param_types_src, |
| 20133 | fn_attrs.@"callconv", |
| 20134 | ); |
| 20135 | if (param_attrs.@"noalias") { |
| 20136 | if (param_idx > 31) { |
| 20137 | return sema.fail(block, param_attrs_src, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}); |
| 20138 | } |
| 20139 | noalias_bits |= @as(u32, 1) << @intCast(param_idx); |
| 20140 | } |
| 20141 | param_ty_ip.* = param_ty.toIntern(); |
| 20142 | } |
| 20143 | |
| 20144 | if (fn_attrs.varargs) { |
| 20145 | try sema.checkCallConvSupportsVarArgs(block, fn_attrs_src, fn_attrs.@"callconv"); |
| 20146 | } |
| 20147 | |
| 20148 | try sema.checkReturnTypeAndCallConv( |
| 20149 | block, |
| 20150 | ret_ty, |
| 20151 | ret_ty_src, |
| 20152 | fn_attrs.@"callconv", |
| 20153 | fn_attrs_src, |
| 20154 | if (fn_attrs.varargs) fn_attrs_src else null, |
| 20155 | false, |
| 20156 | false, |
| 20157 | ); |
| 20158 | |
| 20159 | return .fromIntern(try ip.getFuncType(gpa, io, pt.tid, .{ |
| 20160 | .param_types = param_types_ip, |
| 20161 | .noalias_bits = noalias_bits, |
| 20162 | .comptime_bits = 0, |
| 20163 | .return_type = ret_ty.toIntern(), |
| 20164 | .cc = fn_attrs.@"callconv", |
| 20165 | .is_var_args = fn_attrs.varargs, |
| 20166 | .is_noinline = false, |
| 20167 | })); |
| 20168 | } |
| 20169 | |
| 20170 | fn zirReifyStruct( |
| 20171 | sema: *Sema, |
| 20172 | block: *Block, |
| 20173 | extended: Zir.Inst.Extended.InstData, |
| 20174 | inst: Zir.Inst.Index, |
| 20175 | ) CompileError!Air.Inst.Ref { |
| 20176 | const pt = sema.pt; |
| 20177 | const zcu = pt.zcu; |
| 20178 | const comp = zcu.comp; |
| 20179 | const gpa = comp.gpa; |
| 20180 | const io = comp.io; |
| 20181 | const ip = &zcu.intern_pool; |
| 20182 | |
| 20183 | const name_strategy: Zir.Inst.NameStrategy = @fromBackingInt(@intCast(extended.small)); |
| 20184 | const extra = sema.code.extraData(Zir.Inst.ReifyStruct, extended.operand).data; |
| 20185 | const tracked_inst = try block.trackZir(inst); |
| 20186 | |
| 20187 | const src: LazySrcLoc = .{ |
| 20188 | .base_node_inst = tracked_inst, |
| 20189 | .offset = .nodeOffset(.zero), |
| 20190 | }; |
| 20191 | |
| 20192 | const layout_src: LazySrcLoc = .{ |
| 20193 | .base_node_inst = tracked_inst, |
| 20194 | .offset = .{ .node_offset_builtin_call_arg = .{ |
| 20195 | .builtin_call_node = .zero, |
| 20196 | .arg_index = 0, |
| 20197 | } }, |
| 20198 | }; |
| 20199 | const backing_ty_src: LazySrcLoc = .{ |
| 20200 | .base_node_inst = tracked_inst, |
| 20201 | .offset = .{ .node_offset_builtin_call_arg = .{ |
| 20202 | .builtin_call_node = .zero, |
| 20203 | .arg_index = 1, |
| 20204 | } }, |
| 20205 | }; |
| 20206 | const field_names_src: LazySrcLoc = .{ |
| 20207 | .base_node_inst = tracked_inst, |
| 20208 | .offset = .{ .node_offset_builtin_call_arg = .{ |
| 20209 | .builtin_call_node = .zero, |
| 20210 | .arg_index = 2, |
| 20211 | } }, |
| 20212 | }; |
| 20213 | const field_types_src: LazySrcLoc = .{ |
| 20214 | .base_node_inst = tracked_inst, |
| 20215 | .offset = .{ .node_offset_builtin_call_arg = .{ |
| 20216 | .builtin_call_node = .zero, |
| 20217 | .arg_index = 3, |
| 20218 | } }, |
| 20219 | }; |
| 20220 | const field_attrs_src: LazySrcLoc = .{ |
| 20221 | .base_node_inst = tracked_inst, |
| 20222 | .offset = .{ .node_offset_builtin_call_arg = .{ |
| 20223 | .builtin_call_node = .zero, |
| 20224 | .arg_index = 4, |
| 20225 | } }, |
| 20226 | }; |
| 20227 | |
| 20228 | const container_layout_ty = try sema.getStdLangType(layout_src, .@"Type.ContainerLayout"); |
| 20229 | const single_field_attrs_ty = try sema.getStdLangType(field_attrs_src, .@"Type.Struct.FieldAttributes"); |
| 20230 | |
| 20231 | const layout_uncoerced = sema.resolveInst(extra.layout); |
| 20232 | const layout_coerced = try sema.coerce(block, container_layout_ty, layout_uncoerced, layout_src); |
| 20233 | const layout_val = try sema.resolveConstDefinedValue(block, layout_src, layout_coerced, .{ .simple = .struct_layout }); |
| 20234 | const layout = try sema.interpretStdLangType(block, layout_src, layout_val, std.lang.Type.ContainerLayout); |
| 20235 | |
| 20236 | const backing_int_ty_uncoerced = sema.resolveInst(extra.backing_ty); |
| 20237 | const backing_int_ty_coerced = try sema.coerce(block, .optional_type, backing_int_ty_uncoerced, backing_ty_src); |
| 20238 | const backing_int_ty_val = try sema.resolveConstDefinedValue(block, backing_ty_src, backing_int_ty_coerced, .{ .simple = .packed_struct_backing_int_type }); |
| 20239 | |
| 20240 | const field_names_uncoerced = sema.resolveInst(extra.field_names); |
| 20241 | const field_names_coerced = try sema.coerce(block, .slice_const_slice_const_u8, field_names_uncoerced, field_names_src); |
| 20242 | const field_names_slice = try sema.resolveConstDefinedValue(block, field_names_src, field_names_coerced, .{ .simple = .struct_field_names }); |
| 20243 | const field_names_arr = try sema.derefSliceAsArray(block, field_names_src, field_names_slice, .{ .simple = .struct_field_names }); |
| 20244 | |
| 20245 | const fields_len = try sema.usizeCast(block, src, field_names_arr.typeOf(zcu).arrayLen(zcu)); |
| 20246 | |
| 20247 | const field_types_ty = try pt.singleConstPtrType(try pt.arrayType(.{ |
| 20248 | .len = fields_len, |
| 20249 | .child = .type_type, |
| 20250 | })); |
| 20251 | const field_attrs_ty = try pt.singleConstPtrType(try pt.arrayType(.{ |
| 20252 | .len = fields_len, |
| 20253 | .child = single_field_attrs_ty.toIntern(), |
| 20254 | })); |
| 20255 | |
| 20256 | const field_types_uncoerced = sema.resolveInst(extra.field_types); |
| 20257 | const field_types_coerced = try sema.coerce(block, field_types_ty, field_types_uncoerced, field_types_src); |
| 20258 | const field_types_slice = try sema.resolveConstDefinedValue(block, field_types_src, field_types_coerced, .{ .simple = .struct_field_types }); |
| 20259 | const field_types_arr = try sema.derefSliceAsArray(block, field_types_src, field_types_slice, .{ .simple = .struct_field_types }); |
| 20260 | |
| 20261 | const field_attrs_uncoerced = sema.resolveInst(extra.field_attrs); |
| 20262 | const field_attrs_coerced = try sema.coerce(block, field_attrs_ty, field_attrs_uncoerced, field_attrs_src); |
| 20263 | const field_attrs_slice = try sema.resolveConstDefinedValue(block, field_attrs_src, field_attrs_coerced, .{ .simple = .struct_field_attrs }); |
| 20264 | const field_attrs_arr = try sema.derefSliceAsArray(block, field_attrs_src, field_attrs_slice, .{ .simple = .struct_field_attrs }); |
| 20265 | |
| 20266 | // Before we begin, check for undefs... |
| 20267 | if (try sema.anyUndef(block, field_attrs_src, field_attrs_arr)) { |
| 20268 | return sema.failWithUseOfUndef(block, field_attrs_src, null); |
| 20269 | } |
| 20270 | if (try sema.anyUndef(block, field_types_src, field_types_arr)) { |
| 20271 | return sema.failWithUseOfUndef(block, field_types_src, null); |
| 20272 | } |
| 20273 | // We don't need to check `field_names_arr`, because `sliceToIpString` will check that for us. |
| 20274 | if (try sema.anyUndef(block, backing_ty_src, backing_int_ty_val)) { |
| 20275 | return sema.failWithUseOfUndef(block, backing_ty_src, null); |
| 20276 | } |
| 20277 | |
| 20278 | // Most validation of this type happens during type resolution. We basically need to do the work |
| 20279 | // which AstGen would normally do. An exception is checking for duplicate field names, which is |
| 20280 | // handled by type resolution---it just simplifies some logic a little. |
| 20281 | |
| 20282 | // As well as validation, we're going to gather some information about the fields, and construct |
| 20283 | // a hash representing the inputs for deduplication purposes. |
| 20284 | |
| 20285 | var any_comptime_fields = false; |
| 20286 | var any_field_defaults = false; |
| 20287 | var any_field_aligns = false; |
| 20288 | |
| 20289 | // TODO: use a longer hash! |
| 20290 | var hasher = std.hash.Wyhash.init(0); |
| 20291 | std.hash.autoHash(&hasher, layout); |
| 20292 | std.hash.autoHash(&hasher, backing_int_ty_val); |
| 20293 | |
| 20294 | const backing_int_ty: ?Type = if (backing_int_ty_val.optionalValue(zcu)) |backing| ty: { |
| 20295 | switch (layout) { |
| 20296 | .auto, .@"extern" => return sema.fail(block, backing_ty_src, "non-packed struct does not support backing integer type", .{}), |
| 20297 | .@"packed" => {}, |
| 20298 | } |
| 20299 | break :ty backing.toType(); |
| 20300 | } else null; |
| 20301 | |
| 20302 | // The field *type* array has already been deduplicated for us thanks to the InternPool! |
| 20303 | std.hash.autoHash(&hasher, field_types_arr); |
| 20304 | // However, for field names and attributes, we need to actually iterate the individual fields, |
| 20305 | // because the presence of pointers (the `[]const u8` for the name and the `*const anyopaque` |
| 20306 | // for the default value) means that distinct interned values could ultimately result in the |
| 20307 | // same struct type. |
| 20308 | for (0..fields_len) |field_idx| { |
| 20309 | const field_name_val = try field_names_arr.elemValue(pt, field_idx); |
| 20310 | const field_attrs_val = try field_attrs_arr.elemValue(pt, field_idx); |
| 20311 | |
| 20312 | const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, .{ .simple = .struct_field_names }); |
| 20313 | |
| 20314 | const field_attr_comptime = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex( |
| 20315 | std.lang.Type.Struct.FieldAttributes, |
| 20316 | "comptime", |
| 20317 | ).?); |
| 20318 | const field_attr_align = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex( |
| 20319 | std.lang.Type.Struct.FieldAttributes, |
| 20320 | "align", |
| 20321 | ).?); |
| 20322 | const field_attr_default_value_ptr = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex( |
| 20323 | std.lang.Type.Struct.FieldAttributes, |
| 20324 | "default_value_ptr", |
| 20325 | ).?); |
| 20326 | |
| 20327 | const field_default: InternPool.Index = d: { |
| 20328 | const ptr_val = field_attr_default_value_ptr.optionalValue(zcu) orelse break :d .none; |
| 20329 | const field_ty = (try field_types_arr.elemValue(pt, field_idx)).toType(); |
| 20330 | const ptr_ty = try pt.singleConstPtrType(field_ty); |
| 20331 | const deref_val = try sema.pointerDeref(block, field_attrs_src, ptr_val, ptr_ty) orelse return sema.failWithNeededComptime( |
| 20332 | block, |
| 20333 | field_attrs_src, |
| 20334 | .{ .simple = .struct_field_default_value }, |
| 20335 | ); |
| 20336 | if (deref_val.canMutateComptimeVarState(zcu)) { |
| 20337 | return sema.failWithContainsReferenceToComptimeVar(block, field_attrs_src, field_name, "field default value", deref_val); |
| 20338 | } |
| 20339 | any_field_defaults = true; |
| 20340 | break :d deref_val.toIntern(); |
| 20341 | }; |
| 20342 | |
| 20343 | if (field_attr_comptime.toBool()) { |
| 20344 | if (field_default == .none) { |
| 20345 | return sema.fail(block, field_attrs_src, "comptime field without default initialization value", .{}); |
| 20346 | } |
| 20347 | if (layout != .auto) { |
| 20348 | return sema.fail(block, field_attrs_src, "{t} struct fields cannot be marked comptime", .{layout}); |
| 20349 | } |
| 20350 | any_comptime_fields = true; |
| 20351 | } |
| 20352 | |
| 20353 | if (field_attr_align.optionalValue(zcu)) |align_val| { |
| 20354 | if (layout == .@"packed") { |
| 20355 | return sema.fail(block, field_attrs_src, "packed struct fields cannot be aligned", .{}); |
| 20356 | } |
| 20357 | // Trigger a compile error if the alignment is invalid. |
| 20358 | _ = try sema.validateAlign(block, field_attrs_src, align_val.toUnsignedInt(zcu)); |
| 20359 | any_field_aligns = true; |
| 20360 | } |
| 20361 | |
| 20362 | std.hash.autoHash(&hasher, .{ |
| 20363 | field_name, |
| 20364 | field_attr_comptime, |
| 20365 | field_attr_align, |
| 20366 | field_default, |
| 20367 | }); |
| 20368 | } |
| 20369 | |
| 20370 | switch (try ip.getReifiedStructType(gpa, io, pt.tid, .{ |
| 20371 | .zir_index = tracked_inst, |
| 20372 | .type_hash = hasher.final(), |
| 20373 | .fields_len = @intCast(fields_len), |
| 20374 | .layout = layout, |
| 20375 | .any_comptime_fields = any_comptime_fields, |
| 20376 | .any_field_defaults = any_field_defaults, |
| 20377 | .any_field_aligns = any_field_aligns, |
| 20378 | .packed_backing_int_type = if (backing_int_ty) |ty| ty.toIntern() else .none, |
| 20379 | })) { |
| 20380 | .existing => |ty| { |
| 20381 | try sema.addTypeReferenceEntry(src, .fromInterned(ty)); |
| 20382 | // No need for `ensureNamespaceUpToDate` because this type's namespace is always empty. |
| 20383 | return .fromIntern(ty); |
| 20384 | }, |
| 20385 | .wip => |wip| { |
| 20386 | errdefer wip.cancel(ip, pt.tid); |
| 20387 | try sema.setTypeName(block, &wip, name_strategy, "struct", inst); |
| 20388 | for (0..fields_len) |field_idx| { |
| 20389 | const field_name_val = try field_names_arr.elemValue(pt, field_idx); |
| 20390 | const field_attrs_val = try field_attrs_arr.elemValue(pt, field_idx); |
| 20391 | |
| 20392 | // No source location or reason; first loop checked this is valid. |
| 20393 | const field_name = try sema.sliceToIpString(block, .unneeded, field_name_val, undefined); |
| 20394 | wip.field_names.get(ip)[field_idx] = field_name; |
| 20395 | |
| 20396 | const field_ty = (try field_types_arr.elemValue(pt, field_idx)).toType(); |
| 20397 | wip.field_types.get(ip)[field_idx] = field_ty.toIntern(); |
| 20398 | |
| 20399 | const field_attr_comptime = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex( |
| 20400 | std.lang.Type.Struct.FieldAttributes, |
| 20401 | "comptime", |
| 20402 | ).?); |
| 20403 | const field_attr_align = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex( |
| 20404 | std.lang.Type.Struct.FieldAttributes, |
| 20405 | "align", |
| 20406 | ).?); |
| 20407 | const field_attr_default_value_ptr = try field_attrs_val.fieldValue(pt, std.meta.fieldIndex( |
| 20408 | std.lang.Type.Struct.FieldAttributes, |
| 20409 | "default_value_ptr", |
| 20410 | ).?); |
| 20411 | |
| 20412 | if (field_attr_comptime.toBool()) { |
| 20413 | const bit_bag_index = field_idx / 32; |
| 20414 | const mask = @as(u32, 1) << @intCast(field_idx % 32); |
| 20415 | wip.field_is_comptime_bits.getAll(ip)[bit_bag_index] |= mask; |
| 20416 | } |
| 20417 | |
| 20418 | if (field_attr_default_value_ptr.optionalValue(zcu)) |ptr_val| { |
| 20419 | const ptr_ty = try pt.singleConstPtrType(field_ty); |
| 20420 | // No source location; first loop checked this is valid. |
| 20421 | const deref_val = (try sema.pointerDeref(block, .unneeded, ptr_val, ptr_ty)).?; |
| 20422 | wip.field_values.get(ip)[field_idx] = deref_val.toIntern(); |
| 20423 | } else if (any_field_defaults) { |
| 20424 | wip.field_values.get(ip)[field_idx] = .none; |
| 20425 | } |
| 20426 | |
| 20427 | if (field_attr_align.optionalValue(zcu)) |field_align_val| { |
| 20428 | const bytes = field_align_val.toUnsignedInt(zcu); |
| 20429 | // No source location; first loop checked this is valid. |
| 20430 | const a = try sema.validateAlign(block, .unneeded, bytes); |
| 20431 | wip.field_aligns.get(ip)[field_idx] = a; |
| 20432 | } else if (any_field_aligns) { |
| 20433 | wip.field_aligns.get(ip)[field_idx] = .none; |
| 20434 | } |
| 20435 | } |
| 20436 | |
| 20437 | const new_namespace_index = try pt.createNamespace(.{ |
| 20438 | .parent = block.namespace.toOptional(), |
| 20439 | .owner_type = wip.index, |
| 20440 | .file_scope = block.getFileScopeIndex(zcu), |
| 20441 | .generation = zcu.generation, |
| 20442 | }); |
| 20443 | errdefer pt.destroyNamespace(new_namespace_index); |
| 20444 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); |
| 20445 | try sema.addTypeReferenceEntry(src, .fromInterned(wip.index)); |
| 20446 | return .fromIntern(wip.finish(ip, new_namespace_index)); |
| 20447 | }, |
| 20448 | } |
| 20449 | } |
| 20450 | |
| 20451 | fn zirReifyUnion( |
| 20452 | sema: *Sema, |
| 20453 | block: *Block, |
| 20454 | extended: Zir.Inst.Extended.InstData, |
| 20455 | inst: Zir.Inst.Index, |
| 20456 | ) CompileError!Air.Inst.Ref { |
| 20457 | const pt = sema.pt; |
| 20458 | const zcu = pt.zcu; |
| 20459 | const comp = zcu.comp; |
| 20460 | const gpa = comp.gpa; |
| 20461 | const io = comp.io; |
| 20462 | const ip = &zcu.intern_pool; |
| 20463 | |
| 20464 | const name_strategy: Zir.Inst.NameStrategy = @fromBackingInt(@intCast(extended.small)); |
| 20465 | const extra = sema.code.extraData(Zir.Inst.ReifyUnion, extended.operand).data; |
| 20466 | const tracked_inst = try block.trackZir(inst); |
| 20467 | const src: LazySrcLoc = .{ |
| 20468 | .base_node_inst = tracked_inst, |
| 20469 | .offset = .nodeOffset(.zero), |
| 20470 | }; |
| 20471 | |
| 20472 | const layout_src: LazySrcLoc = .{ |
| 20473 | .base_node_inst = tracked_inst, |
| 20474 | .offset = .{ .node_offset_builtin_call_arg = .{ |
| 20475 | .builtin_call_node = .zero, |
| 20476 | .arg_index = 0, |
| 20477 | } }, |
| 20478 | }; |
| 20479 | const arg_ty_src: LazySrcLoc = .{ |
| 20480 | .base_node_inst = tracked_inst, |
| 20481 | .offset = .{ .node_offset_builtin_call_arg = .{ |
| 20482 | .builtin_call_node = .zero, |
| 20483 | .arg_index = 1, |
| 20484 | } }, |
| 20485 | }; |
| 20486 | const field_names_src: LazySrcLoc = .{ |
| 20487 | .base_node_inst = tracked_inst, |
| 20488 | .offset = .{ .node_offset_builtin_call_arg = .{ |
| 20489 | .builtin_call_node = .zero, |
| 20490 | .arg_index = 2, |
| 20491 | } }, |
| 20492 | }; |
| 20493 | const field_types_src: LazySrcLoc = .{ |
| 20494 | .base_node_inst = tracked_inst, |
| 20495 | .offset = .{ .node_offset_builtin_call_arg = .{ |
| 20496 | .builtin_call_node = .zero, |
| 20497 | .arg_index = 3, |
| 20498 | } }, |
| 20499 | }; |
| 20500 | const field_attrs_src: LazySrcLoc = .{ |
| 20501 | .base_node_inst = tracked_inst, |
| 20502 | .offset = .{ .node_offset_builtin_call_arg = .{ |
| 20503 | .builtin_call_node = .zero, |
| 20504 | .arg_index = 4, |
| 20505 | } }, |
| 20506 | }; |
| 20507 | |
| 20508 | const container_layout_ty = try sema.getStdLangType(layout_src, .@"Type.ContainerLayout"); |
| 20509 | const single_field_attrs_ty = try sema.getStdLangType(field_attrs_src, .@"Type.Union.FieldAttributes"); |
| 20510 | |
| 20511 | const layout_uncoerced = sema.resolveInst(extra.layout); |
| 20512 | const layout_coerced = try sema.coerce(block, container_layout_ty, layout_uncoerced, layout_src); |
| 20513 | const layout_val = try sema.resolveConstDefinedValue(block, layout_src, layout_coerced, .{ .simple = .union_layout }); |
| 20514 | const layout = try sema.interpretStdLangType(block, layout_src, layout_val, std.lang.Type.ContainerLayout); |
| 20515 | |
| 20516 | const arg_ty_uncoerced = sema.resolveInst(extra.arg_ty); |
| 20517 | const arg_ty_coerced = try sema.coerce(block, .optional_type, arg_ty_uncoerced, arg_ty_src); |
| 20518 | const arg_ty_val = try sema.resolveConstDefinedValue(block, arg_ty_src, arg_ty_coerced, switch (layout) { |
| 20519 | .@"packed" => .{ .simple = .packed_union_backing_int_type }, |
| 20520 | .auto, .@"extern" => .{ .simple = .union_enum_tag_type }, |
| 20521 | }); |
| 20522 | |
| 20523 | const field_names_uncoerced = sema.resolveInst(extra.field_names); |
| 20524 | const field_names_coerced = try sema.coerce(block, .slice_const_slice_const_u8, field_names_uncoerced, field_names_src); |
| 20525 | const field_names_slice = try sema.resolveConstDefinedValue(block, field_names_src, field_names_coerced, .{ .simple = .union_field_names }); |
| 20526 | const field_names_arr = try sema.derefSliceAsArray(block, field_names_src, field_names_slice, .{ .simple = .union_field_names }); |
| 20527 | |
| 20528 | const fields_len = try sema.usizeCast(block, src, field_names_arr.typeOf(zcu).arrayLen(zcu)); |
| 20529 | |
| 20530 | const field_types_ty = try pt.singleConstPtrType(try pt.arrayType(.{ |
| 20531 | .len = fields_len, |
| 20532 | .child = .type_type, |
| 20533 | })); |
| 20534 | const field_attrs_ty = try pt.singleConstPtrType(try pt.arrayType(.{ |
| 20535 | .len = fields_len, |
| 20536 | .child = single_field_attrs_ty.toIntern(), |
| 20537 | })); |
| 20538 | |
| 20539 | const field_types_uncoerced = sema.resolveInst(extra.field_types); |
| 20540 | const field_types_coerced = try sema.coerce(block, field_types_ty, field_types_uncoerced, field_types_src); |
| 20541 | const field_types_slice = try sema.resolveConstDefinedValue(block, field_types_src, field_types_coerced, .{ .simple = .union_field_types }); |
| 20542 | const field_types_arr = try sema.derefSliceAsArray(block, field_types_src, field_types_slice, .{ .simple = .union_field_types }); |
| 20543 | |
| 20544 | const field_attrs_uncoerced = sema.resolveInst(extra.field_attrs); |
| 20545 | const field_attrs_coerced = try sema.coerce(block, field_attrs_ty, field_attrs_uncoerced, field_attrs_src); |
| 20546 | const field_attrs_slice = try sema.resolveConstDefinedValue(block, field_attrs_src, field_attrs_coerced, .{ .simple = .union_field_attrs }); |
| 20547 | const field_attrs_arr = try sema.derefSliceAsArray(block, field_attrs_src, field_attrs_slice, .{ .simple = .union_field_attrs }); |
| 20548 | |
| 20549 | // Before we begin, check for undefs... |
| 20550 | if (try sema.anyUndef(block, field_attrs_src, field_attrs_arr)) { |
| 20551 | return sema.failWithUseOfUndef(block, field_attrs_src, null); |
| 20552 | } |
| 20553 | if (try sema.anyUndef(block, field_types_src, field_types_arr)) { |
| 20554 | return sema.failWithUseOfUndef(block, field_types_src, null); |
| 20555 | } |
| 20556 | // We don't need to check `field_names_arr`, because `sliceToIpString` will check that for us. |
| 20557 | if (try sema.anyUndef(block, arg_ty_src, arg_ty_val)) { |
| 20558 | return sema.failWithUseOfUndef(block, arg_ty_src, null); |
| 20559 | } |
| 20560 | |
| 20561 | // Most validation of this type happens during type resolution. We basically need to do the work |
| 20562 | // which AstGen would normally do. An exception is checking for duplicate field names, which is |
| 20563 | // handled by type resolution---it just simplifies some logic a little. |
| 20564 | |
| 20565 | // As well as validation, we're going to gather some information about the fields, and construct |
| 20566 | // a hash representing the inputs for deduplication purposes. |
| 20567 | |
| 20568 | var any_field_aligns = false; |
| 20569 | |
| 20570 | // TODO: use a longer hash! |
| 20571 | var hasher = std.hash.Wyhash.init(0); |
| 20572 | std.hash.autoHash(&hasher, layout); |
| 20573 | std.hash.autoHash(&hasher, arg_ty_val); |
| 20574 | |
| 20575 | const explicit_tag_ty: ?Type, const explicit_packed_backing_type: ?Type = ty: { |
| 20576 | const arg_ty = arg_ty_val.optionalValue(zcu) orelse break :ty .{ null, null }; |
| 20577 | switch (layout) { |
| 20578 | .@"extern" => return sema.fail(block, arg_ty_src, "extern union does not support enum tag type", .{}), |
| 20579 | .@"packed" => break :ty .{ null, arg_ty.toType() }, |
| 20580 | .auto => break :ty .{ arg_ty.toType(), null }, |
| 20581 | } |
| 20582 | }; |
| 20583 | |
| 20584 | // `field_types_arr` and `field_attrs_arr` are already deduplicated by the InternPool! |
| 20585 | std.hash.autoHash(&hasher, field_types_arr); |
| 20586 | std.hash.autoHash(&hasher, field_attrs_arr); |
| 20587 | // However, for field names, we need to iterate the individual fields, because the pointers (the |
| 20588 | // names are slices) mean that distinct values could ultimately result in the same union type. |
| 20589 | for (0..fields_len) |field_idx| { |
| 20590 | const field_name_val = try field_names_arr.elemValue(pt, field_idx); |
| 20591 | const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, .{ .simple = .union_field_names }); |
| 20592 | std.hash.autoHash(&hasher, field_name); |
| 20593 | |
| 20594 | const field_attrs = try sema.interpretStdLangType( |
| 20595 | block, |
| 20596 | field_attrs_src, |
| 20597 | try field_attrs_arr.elemValue(pt, field_idx), |
| 20598 | std.lang.Type.Union.FieldAttributes, |
| 20599 | ); |
| 20600 | if (field_attrs.@"align") |bytes| { |
| 20601 | if (layout == .@"packed") { |
| 20602 | return sema.fail(block, field_attrs_src, "packed union fields cannot be aligned", .{}); |
| 20603 | } |
| 20604 | // Trigger a compile error if the alignment is invalid. |
| 20605 | _ = try sema.validateAlign(block, field_attrs_src, bytes); |
| 20606 | any_field_aligns = true; |
| 20607 | } |
| 20608 | } |
| 20609 | |
| 20610 | switch (try ip.getReifiedUnionType(gpa, io, pt.tid, .{ |
| 20611 | .zir_index = tracked_inst, |
| 20612 | .type_hash = hasher.final(), |
| 20613 | .fields_len = @intCast(fields_len), |
| 20614 | .layout = layout, |
| 20615 | .any_field_aligns = any_field_aligns, |
| 20616 | .tag_usage = tag: { |
| 20617 | if (explicit_tag_ty != null) break :tag .tagged; |
| 20618 | if (layout == .auto and block.wantSafeTypes()) break :tag .safety; |
| 20619 | break :tag .none; |
| 20620 | }, |
| 20621 | .enum_tag_type = if (explicit_tag_ty) |ty| ty.toIntern() else .none, |
| 20622 | .packed_backing_int_type = if (explicit_packed_backing_type) |ty| ty.toIntern() else .none, |
| 20623 | })) { |
| 20624 | .existing => |ty| { |
| 20625 | try sema.addTypeReferenceEntry(src, .fromInterned(ty)); |
| 20626 | // No need for `ensureNamespaceUpToDate` because this type's namespace is always empty. |
| 20627 | return .fromIntern(ty); |
| 20628 | }, |
| 20629 | .wip => |wip| { |
| 20630 | errdefer wip.cancel(ip, pt.tid); |
| 20631 | try sema.setTypeName(block, &wip, name_strategy, "union", inst); |
| 20632 | |
| 20633 | for (0..fields_len) |field_idx| { |
| 20634 | const field_name_val = try field_names_arr.elemValue(pt, field_idx); |
| 20635 | // No source location or reason; first loop checked this is valid. |
| 20636 | const field_name = try sema.sliceToIpString(block, .unneeded, field_name_val, undefined); |
| 20637 | wip.field_names.get(ip)[field_idx] = field_name; |
| 20638 | |
| 20639 | const field_ty = (try field_types_arr.elemValue(pt, field_idx)).toType(); |
| 20640 | wip.field_types.get(ip)[field_idx] = field_ty.toIntern(); |
| 20641 | |
| 20642 | // No source location; first loop checked this is valid. |
| 20643 | const field_attrs = try sema.interpretStdLangType( |
| 20644 | block, |
| 20645 | .unneeded, |
| 20646 | try field_attrs_arr.elemValue(pt, field_idx), |
| 20647 | std.lang.Type.Union.FieldAttributes, |
| 20648 | ); |
| 20649 | if (field_attrs.@"align") |bytes| { |
| 20650 | // No source location; first loop checked this is valid. |
| 20651 | const a = try sema.validateAlign(block, .unneeded, bytes); |
| 20652 | wip.field_aligns.get(ip)[field_idx] = a; |
| 20653 | } else if (any_field_aligns) { |
| 20654 | wip.field_aligns.get(ip)[field_idx] = .none; |
| 20655 | } |
| 20656 | } |
| 20657 | |
| 20658 | const new_namespace_index = try pt.createNamespace(.{ |
| 20659 | .parent = block.namespace.toOptional(), |
| 20660 | .owner_type = wip.index, |
| 20661 | .file_scope = block.getFileScopeIndex(zcu), |
| 20662 | .generation = zcu.generation, |
| 20663 | }); |
| 20664 | errdefer pt.destroyNamespace(new_namespace_index); |
| 20665 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); |
| 20666 | try sema.addTypeReferenceEntry(src, .fromInterned(wip.index)); |
| 20667 | return .fromIntern(wip.finish(ip, new_namespace_index)); |
| 20668 | }, |
| 20669 | } |
| 20670 | } |
| 20671 | |
| 20672 | fn zirReifyEnum( |
| 20673 | sema: *Sema, |
| 20674 | block: *Block, |
| 20675 | extended: Zir.Inst.Extended.InstData, |
| 20676 | inst: Zir.Inst.Index, |
| 20677 | ) CompileError!Air.Inst.Ref { |
| 20678 | const pt = sema.pt; |
| 20679 | const zcu = pt.zcu; |
| 20680 | const comp = zcu.comp; |
| 20681 | const gpa = comp.gpa; |
| 20682 | const io = comp.io; |
| 20683 | const ip = &zcu.intern_pool; |
| 20684 | |
| 20685 | const name_strategy: Zir.Inst.NameStrategy = @fromBackingInt(@intCast(extended.small)); |
| 20686 | const extra = sema.code.extraData(Zir.Inst.ReifyEnum, extended.operand).data; |
| 20687 | const tracked_inst = try block.trackZir(inst); |
| 20688 | const src: LazySrcLoc = .{ |
| 20689 | .base_node_inst = tracked_inst, |
| 20690 | .offset = .nodeOffset(.zero), |
| 20691 | }; |
| 20692 | |
| 20693 | const tag_ty_src: LazySrcLoc = .{ |
| 20694 | .base_node_inst = tracked_inst, |
| 20695 | .offset = .{ .node_offset_builtin_call_arg = .{ |
| 20696 | .builtin_call_node = .zero, |
| 20697 | .arg_index = 0, |
| 20698 | } }, |
| 20699 | }; |
| 20700 | const mode_src: LazySrcLoc = .{ |
| 20701 | .base_node_inst = tracked_inst, |
| 20702 | .offset = .{ .node_offset_builtin_call_arg = .{ |
| 20703 | .builtin_call_node = .zero, |
| 20704 | .arg_index = 1, |
| 20705 | } }, |
| 20706 | }; |
| 20707 | const field_names_src: LazySrcLoc = .{ |
| 20708 | .base_node_inst = tracked_inst, |
| 20709 | .offset = .{ .node_offset_builtin_call_arg = .{ |
| 20710 | .builtin_call_node = .zero, |
| 20711 | .arg_index = 2, |
| 20712 | } }, |
| 20713 | }; |
| 20714 | const field_values_src: LazySrcLoc = .{ |
| 20715 | .base_node_inst = tracked_inst, |
| 20716 | .offset = .{ .node_offset_builtin_call_arg = .{ |
| 20717 | .builtin_call_node = .zero, |
| 20718 | .arg_index = 3, |
| 20719 | } }, |
| 20720 | }; |
| 20721 | |
| 20722 | const enum_mode_ty = try sema.getStdLangType(mode_src, .@"Type.Enum.Mode"); |
| 20723 | |
| 20724 | const tag_ty_uncoerced = sema.resolveInst(extra.tag_ty); |
| 20725 | const tag_ty_coerced = try sema.coerce(block, .type, tag_ty_uncoerced, tag_ty_src); |
| 20726 | const tag_ty_val = try sema.resolveConstDefinedValue(block, tag_ty_src, tag_ty_coerced, .{ .simple = .enum_int_tag_type }); |
| 20727 | const tag_ty = tag_ty_val.toType(); |
| 20728 | |
| 20729 | const mode_uncoerced = sema.resolveInst(extra.mode); |
| 20730 | const mode_coerced = try sema.coerce(block, enum_mode_ty, mode_uncoerced, mode_src); |
| 20731 | const mode_val = try sema.resolveConstDefinedValue(block, mode_src, mode_coerced, .{ .simple = .type }); |
| 20732 | const nonexhaustive = switch (try sema.interpretStdLangType(block, mode_src, mode_val, std.lang.Type.Enum.Mode)) { |
| 20733 | .exhaustive => false, |
| 20734 | .nonexhaustive => true, |
| 20735 | }; |
| 20736 | |
| 20737 | const field_names_uncoerced = sema.resolveInst(extra.field_names); |
| 20738 | const field_names_coerced = try sema.coerce(block, .slice_const_slice_const_u8, field_names_uncoerced, field_names_src); |
| 20739 | const field_names_slice = try sema.resolveConstDefinedValue(block, field_names_src, field_names_coerced, .{ .simple = .enum_field_names }); |
| 20740 | const field_names_arr = try sema.derefSliceAsArray(block, field_names_src, field_names_slice, .{ .simple = .enum_field_names }); |
| 20741 | |
| 20742 | const fields_len = try sema.usizeCast(block, src, field_names_arr.typeOf(zcu).arrayLen(zcu)); |
| 20743 | |
| 20744 | const field_values_ty = try pt.singleConstPtrType(try pt.arrayType(.{ |
| 20745 | .len = fields_len, |
| 20746 | .child = tag_ty.toIntern(), |
| 20747 | })); |
| 20748 | |
| 20749 | const field_values_uncoerced = sema.resolveInst(extra.field_values); |
| 20750 | const field_values_coerced = try sema.coerce(block, field_values_ty, field_values_uncoerced, field_values_src); |
| 20751 | const field_values_slice = try sema.resolveConstDefinedValue(block, field_values_src, field_values_coerced, .{ .simple = .enum_field_values }); |
| 20752 | const field_values_arr = try sema.derefSliceAsArray(block, field_values_src, field_values_slice, .{ .simple = .enum_field_values }); |
| 20753 | |
| 20754 | // Before we begin, check for undefs... |
| 20755 | if (try sema.anyUndef(block, field_values_src, field_values_arr)) { |
| 20756 | return sema.failWithUseOfUndef(block, field_values_src, null); |
| 20757 | } |
| 20758 | // We don't need to check `field_names_arr`, because `sliceToIpString` will check that for us. |
| 20759 | |
| 20760 | // Most validation of this type happens during type resolution. We basically need to do the work |
| 20761 | // which AstGen would normally do. An exception is checking for duplicate field names, which is |
| 20762 | // handled by type resolution---it just simplifies some logic a little. |
| 20763 | |
| 20764 | // As well as validation, we're going to gather some information about the fields, and construct |
| 20765 | // a hash representing the inputs for deduplication purposes. |
| 20766 | |
| 20767 | // TODO: use a longer hash! |
| 20768 | var hasher = std.hash.Wyhash.init(0); |
| 20769 | std.hash.autoHash(&hasher, tag_ty.toIntern()); |
| 20770 | std.hash.autoHash(&hasher, nonexhaustive); |
| 20771 | std.hash.autoHash(&hasher, fields_len); |
| 20772 | // `field_values_arr` is already deduplicated by the InternPool! |
| 20773 | std.hash.autoHash(&hasher, field_values_arr); |
| 20774 | // However, for field names, we need to iterate the individual fields, because the pointers (the |
| 20775 | // names are slices) mean that distinct values could ultimately result in the same enum type. |
| 20776 | for (0..fields_len) |field_idx| { |
| 20777 | const field_name_val = try field_names_arr.elemValue(pt, field_idx); |
| 20778 | const field_name = try sema.sliceToIpString(block, field_names_src, field_name_val, .{ .simple = .enum_field_names }); |
| 20779 | std.hash.autoHash(&hasher, field_name); |
| 20780 | } |
| 20781 | |
| 20782 | switch (try ip.getReifiedEnumType(gpa, io, pt.tid, .{ |
| 20783 | .zir_index = tracked_inst, |
| 20784 | .type_hash = hasher.final(), |
| 20785 | .fields_len = @intCast(fields_len), |
| 20786 | .nonexhaustive = nonexhaustive, |
| 20787 | .int_tag_type = tag_ty.toIntern(), |
| 20788 | })) { |
| 20789 | .existing => |ty| { |
| 20790 | try sema.addTypeReferenceEntry(src, .fromInterned(ty)); |
| 20791 | // No need for `ensureNamespaceUpToDate` because this type's namespace is always empty. |
| 20792 | return .fromIntern(ty); |
| 20793 | }, |
| 20794 | .wip => |wip| { |
| 20795 | errdefer wip.cancel(ip, pt.tid); |
| 20796 | |
| 20797 | try sema.setTypeName(block, &wip, name_strategy, "enum", inst); |
| 20798 | |
| 20799 | // Populate field names and values. Duplicate checking will be handled by type resolution. |
| 20800 | for (0..fields_len) |field_index| { |
| 20801 | const field_name_val = try field_names_arr.elemValue(pt, field_index); |
| 20802 | // No source location or reason; first loop checked this is valid. |
| 20803 | const field_name = try sema.sliceToIpString(block, .unneeded, field_name_val, undefined); |
| 20804 | wip.field_names.get(ip)[field_index] = field_name; |
| 20805 | |
| 20806 | const field_val = try field_values_arr.elemValue(pt, field_index); |
| 20807 | wip.field_values.get(ip)[field_index] = field_val.toIntern(); |
| 20808 | } |
| 20809 | |
| 20810 | const new_namespace_index = try pt.createNamespace(.{ |
| 20811 | .parent = block.namespace.toOptional(), |
| 20812 | .owner_type = wip.index, |
| 20813 | .file_scope = block.getFileScopeIndex(zcu), |
| 20814 | .generation = zcu.generation, |
| 20815 | }); |
| 20816 | errdefer pt.destroyNamespace(new_namespace_index); |
| 20817 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); |
| 20818 | try sema.addTypeReferenceEntry(src, .fromInterned(wip.index)); |
| 20819 | return .fromIntern(wip.finish(ip, new_namespace_index)); |
| 20820 | }, |
| 20821 | } |
| 20822 | } |
| 20823 | |
| 20824 | fn zirReifySpirvType( |
| 20825 | sema: *Sema, |
| 20826 | block: *Block, |
| 20827 | extended: Zir.Inst.Extended.InstData, |
| 20828 | inst: Zir.Inst.Index, |
| 20829 | ) CompileError!Air.Inst.Ref { |
| 20830 | const pt = sema.pt; |
| 20831 | const zcu = pt.zcu; |
| 20832 | const comp = zcu.comp; |
| 20833 | const gpa = comp.gpa; |
| 20834 | const io = comp.io; |
| 20835 | const ip = &zcu.intern_pool; |
| 20836 | const target = zcu.getTarget(); |
| 20837 | |
| 20838 | const extra = sema.code.extraData(Zir.Inst.ReifySpirvType, extended.operand).data; |
| 20839 | const tracked_inst = try block.trackZir(inst); |
| 20840 | const src: LazySrcLoc = .{ |
| 20841 | .base_node_inst = tracked_inst, |
| 20842 | .offset = .nodeOffset(.zero), |
| 20843 | }; |
| 20844 | const operand_src: LazySrcLoc = .{ |
| 20845 | .base_node_inst = tracked_inst, |
| 20846 | .offset = .{ .node_offset_builtin_call_arg = .{ |
| 20847 | .builtin_call_node = .zero, |
| 20848 | .arg_index = 0, |
| 20849 | } }, |
| 20850 | }; |
| 20851 | |
| 20852 | if (!target.cpu.arch.isSpirV()) { |
| 20853 | return sema.fail( |
| 20854 | block, |
| 20855 | src, |
| 20856 | "builtin @SpirvType is only available when targeting SPIR-V; targeted CPU architecture is {t}", |
| 20857 | .{target.cpu.arch}, |
| 20858 | ); |
| 20859 | } |
| 20860 | |
| 20861 | const spirv_type_options_ty = try sema.getStdLangType(operand_src, .@"Type.Spirv"); |
| 20862 | const operand_uncoerced = sema.resolveInst(extra.operand); |
| 20863 | const operand_coerced = try sema.coerce(block, spirv_type_options_ty, operand_uncoerced, operand_src); |
| 20864 | const operand_val = try sema.resolveConstDefinedValue(block, operand_src, operand_coerced, .{ .simple = .type }); |
| 20865 | const union_val = ip.indexToKey(operand_val.toIntern()).un; |
| 20866 | |
| 20867 | if (try sema.anyUndef(block, operand_src, .fromInterned(union_val.val))) { |
| 20868 | return sema.failWithUseOfUndef(block, operand_src, null); |
| 20869 | } |
| 20870 | |
| 20871 | const tag = try sema.interpretStdLangType(block, src, .fromInterned(union_val.tag), @typeInfo(std.lang.Type.Spirv).@"union".tag_type.?); |
| 20872 | const ip_data: InternPool.Key.SpirvType = switch (tag) { |
| 20873 | .sampler => .{ |
| 20874 | .ty = .none, |
| 20875 | .flags = .{ |
| 20876 | .tag = .sampler, |
| 20877 | .usage = .unknown, |
| 20878 | .format = .unknown, |
| 20879 | .dim = .@"1d", |
| 20880 | .depth = .unknown, |
| 20881 | .access = .unknown, |
| 20882 | .is_arrayed = false, |
| 20883 | .is_multisampled = false, |
| 20884 | }, |
| 20885 | }, |
| 20886 | .image => ip_data: { |
| 20887 | const struct_type = ip.loadStructType(ip.typeOf(union_val.val)); |
| 20888 | const usage_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( |
| 20889 | ip, |
| 20890 | try ip.getOrPutString(gpa, io, pt.tid, "usage", .no_embedded_nulls), |
| 20891 | ).?); |
| 20892 | const format_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( |
| 20893 | ip, |
| 20894 | try ip.getOrPutString(gpa, io, pt.tid, "format", .no_embedded_nulls), |
| 20895 | ).?); |
| 20896 | const dim_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( |
| 20897 | ip, |
| 20898 | try ip.getOrPutString(gpa, io, pt.tid, "dim", .no_embedded_nulls), |
| 20899 | ).?); |
| 20900 | const depth_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( |
| 20901 | ip, |
| 20902 | try ip.getOrPutString(gpa, io, pt.tid, "depth", .no_embedded_nulls), |
| 20903 | ).?); |
| 20904 | const access_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( |
| 20905 | ip, |
| 20906 | try ip.getOrPutString(gpa, io, pt.tid, "access", .no_embedded_nulls), |
| 20907 | ).?); |
| 20908 | const arrayed_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( |
| 20909 | ip, |
| 20910 | try ip.getOrPutString(gpa, io, pt.tid, "arrayed", .no_embedded_nulls), |
| 20911 | ).?); |
| 20912 | const multisampled_val = try Value.fromInterned(union_val.val).fieldValue(pt, struct_type.nameIndex( |
| 20913 | ip, |
| 20914 | try ip.getOrPutString(gpa, io, pt.tid, "multisampled", .no_embedded_nulls), |
| 20915 | ).?); |
| 20916 | const format = try sema.interpretStdLangType(block, operand_src, format_val, std.lang.Type.Spirv.Image.Format); |
| 20917 | const dim = try sema.interpretStdLangType(block, operand_src, dim_val, std.lang.Type.Spirv.Image.Dimensionality); |
| 20918 | const depth = try sema.interpretStdLangType(block, operand_src, depth_val, std.lang.Type.Spirv.Image.Depth); |
| 20919 | const access = try sema.interpretStdLangType(block, operand_src, access_val, std.lang.Type.Spirv.Image.Access); |
| 20920 | |
| 20921 | switch (target.os.tag) { |
| 20922 | .opencl => if (access == .unknown) { |
| 20923 | return sema.fail(block, operand_src, "'access' field must be specified under the 'opencl' os", .{}); |
| 20924 | }, |
| 20925 | else => if (access != .unknown) { |
| 20926 | return sema.fail(block, operand_src, "access qualifier '.{t}' is only valid under the 'opencl' os", .{access}); |
| 20927 | }, |
| 20928 | } |
| 20929 | |
| 20930 | const arrayed = try sema.interpretStdLangType(block, operand_src, arrayed_val, bool); |
| 20931 | const multisampled = try sema.interpretStdLangType(block, operand_src, multisampled_val, bool); |
| 20932 | |
| 20933 | const usage_tag_val = usage_val.unionTag(zcu).?; |
| 20934 | const usage_tag = try sema.interpretStdLangType(block, operand_src, usage_tag_val, @typeInfo(std.lang.Type.Spirv.Image.Usage).@"union".tag_type.?); |
| 20935 | |
| 20936 | switch (target.os.tag) { |
| 20937 | .vulkan => { |
| 20938 | if (usage_tag == .unknown) { |
| 20939 | return sema.fail( |
| 20940 | block, |
| 20941 | operand_src, |
| 20942 | "'usage' must be '.sampled' or '.storage' under the 'vulkan' os (Sampled == 0 is forbidden)", |
| 20943 | .{}, |
| 20944 | ); |
| 20945 | } |
| 20946 | }, |
| 20947 | .opencl => { |
| 20948 | if (usage_tag != .unknown) { |
| 20949 | return sema.fail(block, operand_src, "'usage' must be '.unknown' under the 'opencl' os", .{}); |
| 20950 | } |
| 20951 | if (multisampled) { |
| 20952 | return sema.fail(block, operand_src, "'multisampled' must be 'false' under the 'opencl' os", .{}); |
| 20953 | } |
| 20954 | if (format != .unknown) { |
| 20955 | return sema.fail(block, operand_src, "'format' must be '.unknown' under the 'opencl' os", .{}); |
| 20956 | } |
| 20957 | if (dim == .cube) { |
| 20958 | return sema.fail(block, operand_src, "'dim' '.cube' is not allowed under the 'opencl' os", .{}); |
| 20959 | } |
| 20960 | if (arrayed and dim != .@"1d" and dim != .@"2d") { |
| 20961 | return sema.fail(block, operand_src, "'arrayed' may only be 'true' when 'dim' is '.1d' or '.2d' under the 'opencl' os", .{}); |
| 20962 | } |
| 20963 | }, |
| 20964 | else => {}, |
| 20965 | } |
| 20966 | |
| 20967 | break :ip_data .{ |
| 20968 | .ty = blk: { |
| 20969 | const sampled_type = usage_val.unionPayload(zcu).toType(); |
| 20970 | |
| 20971 | if (target.os.tag != .opencl and sampled_type.toIntern() == .void_type) { |
| 20972 | return sema.fail(block, operand_src, "'void' type for '{t}' field is only valid under the 'opencl' os", .{usage_tag}); |
| 20973 | } |
| 20974 | if (target.os.tag == .opencl and sampled_type.toIntern() != .void_type) { |
| 20975 | return sema.fail(block, operand_src, "'{t}' field type must be 'void' under the 'opencl' os", .{usage_tag}); |
| 20976 | } |
| 20977 | |
| 20978 | if (sampled_type.toIntern() != .void_type and |
| 20979 | (!sampled_type.hasRuntimeBits(zcu) or (!sampled_type.isRuntimeFloat() and !sampled_type.isInt(zcu)))) |
| 20980 | { |
| 20981 | return sema.fail(block, operand_src, "invalid '{t}' field value '{f}'", .{ usage_tag, sampled_type.fmt(pt) }); |
| 20982 | } |
| 20983 | |
| 20984 | if (target.os.tag == .vulkan) { |
| 20985 | const ok = (sampled_type.isRuntimeFloat() and sampled_type.bitSize(zcu) == 32) or |
| 20986 | (sampled_type.isInt(zcu) and (sampled_type.bitSize(zcu) == 32 or sampled_type.bitSize(zcu) == 64)); |
| 20987 | if (!ok) { |
| 20988 | return sema.fail( |
| 20989 | block, |
| 20990 | operand_src, |
| 20991 | "'{t}' field value must be a 32-bit int, 64-bit int or 32-bit float under the 'vulkan' os", |
| 20992 | .{usage_tag}, |
| 20993 | ); |
| 20994 | } |
| 20995 | |
| 20996 | if (format != .unknown) { |
| 20997 | const format_kind: enum { float, sint, uint } = switch (format) { |
| 20998 | .rgba32f, .rgba16f, .rgba8unorm, .rgba8snorm, .r32f => .float, |
| 20999 | .rgba32i, .rgba16i, .rgba8i, .r32i => .sint, |
| 21000 | .rgba32u, .rgba16u, .rgba8u, .r32u => .uint, |
| 21001 | .unknown => unreachable, |
| 21002 | }; |
| 21003 | const matches = switch (format_kind) { |
| 21004 | .float => sampled_type.isRuntimeFloat(), |
| 21005 | .sint => sampled_type.isInt(zcu) and sampled_type.intInfo(zcu).signedness == .signed, |
| 21006 | .uint => sampled_type.isInt(zcu) and sampled_type.intInfo(zcu).signedness == .unsigned, |
| 21007 | }; |
| 21008 | if (!matches) { |
| 21009 | return sema.fail( |
| 21010 | block, |
| 21011 | operand_src, |
| 21012 | "image 'format' '.{t}' does not match '{t}' type '{f}' under the 'vulkan' os", |
| 21013 | .{ format, usage_tag, sampled_type.fmt(pt) }, |
| 21014 | ); |
| 21015 | } |
| 21016 | } |
| 21017 | } |
| 21018 | |
| 21019 | break :blk sampled_type.toIntern(); |
| 21020 | }, |
| 21021 | .flags = .{ |
| 21022 | .tag = .image, |
| 21023 | .usage = usage_tag, |
| 21024 | .format = format, |
| 21025 | .dim = dim, |
| 21026 | .depth = depth, |
| 21027 | .access = access, |
| 21028 | .is_arrayed = arrayed, |
| 21029 | .is_multisampled = multisampled, |
| 21030 | }, |
| 21031 | }; |
| 21032 | }, |
| 21033 | .sampled_image => blk: { |
| 21034 | const image_ty = Value.fromInterned(union_val.val).toType(); |
| 21035 | if (image_ty.zigTypeTag(zcu) != .spirv or ip.loadSpirvType(image_ty.toIntern()).flags.tag != .image) { |
| 21036 | return sema.fail(block, operand_src, "'sampled_image' element must be an @SpirvType image, found '{f}'", .{image_ty.fmt(pt)}); |
| 21037 | } |
| 21038 | const image_info = ip.loadSpirvType(image_ty.toIntern()).flags; |
| 21039 | if (image_info.usage != .sampled) { |
| 21040 | return sema.fail(block, operand_src, "'sampled_image' element must be an image with 'usage = .sampled'", .{}); |
| 21041 | } |
| 21042 | break :blk .{ |
| 21043 | .ty = union_val.val, |
| 21044 | .flags = .{ |
| 21045 | .tag = tag, |
| 21046 | .usage = .unknown, |
| 21047 | .format = .unknown, |
| 21048 | .dim = .@"1d", |
| 21049 | .depth = .unknown, |
| 21050 | .access = .unknown, |
| 21051 | .is_arrayed = false, |
| 21052 | .is_multisampled = false, |
| 21053 | }, |
| 21054 | }; |
| 21055 | }, |
| 21056 | .runtime_array => blk: { |
| 21057 | const elem_ty = Value.fromInterned(union_val.val).toType(); |
| 21058 | if (elem_ty.toIntern() == .void_type) { |
| 21059 | return sema.fail(block, operand_src, "'runtime_array' element type must not be 'void'", .{}); |
| 21060 | } |
| 21061 | if (target.os.tag == .vulkan and |
| 21062 | elem_ty.zigTypeTag(zcu) == .spirv and |
| 21063 | ip.loadSpirvType(elem_ty.toIntern()).flags.tag == .runtime_array) |
| 21064 | { |
| 21065 | return sema.fail(block, operand_src, "'runtime_array' of 'runtime_array' is not allowed under the 'vulkan' os", .{}); |
| 21066 | } |
| 21067 | break :blk .{ |
| 21068 | .ty = union_val.val, |
| 21069 | .flags = .{ |
| 21070 | .tag = tag, |
| 21071 | .usage = .unknown, |
| 21072 | .format = .unknown, |
| 21073 | .dim = .@"1d", |
| 21074 | .depth = .unknown, |
| 21075 | .access = .unknown, |
| 21076 | .is_arrayed = false, |
| 21077 | .is_multisampled = false, |
| 21078 | }, |
| 21079 | }; |
| 21080 | }, |
| 21081 | }; |
| 21082 | |
| 21083 | return .fromIntern(try ip.getReifiedSpirvType(gpa, io, pt.tid, ip_data)); |
| 21084 | } |
| 21085 | |
| 21086 | fn resolveVaListRef(sema: *Sema, block: *Block, src: LazySrcLoc, zir_ref: Zir.Inst.Ref) CompileError!Air.Inst.Ref { |
| 21087 | const pt = sema.pt; |
| 21088 | const va_list_ty = try sema.getStdLangType(src, .VaList); |
| 21089 | const va_list_ptr = try pt.singleMutPtrType(va_list_ty); |
| 21090 | |
| 21091 | const inst = sema.resolveInst(zir_ref); |
| 21092 | return sema.coerce(block, va_list_ptr, inst, src); |
| 21093 | } |
| 21094 | |
| 21095 | fn zirCVaArg(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 21096 | const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; |
| 21097 | const src = block.nodeOffset(extra.node); |
| 21098 | const va_list_src = block.builtinCallArgSrc(extra.node, 0); |
| 21099 | const ty_src = block.builtinCallArgSrc(extra.node, 1); |
| 21100 | |
| 21101 | const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.lhs); |
| 21102 | const arg_ty = try sema.resolveType(block, ty_src, extra.rhs); |
| 21103 | try sema.ensureLayoutResolved(arg_ty, ty_src, .parameter); |
| 21104 | if (!arg_ty.validateExtern(.param_ty, sema.pt.zcu)) { |
| 21105 | const msg = msg: { |
| 21106 | const msg = try sema.errMsg(ty_src, "cannot get '{f}' from variadic argument", .{arg_ty.fmt(sema.pt)}); |
| 21107 | errdefer msg.destroy(sema.gpa); |
| 21108 | |
| 21109 | try sema.explainWhyTypeIsNotExtern(msg, ty_src, arg_ty, .param_ty); |
| 21110 | |
| 21111 | try sema.addDeclaredHereNote(msg, arg_ty); |
| 21112 | break :msg msg; |
| 21113 | }; |
| 21114 | return sema.failWithOwnedErrorMsg(block, msg); |
| 21115 | } |
| 21116 | |
| 21117 | try sema.requireRuntimeBlock(block, src, null); |
| 21118 | return block.addTyOp(.c_va_arg, arg_ty, va_list_ref); |
| 21119 | } |
| 21120 | |
| 21121 | fn zirCVaCopy(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 21122 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 21123 | const src = block.nodeOffset(extra.node); |
| 21124 | const va_list_src = block.builtinCallArgSrc(extra.node, 0); |
| 21125 | |
| 21126 | const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand); |
| 21127 | const va_list_ty = try sema.getStdLangType(src, .VaList); |
| 21128 | |
| 21129 | try sema.requireRuntimeBlock(block, src, null); |
| 21130 | return block.addTyOp(.c_va_copy, va_list_ty, va_list_ref); |
| 21131 | } |
| 21132 | |
| 21133 | fn zirCVaEnd(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 21134 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 21135 | const src = block.nodeOffset(extra.node); |
| 21136 | const va_list_src = block.builtinCallArgSrc(extra.node, 0); |
| 21137 | |
| 21138 | const va_list_ref = try sema.resolveVaListRef(block, va_list_src, extra.operand); |
| 21139 | |
| 21140 | try sema.requireRuntimeBlock(block, src, null); |
| 21141 | _ = try block.addUnOp(.c_va_end, va_list_ref); |
| 21142 | return .void_value; |
| 21143 | } |
| 21144 | |
| 21145 | fn zirCVaStart(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 21146 | const src_node: std.zig.Ast.Node.Offset = @fromBackingInt(@intCast(@as(i32, @bitCast(extended.operand)))); |
| 21147 | const src = block.nodeOffset(src_node); |
| 21148 | |
| 21149 | const va_list_ty = try sema.getStdLangType(src, .VaList); |
| 21150 | try sema.requireRuntimeBlock(block, src, null); |
| 21151 | return block.addInst(.{ |
| 21152 | .tag = .c_va_start, |
| 21153 | .data = .{ .ty = va_list_ty }, |
| 21154 | }); |
| 21155 | } |
| 21156 | |
| 21157 | fn zirTypeName(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 21158 | const pt = sema.pt; |
| 21159 | const zcu = pt.zcu; |
| 21160 | const comp = zcu.comp; |
| 21161 | const gpa = comp.gpa; |
| 21162 | const io = comp.io; |
| 21163 | const ip = &zcu.intern_pool; |
| 21164 | |
| 21165 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 21166 | const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 21167 | const ty = try sema.resolveType(block, ty_src, inst_data.operand); |
| 21168 | |
| 21169 | const type_name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}", .{ty.fmt(pt)}, .no_embedded_nulls); |
| 21170 | return sema.addNullTerminatedStrLit(type_name); |
| 21171 | } |
| 21172 | |
| 21173 | fn zirFrameType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 21174 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 21175 | const src = block.nodeOffset(inst_data.src_node); |
| 21176 | return sema.failWithUseOfAsync(block, src); |
| 21177 | } |
| 21178 | |
| 21179 | fn zirIntFromFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 21180 | const pt = sema.pt; |
| 21181 | const zcu = pt.zcu; |
| 21182 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 21183 | const src = block.nodeOffset(inst_data.src_node); |
| 21184 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 21185 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 21186 | const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@intFromFloat"); |
| 21187 | const operand = sema.resolveInst(extra.rhs); |
| 21188 | const operand_ty = sema.typeOf(operand); |
| 21189 | |
| 21190 | try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); |
| 21191 | const is_vector = dest_ty.zigTypeTag(zcu) == .vector; |
| 21192 | |
| 21193 | const dest_scalar_ty = dest_ty.scalarType(zcu); |
| 21194 | const operand_scalar_ty = operand_ty.scalarType(zcu); |
| 21195 | |
| 21196 | switch (dest_scalar_ty.zigTypeTag(zcu)) { |
| 21197 | .comptime_int, .int => {}, |
| 21198 | else => return sema.fail(block, src, "expected integer result type, found '{f}'", .{dest_scalar_ty.fmt(pt)}), |
| 21199 | } |
| 21200 | try sema.checkFloatType(block, operand_src, operand_scalar_ty); |
| 21201 | |
| 21202 | if (sema.resolveValue(operand)) |operand_val| { |
| 21203 | const result_val = try sema.intFromFloat(block, operand_src, operand_val, operand_ty, dest_ty, .truncate); |
| 21204 | return Air.internedToRef(result_val.toIntern()); |
| 21205 | } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) { |
| 21206 | return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_int }); |
| 21207 | } |
| 21208 | |
| 21209 | try sema.requireRuntimeBlock(block, src, operand_src); |
| 21210 | if (dest_scalar_ty.toIntern() == .u0_type) { |
| 21211 | if (block.wantSafety()) { |
| 21212 | // Emit an explicit safety check. We can do this one like `abs(x) < 1`. |
| 21213 | const abs_ref = try block.addTyOp(.abs, operand_ty, operand); |
| 21214 | const max_abs_ref = if (is_vector) try block.addReduce(abs_ref, .Max) else abs_ref; |
| 21215 | const one_ref = Air.internedToRef((try pt.floatValue(operand_scalar_ty, 1.0)).toIntern()); |
| 21216 | const ok_ref = try block.addBinOp(.cmp_lt, max_abs_ref, one_ref); |
| 21217 | try sema.addSafetyCheck(block, src, ok_ref, .integer_part_out_of_bounds); |
| 21218 | } |
| 21219 | const scalar_val = try pt.intValue(dest_scalar_ty, 0); |
| 21220 | return Air.internedToRef((try sema.splat(dest_ty, scalar_val)).toIntern()); |
| 21221 | } |
| 21222 | if (block.wantSafety()) { |
| 21223 | try sema.preparePanicId(src, .integer_part_out_of_bounds); |
| 21224 | return block.addTyOp(switch (block.float_mode) { |
| 21225 | .optimized => .int_from_float_optimized_safe, |
| 21226 | .strict => .int_from_float_safe, |
| 21227 | }, dest_ty, operand); |
| 21228 | } |
| 21229 | return block.addTyOp(switch (block.float_mode) { |
| 21230 | .optimized => .int_from_float_optimized, |
| 21231 | .strict => .int_from_float, |
| 21232 | }, dest_ty, operand); |
| 21233 | } |
| 21234 | |
| 21235 | fn zirRoundCast( |
| 21236 | sema: *Sema, |
| 21237 | block: *Block, |
| 21238 | extended: Zir.Inst.Extended.InstData, |
| 21239 | ) CompileError!Air.Inst.Ref { |
| 21240 | const pt = sema.pt; |
| 21241 | const zcu = pt.zcu; |
| 21242 | const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; |
| 21243 | const src = block.nodeOffset(extra.node); |
| 21244 | const operand_src = block.builtinCallArgSrc(extra.node, 0); |
| 21245 | |
| 21246 | const operand = sema.resolveInst(extra.rhs); |
| 21247 | |
| 21248 | const round_op: Zir.Inst.RoundOp = @fromBackingInt(@intCast(extended.small)); |
| 21249 | const mode: IntFromFloatMode = switch (round_op) { |
| 21250 | .round => .round, |
| 21251 | .floor => .floor, |
| 21252 | .ceil => .ceil, |
| 21253 | .trunc => .truncate, |
| 21254 | }; |
| 21255 | |
| 21256 | const dest_ty = (try sema.resolveTypeOrPoison(block, src, extra.lhs) orelse switch (mode) { |
| 21257 | // zig fmt: off |
| 21258 | .round => return sema.unaryMath(block, operand_src, operand, .round, Value.round), |
| 21259 | .floor => return sema.unaryMath(block, operand_src, operand, .floor, Value.floor), |
| 21260 | .ceil => return sema.unaryMath(block, operand_src, operand, .ceil, Value.ceil), |
| 21261 | .truncate => return sema.unaryMath(block, operand_src, operand, .trunc_float, Value.trunc), |
| 21262 | // zig fmt: on |
| 21263 | .exact => unreachable, |
| 21264 | }).optEuBaseType(zcu); |
| 21265 | |
| 21266 | const operand_ty = sema.typeOf(operand); |
| 21267 | |
| 21268 | try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); |
| 21269 | |
| 21270 | const dest_scalar_ty = dest_ty.scalarType(zcu); |
| 21271 | const operand_scalar_ty = operand_ty.scalarType(zcu); |
| 21272 | |
| 21273 | switch (operand_scalar_ty.zigTypeTag(zcu)) { |
| 21274 | .comptime_float, .float => {}, |
| 21275 | else => return sema.fail( |
| 21276 | block, |
| 21277 | operand_src, |
| 21278 | "expected float or vector type, found '{f}'", |
| 21279 | .{operand_ty.fmt(pt)}, |
| 21280 | ), |
| 21281 | } |
| 21282 | |
| 21283 | switch (dest_scalar_ty.zigTypeTag(zcu)) { |
| 21284 | .float, .comptime_float => { |
| 21285 | const coerced_operand = try sema.coerce(block, dest_ty, operand, operand_src); |
| 21286 | |
| 21287 | const result_ref = switch (mode) { |
| 21288 | .round => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.round), |
| 21289 | .floor => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.floor), |
| 21290 | .ceil => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.ceil), |
| 21291 | .truncate => try sema.maybeConstantUnaryMath(coerced_operand, dest_ty, Value.trunc), |
| 21292 | .exact => unreachable, |
| 21293 | }; |
| 21294 | |
| 21295 | if (result_ref) |ref| return ref; |
| 21296 | |
| 21297 | const air_tag: Air.Inst.Tag = switch (mode) { |
| 21298 | .round => .round, |
| 21299 | .floor => .floor, |
| 21300 | .ceil => .ceil, |
| 21301 | .truncate => .trunc_float, |
| 21302 | .exact => unreachable, |
| 21303 | }; |
| 21304 | |
| 21305 | try sema.requireRuntimeBlock(block, operand_src, null); |
| 21306 | return block.addUnOp(air_tag, coerced_operand); |
| 21307 | }, |
| 21308 | .int, .comptime_int => {}, |
| 21309 | else => return sema.fail( |
| 21310 | block, |
| 21311 | src, |
| 21312 | "expected integer, float, or vector of either integers or floats, found '{f}'", |
| 21313 | .{dest_ty.fmt(pt)}, |
| 21314 | ), |
| 21315 | } |
| 21316 | |
| 21317 | if (sema.resolveValue(operand)) |operand_val| { |
| 21318 | const result_val = try sema.intFromFloat(block, operand_src, operand_val, operand_ty, dest_ty, mode); |
| 21319 | return .fromValue(result_val); |
| 21320 | } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) { |
| 21321 | return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_int }); |
| 21322 | } |
| 21323 | |
| 21324 | try sema.requireRuntimeBlock(block, src, operand_src); |
| 21325 | |
| 21326 | if (dest_scalar_ty.toIntern() == .u0_type) { |
| 21327 | if (block.wantSafety()) { |
| 21328 | const abs_ref = try block.addTyOp(.abs, operand_ty, operand); |
| 21329 | const is_vector = dest_ty.zigTypeTag(zcu) == .vector; |
| 21330 | const max_abs_ref = if (is_vector) try block.addReduce(abs_ref, .Max) else abs_ref; |
| 21331 | const one_ref = Air.internedToRef((try pt.floatValue(operand_scalar_ty, 1.0)).toIntern()); |
| 21332 | const ok_ref = try block.addBinOp(.cmp_lt, max_abs_ref, one_ref); |
| 21333 | try sema.addSafetyCheck(block, src, ok_ref, .integer_part_out_of_bounds); |
| 21334 | } |
| 21335 | const scalar_val = try pt.intValue(dest_scalar_ty, 0); |
| 21336 | return Air.internedToRef((try sema.splat(dest_ty, scalar_val)).toIntern()); |
| 21337 | } |
| 21338 | |
| 21339 | const safe = block.wantSafety(); |
| 21340 | |
| 21341 | if (safe) { |
| 21342 | try sema.preparePanicId(src, .integer_part_out_of_bounds); |
| 21343 | } |
| 21344 | |
| 21345 | const uncasted_result: Air.Inst.Ref = switch (mode) { |
| 21346 | .truncate => operand, |
| 21347 | .round => try block.addUnOp(.round, operand), |
| 21348 | .floor => try block.addUnOp(.floor, operand), |
| 21349 | .ceil => try block.addUnOp(.ceil, operand), |
| 21350 | .exact => unreachable, |
| 21351 | }; |
| 21352 | const air_cast_tag: Air.Inst.Tag = switch (block.float_mode) { |
| 21353 | .optimized => if (safe) .int_from_float_optimized_safe else .int_from_float_optimized, |
| 21354 | .strict => if (safe) .int_from_float_safe else .int_from_float, |
| 21355 | }; |
| 21356 | return block.addTyOp(air_cast_tag, dest_ty, uncasted_result); |
| 21357 | } |
| 21358 | |
| 21359 | fn zirFloatFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 21360 | const pt = sema.pt; |
| 21361 | const zcu = pt.zcu; |
| 21362 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 21363 | const src = block.nodeOffset(inst_data.src_node); |
| 21364 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 21365 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 21366 | const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@floatFromInt"); |
| 21367 | const operand = sema.resolveInst(extra.rhs); |
| 21368 | const operand_ty = sema.typeOf(operand); |
| 21369 | |
| 21370 | try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, operand_ty, src, operand_src); |
| 21371 | |
| 21372 | const dest_scalar_ty = dest_ty.scalarType(zcu); |
| 21373 | const operand_scalar_ty = operand_ty.scalarType(zcu); |
| 21374 | |
| 21375 | switch (dest_scalar_ty.zigTypeTag(zcu)) { |
| 21376 | .comptime_float, .float => {}, |
| 21377 | else => return sema.fail(block, src, "expected float result type, found '{f}'", .{dest_scalar_ty.fmt(pt)}), |
| 21378 | } |
| 21379 | _ = try sema.checkIntType(block, operand_src, operand_scalar_ty); |
| 21380 | |
| 21381 | if (sema.resolveValue(operand)) |operand_val| { |
| 21382 | if (operand_val.isUndef(zcu)) return .fromValue(try pt.undefValue(dest_ty)); |
| 21383 | if (dest_ty.zigTypeTag(zcu) != .vector) { |
| 21384 | return .fromValue(try pt.floatValue(dest_ty, operand_val.toFloat(f128, zcu))); |
| 21385 | } |
| 21386 | const dest_elems = try sema.arena.alloc(InternPool.Index, dest_ty.vectorLen(zcu)); |
| 21387 | for (dest_elems, 0..) |*out_elem, elem_idx| { |
| 21388 | const orig_elem = try operand_val.elemValue(pt, elem_idx); |
| 21389 | const casted_elem = if (orig_elem.isUndef(zcu)) |
| 21390 | try pt.undefValue(dest_scalar_ty) |
| 21391 | else |
| 21392 | try pt.floatValue(dest_scalar_ty, orig_elem.toFloat(f128, zcu)); |
| 21393 | out_elem.* = casted_elem.toIntern(); |
| 21394 | } |
| 21395 | return .fromValue(try pt.aggregateValue(dest_ty, dest_elems)); |
| 21396 | } else if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_float) { |
| 21397 | return sema.failWithNeededComptime(block, operand_src, .{ .simple = .casted_to_comptime_float }); |
| 21398 | } |
| 21399 | |
| 21400 | try sema.requireRuntimeBlock(block, src, operand_src); |
| 21401 | return block.addTyOp(.float_from_int, dest_ty, operand); |
| 21402 | } |
| 21403 | |
| 21404 | fn zirPtrFromInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 21405 | const pt = sema.pt; |
| 21406 | const zcu = pt.zcu; |
| 21407 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 21408 | const src = block.nodeOffset(inst_data.src_node); |
| 21409 | |
| 21410 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 21411 | |
| 21412 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 21413 | const operand_res = sema.resolveInst(extra.rhs); |
| 21414 | |
| 21415 | const uncoerced_operand_ty = sema.typeOf(operand_res); |
| 21416 | const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrFromInt"); |
| 21417 | try sema.checkVectorizableBinaryOperands(block, operand_src, dest_ty, uncoerced_operand_ty, src, operand_src); |
| 21418 | |
| 21419 | const is_vector = dest_ty.zigTypeTag(zcu) == .vector; |
| 21420 | const operand_ty: Type = if (is_vector) operand_ty: { |
| 21421 | const len = dest_ty.vectorLen(zcu); |
| 21422 | break :operand_ty try pt.vectorType(.{ .child = .usize_type, .len = len }); |
| 21423 | } else .usize; |
| 21424 | |
| 21425 | const operand_coerced = try sema.coerce(block, operand_ty, operand_res, operand_src); |
| 21426 | |
| 21427 | const ptr_ty = dest_ty.scalarType(zcu); |
| 21428 | try sema.checkPtrType(block, src, ptr_ty, true); |
| 21429 | |
| 21430 | const elem_ty = ptr_ty.nullablePtrElem(zcu); |
| 21431 | |
| 21432 | try sema.ensureLayoutResolved(elem_ty, src, .align_check); |
| 21433 | const ptr_align = ptr_ty.ptrAlignment(zcu); |
| 21434 | |
| 21435 | if (ptr_ty.isSlice(zcu)) { |
| 21436 | const msg = msg: { |
| 21437 | const msg = try sema.errMsg(src, "integer cannot be converted to slice type '{f}'", .{ptr_ty.fmt(pt)}); |
| 21438 | errdefer msg.destroy(sema.gpa); |
| 21439 | try sema.errNote(src, msg, "slice length cannot be inferred from address", .{}); |
| 21440 | break :msg msg; |
| 21441 | }; |
| 21442 | return sema.failWithOwnedErrorMsg(block, msg); |
| 21443 | } |
| 21444 | |
| 21445 | if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| { |
| 21446 | if (!is_vector) { |
| 21447 | const ptr_val = try sema.ptrFromIntVal(block, operand_src, val, ptr_ty, ptr_align, null); |
| 21448 | return Air.internedToRef(ptr_val.toIntern()); |
| 21449 | } |
| 21450 | const len = dest_ty.vectorLen(zcu); |
| 21451 | const new_elems = try sema.arena.alloc(InternPool.Index, len); |
| 21452 | for (new_elems, 0..) |*new_elem, elem_idx| { |
| 21453 | const elem = try val.elemValue(pt, elem_idx); |
| 21454 | const ptr_val = try sema.ptrFromIntVal(block, operand_src, elem, ptr_ty, ptr_align, elem_idx); |
| 21455 | new_elem.* = ptr_val.toIntern(); |
| 21456 | } |
| 21457 | return Air.internedToRef((try pt.aggregateValue(dest_ty, new_elems)).toIntern()); |
| 21458 | } |
| 21459 | try sema.requireRuntimeBlock(block, src, operand_src); |
| 21460 | try sema.checkLogicalPtrOperation(block, src, ptr_ty); |
| 21461 | if (block.wantSafety()) { |
| 21462 | if (!ptr_ty.isAllowzeroPtr(zcu)) { |
| 21463 | const is_non_zero = if (is_vector) all_non_zero: { |
| 21464 | const zero_usize = Air.internedToRef((try sema.splat(operand_ty, .zero_usize)).toIntern()); |
| 21465 | const is_non_zero = try block.addCmpVector(operand_coerced, zero_usize, .neq); |
| 21466 | break :all_non_zero try block.addReduce(is_non_zero, .And); |
| 21467 | } else try block.addBinOp(.cmp_neq, operand_coerced, .zero_usize); |
| 21468 | try sema.addSafetyCheck(block, src, is_non_zero, .cast_to_null); |
| 21469 | } |
| 21470 | if (ptr_align.compare(.gt, .@"1")) { |
| 21471 | const align_bytes_minus_1 = ptr_align.toByteUnits().? - 1; |
| 21472 | const align_mask = Air.internedToRef((try sema.splat(operand_ty, try pt.intValue( |
| 21473 | .usize, |
| 21474 | if (elem_ty.fnPtrMaskOrNull(zcu)) |mask| |
| 21475 | align_bytes_minus_1 & mask |
| 21476 | else |
| 21477 | align_bytes_minus_1, |
| 21478 | ))).toIntern()); |
| 21479 | const remainder = try block.addBinOp(.bit_and, operand_coerced, align_mask); |
| 21480 | const is_aligned = if (is_vector) all_aligned: { |
| 21481 | const splat_zero_usize = Air.internedToRef((try sema.splat(operand_ty, .zero_usize)).toIntern()); |
| 21482 | const is_aligned = try block.addCmpVector(remainder, splat_zero_usize, .eq); |
| 21483 | break :all_aligned try block.addReduce(is_aligned, .And); |
| 21484 | } else try block.addBinOp(.cmp_eq, remainder, .zero_usize); |
| 21485 | try sema.addSafetyCheck(block, src, is_aligned, .incorrect_alignment); |
| 21486 | } |
| 21487 | } |
| 21488 | return block.addTyOp(.ptr_from_int, dest_ty, operand_coerced); |
| 21489 | } |
| 21490 | |
| 21491 | fn ptrFromIntVal( |
| 21492 | sema: *Sema, |
| 21493 | block: *Block, |
| 21494 | operand_src: LazySrcLoc, |
| 21495 | operand_val: Value, |
| 21496 | ptr_ty: Type, |
| 21497 | ptr_align: Alignment, |
| 21498 | vec_idx: ?usize, |
| 21499 | ) !Value { |
| 21500 | const pt = sema.pt; |
| 21501 | const zcu = pt.zcu; |
| 21502 | if (operand_val.isUndef(zcu)) { |
| 21503 | if (ptr_ty.isAllowzeroPtr(zcu) and ptr_align == .@"1") { |
| 21504 | return pt.undefValue(ptr_ty); |
| 21505 | } |
| 21506 | return sema.failWithUseOfUndef(block, operand_src, vec_idx); |
| 21507 | } |
| 21508 | const addr = operand_val.toUnsignedInt(zcu); |
| 21509 | if (!ptr_ty.isAllowzeroPtr(zcu) and addr == 0) |
| 21510 | return sema.fail(block, operand_src, "pointer type '{f}' does not allow address zero", .{ptr_ty.fmt(pt)}); |
| 21511 | if (addr != 0 and ptr_align != .none) { |
| 21512 | const masked_addr = if (ptr_ty.childType(zcu).fnPtrMaskOrNull(zcu)) |mask| |
| 21513 | addr & mask |
| 21514 | else |
| 21515 | addr; |
| 21516 | |
| 21517 | if (!ptr_align.check(masked_addr)) { |
| 21518 | return sema.fail(block, operand_src, "pointer type '{f}' requires aligned address", .{ptr_ty.fmt(pt)}); |
| 21519 | } |
| 21520 | } |
| 21521 | |
| 21522 | return switch (ptr_ty.zigTypeTag(zcu)) { |
| 21523 | .optional => Value.fromInterned(try pt.intern(.{ .opt = .{ |
| 21524 | .ty = ptr_ty.toIntern(), |
| 21525 | .val = if (addr == 0) .none else (try pt.ptrIntValue(ptr_ty.childType(zcu), addr)).toIntern(), |
| 21526 | } })), |
| 21527 | .pointer => try pt.ptrIntValue(ptr_ty, addr), |
| 21528 | else => unreachable, |
| 21529 | }; |
| 21530 | } |
| 21531 | |
| 21532 | fn zirErrorCast(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 21533 | const pt = sema.pt; |
| 21534 | const zcu = pt.zcu; |
| 21535 | const ip = &zcu.intern_pool; |
| 21536 | const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; |
| 21537 | const src = block.nodeOffset(extra.node); |
| 21538 | const operand_src = block.builtinCallArgSrc(extra.node, 0); |
| 21539 | const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_opt, "@errorCast"); |
| 21540 | const operand = sema.resolveInst(extra.rhs); |
| 21541 | const operand_ty = sema.typeOf(operand); |
| 21542 | |
| 21543 | const dest_tag = dest_ty.zigTypeTag(zcu); |
| 21544 | const operand_tag = operand_ty.zigTypeTag(zcu); |
| 21545 | |
| 21546 | if (dest_tag != .error_set and dest_tag != .error_union) { |
| 21547 | return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(dest_tag)}); |
| 21548 | } |
| 21549 | if (operand_tag != .error_set and operand_tag != .error_union) { |
| 21550 | return sema.fail(block, src, "expected error set or error union type, found '{s}'", .{@tagName(operand_tag)}); |
| 21551 | } |
| 21552 | if (dest_tag == .error_set and operand_tag == .error_union) { |
| 21553 | return sema.fail(block, src, "cannot cast an error union type to error set", .{}); |
| 21554 | } |
| 21555 | if (dest_tag == .error_union and operand_tag == .error_union and |
| 21556 | dest_ty.errorUnionPayload(zcu).toIntern() != operand_ty.errorUnionPayload(zcu).toIntern()) |
| 21557 | { |
| 21558 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 21559 | const msg = try sema.errMsg(src, "payload types of error unions must match", .{}); |
| 21560 | errdefer msg.destroy(sema.gpa); |
| 21561 | const dest_payload_ty = dest_ty.errorUnionPayload(zcu); |
| 21562 | const operand_payload_ty = operand_ty.errorUnionPayload(zcu); |
| 21563 | try sema.errNote(src, msg, "destination payload is '{f}'", .{dest_payload_ty.fmt(pt)}); |
| 21564 | try sema.errNote(src, msg, "operand payload is '{f}'", .{operand_payload_ty.fmt(pt)}); |
| 21565 | try addDeclaredHereNote(sema, msg, dest_ty); |
| 21566 | try addDeclaredHereNote(sema, msg, operand_ty); |
| 21567 | break :msg msg; |
| 21568 | }); |
| 21569 | } |
| 21570 | const dest_err_ty = switch (dest_tag) { |
| 21571 | .error_union => dest_ty.errorUnionSet(zcu), |
| 21572 | .error_set => dest_ty, |
| 21573 | else => unreachable, |
| 21574 | }; |
| 21575 | const operand_err_ty = switch (operand_tag) { |
| 21576 | .error_union => operand_ty.errorUnionSet(zcu), |
| 21577 | .error_set => operand_ty, |
| 21578 | else => unreachable, |
| 21579 | }; |
| 21580 | |
| 21581 | switch (ip.indexToKey(operand_err_ty.toIntern())) { |
| 21582 | .inferred_error_set_type => |func| try sema.ensureFuncIesResolved(block, src, func), |
| 21583 | else => {}, |
| 21584 | } |
| 21585 | |
| 21586 | const result: enum { |
| 21587 | /// The operand and destination error sets are disjoint, i.e. have no errors in common. |
| 21588 | disjoint, |
| 21589 | /// The destination error set is a superset of the operand error set, so the operation is |
| 21590 | /// effectively equivalent to a coercion. |
| 21591 | superset, |
| 21592 | /// The operand and destination error sets have *some* errors in common, but the destination |
| 21593 | /// is not a superset of the operand, so a safety check may be needed. |
| 21594 | overlap, |
| 21595 | } = if (operand_err_ty.errorSetIsEmpty(zcu)) res: { |
| 21596 | break :res .disjoint; |
| 21597 | } else check: switch (dest_err_ty.toIntern()) { |
| 21598 | .anyerror_type => .superset, |
| 21599 | .adhoc_inferred_error_set_type => { |
| 21600 | // `@errorCast` to this function's own error set. |
| 21601 | try sema.fn_ret_ty_ies.?.addErrorSet(operand_err_ty, ip, sema.arena); |
| 21602 | break :check .superset; |
| 21603 | }, |
| 21604 | else => |err_set_ty| switch (ip.indexToKey(err_set_ty)) { |
| 21605 | .inferred_error_set_type => |func_index| { |
| 21606 | if (sema.fn_ret_ty_ies) |dst_ies| { |
| 21607 | if (dst_ies.func == func_index) { |
| 21608 | // `@errorCast` to this function's own error set. |
| 21609 | try sema.fn_ret_ty_ies.?.addErrorSet(operand_err_ty, ip, sema.arena); |
| 21610 | break :check .superset; |
| 21611 | } |
| 21612 | } |
| 21613 | try sema.ensureFuncIesResolved(block, src, func_index); |
| 21614 | continue :check ip.funcIesResolvedUnordered(func_index); |
| 21615 | }, |
| 21616 | .error_set_type => |dest| { |
| 21617 | if (dest.names.len == 0) break :check .disjoint; // dest is 'error{}' |
| 21618 | if (operand_err_ty.isAnyError(zcu)) break :check .overlap; // anyerror -> error{...} (non-empty) |
| 21619 | var dest_has_all = true; |
| 21620 | var dest_has_any = false; |
| 21621 | for (operand_err_ty.errorSetNames(zcu).get(ip)) |operand_err_name| { |
| 21622 | if (dest.nameIndex(ip, operand_err_name) != null) { |
| 21623 | dest_has_any = true; |
| 21624 | } else { |
| 21625 | dest_has_all = false; |
| 21626 | } |
| 21627 | } |
| 21628 | if (!dest_has_any) break :check .disjoint; |
| 21629 | if (dest_has_all) break :check .superset; |
| 21630 | break :check .overlap; |
| 21631 | }, |
| 21632 | else => unreachable, |
| 21633 | }, |
| 21634 | }; |
| 21635 | |
| 21636 | if (result == .disjoint and !(operand_tag == .error_union and dest_tag == .error_union)) { |
| 21637 | return sema.fail(block, src, "error sets '{f}' and '{f}' have no common errors", .{ |
| 21638 | operand_err_ty.fmt(pt), dest_err_ty.fmt(pt), |
| 21639 | }); |
| 21640 | } |
| 21641 | |
| 21642 | // operand must be defined since it can be an invalid error value |
| 21643 | if (try sema.resolveDefinedValue(block, operand_src, operand)) |operand_val| { |
| 21644 | const err_name: InternPool.NullTerminatedString = switch (ip.indexToKey(operand_val.toIntern())) { |
| 21645 | .err => |err| err.name, |
| 21646 | .error_union => |eu| switch (eu.val) { |
| 21647 | .err_name => |name| name, |
| 21648 | .payload => |payload_val| { |
| 21649 | assert(dest_tag == .error_union); // should be guaranteed from the type checks above |
| 21650 | const dest_payload_ty = dest_ty.errorUnionPayload(zcu); |
| 21651 | const coerced_payload = try sema.coerce(block, dest_payload_ty, .fromIntern(payload_val), operand_src); |
| 21652 | return sema.wrapErrorUnionPayload(block, dest_ty, coerced_payload, operand_src) catch |err| switch (err) { |
| 21653 | error.NotCoercible => unreachable, |
| 21654 | else => |e| return e, |
| 21655 | }; |
| 21656 | }, |
| 21657 | }, |
| 21658 | else => unreachable, |
| 21659 | }; |
| 21660 | |
| 21661 | if (result != .superset and !dest_err_ty.errorSetHasField(err_name, zcu)) { |
| 21662 | return sema.fail(block, src, "'error.{f}' not a member of error set '{f}'", .{ |
| 21663 | err_name.fmt(ip), dest_err_ty.fmt(pt), |
| 21664 | }); |
| 21665 | } |
| 21666 | |
| 21667 | return .fromIntern(try pt.intern(switch (dest_tag) { |
| 21668 | .error_set => .{ .err = .{ |
| 21669 | .ty = dest_ty.toIntern(), |
| 21670 | .name = err_name, |
| 21671 | } }, |
| 21672 | .error_union => .{ .error_union = .{ |
| 21673 | .ty = dest_ty.toIntern(), |
| 21674 | .val = .{ .err_name = err_name }, |
| 21675 | } }, |
| 21676 | else => unreachable, |
| 21677 | })); |
| 21678 | } |
| 21679 | |
| 21680 | const err_int_ty = try pt.errorIntType(); |
| 21681 | if (block.wantSafety() and result != .superset and zcu.backendSupportsFeature(.error_set_has_value)) { |
| 21682 | const err_code_inst = switch (operand_tag) { |
| 21683 | .error_set => operand, |
| 21684 | .error_union => try block.addTyOp(.unwrap_errunion_err, operand_err_ty, operand), |
| 21685 | else => unreachable, |
| 21686 | }; |
| 21687 | const err_int_inst = try block.addTyOp(.int_from_error, err_int_ty, err_code_inst); |
| 21688 | if (dest_tag == .error_union) { |
| 21689 | const zero_err = try pt.intRef(err_int_ty, 0); |
| 21690 | const is_zero = try block.addBinOp(.cmp_eq, err_int_inst, zero_err); |
| 21691 | if (result == .disjoint) { |
| 21692 | // Error must be zero. |
| 21693 | try sema.addSafetyCheckCall(block, src, is_zero, .@"panic.unexpectedErrorCode", &.{err_code_inst}); |
| 21694 | } else { |
| 21695 | // Error must be in destination set or zero. |
| 21696 | const has_value = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst); |
| 21697 | const ok = try block.addBinOp(.bit_or, has_value, is_zero); |
| 21698 | try sema.addSafetyCheckCall(block, src, ok, .@"panic.unexpectedErrorCode", &.{err_code_inst}); |
| 21699 | } |
| 21700 | } else { |
| 21701 | const ok = try block.addTyOp(.error_set_has_value, dest_err_ty, err_int_inst); |
| 21702 | try sema.addSafetyCheckCall(block, src, ok, .@"panic.unexpectedErrorCode", &.{err_code_inst}); |
| 21703 | } |
| 21704 | } |
| 21705 | |
| 21706 | if (operand_tag == .error_set and dest_tag == .error_union) { |
| 21707 | const err_val = try block.addTyOp(.error_cast, dest_err_ty, operand); |
| 21708 | return block.addTyOp(.wrap_errunion_err, dest_ty, err_val); |
| 21709 | } else { |
| 21710 | return block.addTyOp(.error_cast, dest_ty, operand); |
| 21711 | } |
| 21712 | } |
| 21713 | |
| 21714 | fn zirPtrCastFull(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 21715 | const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?; |
| 21716 | const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(FlagsInt, @truncate(extended.small))); |
| 21717 | const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; |
| 21718 | const src = block.nodeOffset(extra.node); |
| 21719 | const operand_src = block.src(.{ .node_offset_ptrcast_operand = extra.node }); |
| 21720 | const operand = sema.resolveInst(extra.rhs); |
| 21721 | const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, flags.needResultTypeBuiltinName()); |
| 21722 | return sema.ptrCastFull( |
| 21723 | block, |
| 21724 | flags, |
| 21725 | src, |
| 21726 | operand, |
| 21727 | operand_src, |
| 21728 | dest_ty, |
| 21729 | flags.needResultTypeBuiltinName(), |
| 21730 | ); |
| 21731 | } |
| 21732 | |
| 21733 | fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 21734 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 21735 | const src = block.nodeOffset(inst_data.src_node); |
| 21736 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 21737 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 21738 | const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu, "@ptrCast"); |
| 21739 | const operand = sema.resolveInst(extra.rhs); |
| 21740 | |
| 21741 | return sema.ptrCastFull( |
| 21742 | block, |
| 21743 | .{ .ptr_cast = true }, |
| 21744 | src, |
| 21745 | operand, |
| 21746 | operand_src, |
| 21747 | dest_ty, |
| 21748 | "@ptrCast", |
| 21749 | ); |
| 21750 | } |
| 21751 | |
| 21752 | fn ptrCastFull( |
| 21753 | sema: *Sema, |
| 21754 | block: *Block, |
| 21755 | flags: Zir.Inst.FullPtrCastFlags, |
| 21756 | src: LazySrcLoc, |
| 21757 | operand: Air.Inst.Ref, |
| 21758 | operand_src: LazySrcLoc, |
| 21759 | dest_ty: Type, |
| 21760 | operation: []const u8, |
| 21761 | ) CompileError!Air.Inst.Ref { |
| 21762 | const pt = sema.pt; |
| 21763 | const zcu = pt.zcu; |
| 21764 | const comp = zcu.comp; |
| 21765 | const gpa = comp.gpa; |
| 21766 | const io = comp.io; |
| 21767 | |
| 21768 | const operand_ty = sema.typeOf(operand); |
| 21769 | |
| 21770 | try sema.checkPtrType(block, src, dest_ty, true); |
| 21771 | try sema.checkPtrOperand(block, operand_src, operand_ty); |
| 21772 | |
| 21773 | const src_info = operand_ty.ptrInfo(zcu); |
| 21774 | const dest_info = dest_ty.ptrInfo(zcu); |
| 21775 | |
| 21776 | try sema.ensureLayoutResolved(.fromInterned(src_info.child), operand_src, .align_check); |
| 21777 | try sema.ensureLayoutResolved(.fromInterned(dest_info.child), src, .align_check); |
| 21778 | |
| 21779 | const DestSliceLen = union(enum) { |
| 21780 | undef, |
| 21781 | constant: u64, |
| 21782 | equal_runtime_src_slice, |
| 21783 | change_runtime_src_slice: struct { |
| 21784 | bytes_per_src: u64, |
| 21785 | bytes_per_dest: u64, |
| 21786 | }, |
| 21787 | }; |
| 21788 | // Populated iff the destination type is a slice. |
| 21789 | const dest_slice_len: ?DestSliceLen = len: { |
| 21790 | switch (dest_info.flags.size) { |
| 21791 | .slice => {}, |
| 21792 | .many, .c, .one => break :len null, |
| 21793 | } |
| 21794 | // A `null` length means the operand is a runtime-known slice (so the length is runtime-known). |
| 21795 | // `src_elem_type` is different from `src_info.child` if the latter is an array, to ensure we ignore sentinels. |
| 21796 | const src_elem_ty: Type, const opt_src_len: ?u64 = switch (src_info.flags.size) { |
| 21797 | .one => src: { |
| 21798 | const true_child: Type = .fromInterned(src_info.child); |
| 21799 | break :src switch (true_child.zigTypeTag(zcu)) { |
| 21800 | .array => .{ true_child.childType(zcu), true_child.arrayLen(zcu) }, |
| 21801 | else => .{ true_child, 1 }, |
| 21802 | }; |
| 21803 | }, |
| 21804 | .slice => src: { |
| 21805 | const operand_val = sema.resolveValue(operand) orelse break :src .{ .fromInterned(src_info.child), null }; |
| 21806 | if (operand_val.isUndef(zcu)) break :len .undef; |
| 21807 | const slice_val = switch (operand_ty.zigTypeTag(zcu)) { |
| 21808 | .optional => operand_val.optionalValue(zcu) orelse break :len .undef, |
| 21809 | .pointer => operand_val, |
| 21810 | else => unreachable, |
| 21811 | }; |
| 21812 | const slice_len: Value = .fromInterned(zcu.intern_pool.sliceLen(slice_val.toIntern())); |
| 21813 | if (slice_len.isUndef(zcu)) break :len .undef; |
| 21814 | break :src .{ .fromInterned(src_info.child), slice_len.toUnsignedInt(zcu) }; |
| 21815 | }, |
| 21816 | .many, .c => { |
| 21817 | return sema.fail(block, src, "cannot infer length of slice from {s}", .{pointerSizeString(src_info.flags.size)}); |
| 21818 | }, |
| 21819 | }; |
| 21820 | const dest_elem_ty: Type = .fromInterned(dest_info.child); |
| 21821 | if (dest_elem_ty.toIntern() == src_elem_ty.toIntern()) { |
| 21822 | break :len if (opt_src_len) |l| .{ .constant = l } else .equal_runtime_src_slice; |
| 21823 | } |
| 21824 | if (!src_elem_ty.comptimeOnly(zcu) and !dest_elem_ty.comptimeOnly(zcu)) { |
| 21825 | if (src_elem_ty.zigTypeTag(zcu) == .@"opaque") { |
| 21826 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 21827 | const msg = try sema.errMsg(src, "cannot infer length of slice of '{f}' from pointer to opaque type '{f}' with unknown size", .{ |
| 21828 | dest_elem_ty.fmt(pt), src_elem_ty.fmt(pt), |
| 21829 | }); |
| 21830 | errdefer msg.destroy(gpa); |
| 21831 | try sema.addDeclaredHereNote(msg, src_elem_ty); |
| 21832 | break :msg msg; |
| 21833 | }); |
| 21834 | } |
| 21835 | const src_elem_size = src_elem_ty.abiSize(zcu); |
| 21836 | const dest_elem_size = dest_elem_ty.abiSize(zcu); |
| 21837 | if (dest_elem_size == 0) { |
| 21838 | return sema.fail(block, src, "cannot infer length of slice of zero-bit '{f}' from '{f}'", .{ |
| 21839 | dest_elem_ty.fmt(pt), operand_ty.fmt(pt), |
| 21840 | }); |
| 21841 | } |
| 21842 | if (opt_src_len) |src_len| { |
| 21843 | const bytes = src_len * src_elem_size; |
| 21844 | const dest_len = std.math.divExact(u64, bytes, dest_elem_size) catch switch (src_info.flags.size) { |
| 21845 | .slice => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}), |
| 21846 | .one => return sema.fail(block, src, "type '{f}' does not divide exactly into destination elements", .{Type.fromInterned(src_info.child).fmt(pt)}), |
| 21847 | else => unreachable, |
| 21848 | }; |
| 21849 | break :len .{ .constant = dest_len }; |
| 21850 | } |
| 21851 | assert(src_info.flags.size == .slice); |
| 21852 | break :len .{ .change_runtime_src_slice = .{ |
| 21853 | .bytes_per_src = src_elem_size, |
| 21854 | .bytes_per_dest = dest_elem_size, |
| 21855 | } }; |
| 21856 | } |
| 21857 | // We apply rules for comptime memory consistent with comptime loads/stores, where arrays of |
| 21858 | // comptime-only types can be "restructured". |
| 21859 | const dest_base_ty: Type, const dest_base_per_elem: u64 = dest_elem_ty.arrayBase(zcu); |
| 21860 | const src_base_ty: Type, const src_base_per_elem: u64 = src_elem_ty.arrayBase(zcu); |
| 21861 | // The source value has `src_len * src_base_per_elem` values of type `src_base_ty`. |
| 21862 | // The result value will have `dest_len * dest_base_per_elem` values of type `dest_base_ty`. |
| 21863 | if (dest_base_ty.toIntern() != src_base_ty.toIntern()) { |
| 21864 | return sema.fail(block, src, "cannot infer length of comptime-only '{f}' from incompatible '{f}'", .{ |
| 21865 | dest_ty.fmt(pt), operand_ty.fmt(pt), |
| 21866 | }); |
| 21867 | } |
| 21868 | // `src_base_ty` is comptime-only, so `src_elem_ty` is comptime-only, so `operand_ty` is |
| 21869 | // comptime-only, so `operand` is comptime-known, so `opt_src_len` is non-`null`. |
| 21870 | const src_len = opt_src_len.?; |
| 21871 | const base_len = src_len * src_base_per_elem; |
| 21872 | const dest_len = std.math.divExact(u64, base_len, dest_base_per_elem) catch switch (src_info.flags.size) { |
| 21873 | .slice => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}), |
| 21874 | .one => return sema.fail(block, src, "type '{f}' does not divide exactly into destination elements", .{src_elem_ty.fmt(pt)}), |
| 21875 | else => unreachable, |
| 21876 | }; |
| 21877 | break :len .{ .constant = dest_len }; |
| 21878 | }; |
| 21879 | |
| 21880 | // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs |
| 21881 | |
| 21882 | if (!flags.ptr_cast) { |
| 21883 | const is_array_ptr_to_slice = b: { |
| 21884 | if (dest_info.flags.size != .slice) break :b false; |
| 21885 | if (src_info.flags.size != .one) break :b false; |
| 21886 | const src_pointer_child: Type = .fromInterned(src_info.child); |
| 21887 | if (src_pointer_child.zigTypeTag(zcu) != .array) break :b false; |
| 21888 | const src_elem = src_pointer_child.childType(zcu); |
| 21889 | break :b src_elem.toIntern() == dest_info.child; |
| 21890 | }; |
| 21891 | |
| 21892 | check_size: { |
| 21893 | if (src_info.flags.size == dest_info.flags.size) break :check_size; |
| 21894 | if (is_array_ptr_to_slice) break :check_size; |
| 21895 | if (src_info.flags.size == .c) break :check_size; |
| 21896 | if (dest_info.flags.size == .c) break :check_size; |
| 21897 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 21898 | const msg = try sema.errMsg(src, "cannot implicitly convert {s} to {s}", .{ |
| 21899 | pointerSizeString(src_info.flags.size), |
| 21900 | pointerSizeString(dest_info.flags.size), |
| 21901 | }); |
| 21902 | errdefer msg.destroy(sema.gpa); |
| 21903 | if (dest_info.flags.size == .many and |
| 21904 | (src_info.flags.size == .slice or |
| 21905 | (src_info.flags.size == .one and Type.fromInterned(src_info.child).zigTypeTag(zcu) == .array))) |
| 21906 | { |
| 21907 | try sema.errNote(src, msg, "use 'ptr' field to convert slice to many pointer", .{}); |
| 21908 | } else { |
| 21909 | try sema.errNote(src, msg, "use @ptrCast to change pointer size", .{}); |
| 21910 | } |
| 21911 | break :msg msg; |
| 21912 | }); |
| 21913 | } |
| 21914 | |
| 21915 | check_child: { |
| 21916 | const src_child: Type = if (dest_info.flags.size == .slice and src_info.flags.size == .one) blk: { |
| 21917 | // *[n]T -> []T |
| 21918 | break :blk Type.fromInterned(src_info.child).childType(zcu); |
| 21919 | } else .fromInterned(src_info.child); |
| 21920 | |
| 21921 | const dest_child: Type = .fromInterned(dest_info.child); |
| 21922 | |
| 21923 | const imc_res = try sema.coerceInMemoryAllowed( |
| 21924 | block, |
| 21925 | dest_child, |
| 21926 | src_child, |
| 21927 | !dest_info.flags.is_const, |
| 21928 | zcu.getTarget(), |
| 21929 | src, |
| 21930 | operand_src, |
| 21931 | null, |
| 21932 | ); |
| 21933 | if (imc_res == .ok) break :check_child; |
| 21934 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 21935 | const msg = try sema.errMsg(src, "pointer element type '{f}' cannot coerce into element type '{f}'", .{ |
| 21936 | src_child.fmt(pt), dest_child.fmt(pt), |
| 21937 | }); |
| 21938 | errdefer msg.destroy(sema.gpa); |
| 21939 | try imc_res.report(sema, src, msg); |
| 21940 | try sema.errNote(src, msg, "use @ptrCast to cast pointer element type", .{}); |
| 21941 | break :msg msg; |
| 21942 | }); |
| 21943 | } |
| 21944 | |
| 21945 | check_sent: { |
| 21946 | if (dest_info.sentinel == .none) break :check_sent; |
| 21947 | if (src_info.flags.size == .c) break :check_sent; |
| 21948 | if (src_info.sentinel != .none) { |
| 21949 | const coerced_sent = try zcu.intern_pool.getCoerced(gpa, io, pt.tid, src_info.sentinel, dest_info.child); |
| 21950 | if (dest_info.sentinel == coerced_sent) break :check_sent; |
| 21951 | } |
| 21952 | if (is_array_ptr_to_slice) { |
| 21953 | // [*]nT -> []T |
| 21954 | const arr_ty: Type = .fromInterned(src_info.child); |
| 21955 | if (arr_ty.sentinel(zcu)) |src_sentinel| { |
| 21956 | const coerced_sent = try zcu.intern_pool.getCoerced(gpa, io, pt.tid, src_sentinel.toIntern(), dest_info.child); |
| 21957 | if (dest_info.sentinel == coerced_sent) break :check_sent; |
| 21958 | } |
| 21959 | } |
| 21960 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 21961 | const msg = if (src_info.sentinel == .none) blk: { |
| 21962 | break :blk try sema.errMsg(src, "destination pointer requires '{f}' sentinel", .{ |
| 21963 | Value.fromInterned(dest_info.sentinel).fmtValueSema(pt, sema), |
| 21964 | }); |
| 21965 | } else blk: { |
| 21966 | break :blk try sema.errMsg(src, "pointer sentinel '{f}' cannot coerce into pointer sentinel '{f}'", .{ |
| 21967 | Value.fromInterned(src_info.sentinel).fmtValueSema(pt, sema), |
| 21968 | Value.fromInterned(dest_info.sentinel).fmtValueSema(pt, sema), |
| 21969 | }); |
| 21970 | }; |
| 21971 | errdefer msg.destroy(sema.gpa); |
| 21972 | try sema.errNote(src, msg, "use @ptrCast to cast pointer sentinel", .{}); |
| 21973 | break :msg msg; |
| 21974 | }); |
| 21975 | } |
| 21976 | |
| 21977 | if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size) { |
| 21978 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 21979 | const msg = try sema.errMsg(src, "pointer host size '{d}' cannot coerce into pointer host size '{d}'", .{ |
| 21980 | src_info.packed_offset.host_size, |
| 21981 | dest_info.packed_offset.host_size, |
| 21982 | }); |
| 21983 | errdefer msg.destroy(sema.gpa); |
| 21984 | try sema.errNote(src, msg, "use @ptrCast to cast pointer host size", .{}); |
| 21985 | break :msg msg; |
| 21986 | }); |
| 21987 | } |
| 21988 | |
| 21989 | if (src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) { |
| 21990 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 21991 | const msg = try sema.errMsg(src, "pointer bit offset '{d}' cannot coerce into pointer bit offset '{d}'", .{ |
| 21992 | src_info.packed_offset.bit_offset, |
| 21993 | dest_info.packed_offset.bit_offset, |
| 21994 | }); |
| 21995 | errdefer msg.destroy(sema.gpa); |
| 21996 | try sema.errNote(src, msg, "use @ptrCast to cast pointer bit offset", .{}); |
| 21997 | break :msg msg; |
| 21998 | }); |
| 21999 | } |
| 22000 | |
| 22001 | check_allowzero: { |
| 22002 | const src_allows_zero = operand_ty.ptrAllowsZero(zcu); |
| 22003 | const dest_allows_zero = dest_ty.ptrAllowsZero(zcu); |
| 22004 | if (!src_allows_zero) break :check_allowzero; |
| 22005 | if (dest_allows_zero) break :check_allowzero; |
| 22006 | |
| 22007 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 22008 | const msg = try sema.errMsg(src, "'{f}' could have null values which are illegal in type '{f}'", .{ |
| 22009 | operand_ty.fmt(pt), |
| 22010 | dest_ty.fmt(pt), |
| 22011 | }); |
| 22012 | errdefer msg.destroy(sema.gpa); |
| 22013 | try sema.errNote(src, msg, "use @ptrCast to assert the pointer is not null", .{}); |
| 22014 | break :msg msg; |
| 22015 | }); |
| 22016 | } |
| 22017 | |
| 22018 | // TODO: vector index? |
| 22019 | } |
| 22020 | |
| 22021 | const src_align = if (src_info.flags.alignment != .none) |
| 22022 | src_info.flags.alignment |
| 22023 | else |
| 22024 | Type.fromInterned(src_info.child).abiAlignment(zcu); |
| 22025 | |
| 22026 | const dest_align = if (dest_info.flags.alignment != .none) |
| 22027 | dest_info.flags.alignment |
| 22028 | else |
| 22029 | Type.fromInterned(dest_info.child).abiAlignment(zcu); |
| 22030 | |
| 22031 | if (!flags.align_cast) { |
| 22032 | if (dest_align.compare(.gt, src_align)) { |
| 22033 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 22034 | const msg = try sema.errMsg(src, "{s} increases pointer alignment", .{operation}); |
| 22035 | errdefer msg.destroy(sema.gpa); |
| 22036 | try sema.errNote(operand_src, msg, "'{f}' has alignment '{d}'", .{ |
| 22037 | operand_ty.fmt(pt), src_align.toByteUnits() orelse 0, |
| 22038 | }); |
| 22039 | try sema.errNote(src, msg, "'{f}' has alignment '{d}'", .{ |
| 22040 | dest_ty.fmt(pt), dest_align.toByteUnits() orelse 0, |
| 22041 | }); |
| 22042 | try sema.errNote(src, msg, "use @alignCast to assert pointer alignment", .{}); |
| 22043 | break :msg msg; |
| 22044 | }); |
| 22045 | } |
| 22046 | } |
| 22047 | |
| 22048 | if (!flags.addrspace_cast) { |
| 22049 | if (src_info.flags.address_space != dest_info.flags.address_space) { |
| 22050 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 22051 | const msg = try sema.errMsg(src, "{s} changes pointer address space", .{operation}); |
| 22052 | errdefer msg.destroy(sema.gpa); |
| 22053 | try sema.errNote(operand_src, msg, "'{f}' has address space '{s}'", .{ |
| 22054 | operand_ty.fmt(pt), @tagName(src_info.flags.address_space), |
| 22055 | }); |
| 22056 | try sema.errNote(src, msg, "'{f}' has address space '{s}'", .{ |
| 22057 | dest_ty.fmt(pt), @tagName(dest_info.flags.address_space), |
| 22058 | }); |
| 22059 | try sema.errNote(src, msg, "use @addrSpaceCast to cast pointer address space", .{}); |
| 22060 | break :msg msg; |
| 22061 | }); |
| 22062 | } |
| 22063 | } else { |
| 22064 | // Some address space casts are always disallowed |
| 22065 | if (!target_util.addrSpaceCastIsValid(zcu.getTarget(), src_info.flags.address_space, dest_info.flags.address_space)) { |
| 22066 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 22067 | const msg = try sema.errMsg(src, "invalid address space cast", .{}); |
| 22068 | errdefer msg.destroy(sema.gpa); |
| 22069 | try sema.errNote(operand_src, msg, "address space '{s}' is not compatible with address space '{s}'", .{ |
| 22070 | @tagName(src_info.flags.address_space), |
| 22071 | @tagName(dest_info.flags.address_space), |
| 22072 | }); |
| 22073 | break :msg msg; |
| 22074 | }); |
| 22075 | } |
| 22076 | } |
| 22077 | |
| 22078 | if (!flags.const_cast) { |
| 22079 | if (src_info.flags.is_const and !dest_info.flags.is_const) { |
| 22080 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 22081 | const msg = try sema.errMsg(src, "{s} discards const qualifier", .{operation}); |
| 22082 | errdefer msg.destroy(sema.gpa); |
| 22083 | try sema.errNote(src, msg, "use @constCast to discard const qualifier", .{}); |
| 22084 | break :msg msg; |
| 22085 | }); |
| 22086 | } |
| 22087 | } |
| 22088 | |
| 22089 | if (!flags.volatile_cast) { |
| 22090 | if (src_info.flags.is_volatile and !dest_info.flags.is_volatile) { |
| 22091 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 22092 | const msg = try sema.errMsg(src, "{s} discards volatile qualifier", .{operation}); |
| 22093 | errdefer msg.destroy(sema.gpa); |
| 22094 | try sema.errNote(src, msg, "use @volatileCast to discard volatile qualifier", .{}); |
| 22095 | break :msg msg; |
| 22096 | }); |
| 22097 | } |
| 22098 | } |
| 22099 | |
| 22100 | // Type validation done -- this cast is okay. Let's do it! |
| 22101 | // |
| 22102 | // `operand` is a maybe-optional pointer or slice. |
| 22103 | // `dest_ty` is a maybe-optional pointer or slice. |
| 22104 | // |
| 22105 | // We have a few safety checks: |
| 22106 | // * if the destination does not allow zero, check the operand is not null / 0 |
| 22107 | // * if the destination is more aligned than the operand, check the pointer alignment |
| 22108 | // * if `slice_needs_len_change`, check the element count divides neatly |
| 22109 | |
| 22110 | ct: { |
| 22111 | if (flags.addrspace_cast) break :ct; // cannot `@addrSpaceCast` at comptime |
| 22112 | const operand_val = sema.resolveValue(operand) orelse break :ct; |
| 22113 | |
| 22114 | if (operand_val.isUndef(zcu)) { |
| 22115 | if (!dest_ty.ptrAllowsZero(zcu)) { |
| 22116 | return sema.failWithUseOfUndef(block, operand_src, null); |
| 22117 | } |
| 22118 | return pt.undefRef(dest_ty); |
| 22119 | } |
| 22120 | |
| 22121 | if (operand_val.isNull(zcu)) { |
| 22122 | if (!dest_ty.ptrAllowsZero(zcu)) { |
| 22123 | return sema.fail(block, operand_src, "null pointer casted to type '{f}'", .{dest_ty.fmt(pt)}); |
| 22124 | } |
| 22125 | if (dest_ty.zigTypeTag(zcu) == .optional) { |
| 22126 | return Air.internedToRef((try pt.nullValue(dest_ty)).toIntern()); |
| 22127 | } else { |
| 22128 | return Air.internedToRef((try pt.ptrIntValue(dest_ty, 0)).toIntern()); |
| 22129 | } |
| 22130 | } |
| 22131 | |
| 22132 | const ptr_val: Value = switch (src_info.flags.size) { |
| 22133 | .slice => .fromInterned(zcu.intern_pool.indexToKey(operand_val.toIntern()).slice.ptr), |
| 22134 | .one, .many, .c => operand_val, |
| 22135 | }; |
| 22136 | |
| 22137 | if (dest_align.compare(.gt, src_align)) { |
| 22138 | if (ptr_val.getUnsignedInt(zcu)) |addr| { |
| 22139 | const masked_addr = if (Type.fromInterned(dest_info.child).fnPtrMaskOrNull(zcu)) |mask| |
| 22140 | addr & mask |
| 22141 | else |
| 22142 | addr; |
| 22143 | |
| 22144 | if (!dest_align.check(masked_addr)) { |
| 22145 | return sema.fail(block, operand_src, "pointer address 0x{X} is not aligned to {d} bytes", .{ |
| 22146 | addr, |
| 22147 | dest_align.toByteUnits().?, |
| 22148 | }); |
| 22149 | } |
| 22150 | } |
| 22151 | } |
| 22152 | |
| 22153 | if (dest_info.flags.size == .slice) { |
| 22154 | // Because the operand is comptime-known and not `null`, the slice length has already been computed: |
| 22155 | const len: Value = switch (dest_slice_len.?) { |
| 22156 | .undef => .undef_usize, |
| 22157 | .constant => |n| try pt.intValue(.usize, n), |
| 22158 | .equal_runtime_src_slice => unreachable, |
| 22159 | .change_runtime_src_slice => unreachable, |
| 22160 | }; |
| 22161 | const dest_is_optional = dest_ty.zigTypeTag(zcu) == .optional; |
| 22162 | const slice_ty = if (dest_is_optional) dest_ty.optionalChild(zcu) else dest_ty; |
| 22163 | const slice_val = try pt.intern(.{ .slice = .{ |
| 22164 | .ty = slice_ty.toIntern(), |
| 22165 | .ptr = (try pt.getCoerced(ptr_val, slice_ty.slicePtrFieldType(zcu))).toIntern(), |
| 22166 | .len = len.toIntern(), |
| 22167 | } }); |
| 22168 | if (!dest_is_optional) return Air.internedToRef(slice_val); |
| 22169 | return Air.internedToRef(try pt.intern(.{ .opt = .{ |
| 22170 | .ty = dest_ty.toIntern(), |
| 22171 | .val = slice_val, |
| 22172 | } })); |
| 22173 | } else { |
| 22174 | // Any to non-slice |
| 22175 | const new_ptr_val = try pt.getCoerced(ptr_val, dest_ty); |
| 22176 | return Air.internedToRef(new_ptr_val.toIntern()); |
| 22177 | } |
| 22178 | } |
| 22179 | |
| 22180 | try sema.validateRuntimeValue(block, operand_src, operand); |
| 22181 | try sema.checkLogicalPtrCast(block, src, operand_ty, dest_ty); |
| 22182 | |
| 22183 | const can_cast_to_int = !target_util.shouldBlockPointerOps(zcu.getTarget(), operand_ty.ptrAddressSpace(zcu)); |
| 22184 | const need_null_check = can_cast_to_int and block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu); |
| 22185 | const need_align_check = can_cast_to_int and block.wantSafety() and dest_align.compare(.gt, src_align); |
| 22186 | |
| 22187 | const slice_needs_len_change = if (dest_slice_len) |l| switch (l) { |
| 22188 | .undef, .equal_runtime_src_slice => false, |
| 22189 | .constant, .change_runtime_src_slice => true, |
| 22190 | } else false; |
| 22191 | |
| 22192 | // `operand` might be a slice. If `need_operand_ptr`, we'll populate `operand_ptr` with the raw pointer. |
| 22193 | const need_operand_ptr = src_info.flags.size != .slice or // we already have it |
| 22194 | dest_info.flags.size != .slice or // the result is a raw pointer |
| 22195 | need_null_check or // safety check happens on pointer |
| 22196 | need_align_check or // safety check happens on pointer |
| 22197 | flags.addrspace_cast or // AIR addrspace_cast acts on a pointer |
| 22198 | slice_needs_len_change; // to change the length, we reconstruct the slice |
| 22199 | |
| 22200 | // This is not quite just the pointer part of `operand` -- it's also had the address space cast done already. |
| 22201 | const operand_ptr: Air.Inst.Ref = ptr: { |
| 22202 | if (!need_operand_ptr) break :ptr .none; |
| 22203 | // First, just get the pointer. |
| 22204 | const pre_addrspace_cast = inner: { |
| 22205 | if (src_info.flags.size != .slice) break :inner operand; |
| 22206 | if (operand_ty.zigTypeTag(zcu) == .optional) { |
| 22207 | break :inner try sema.analyzeOptionalSlicePtr(block, operand_src, operand, operand_ty); |
| 22208 | } else { |
| 22209 | break :inner try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty); |
| 22210 | } |
| 22211 | }; |
| 22212 | // Now, do an addrspace cast if necessary! |
| 22213 | if (!flags.addrspace_cast) break :ptr pre_addrspace_cast; |
| 22214 | |
| 22215 | const intermediate_ptr_ty = try pt.ptrType(info: { |
| 22216 | var info = src_info; |
| 22217 | info.flags.address_space = dest_info.flags.address_space; |
| 22218 | break :info info; |
| 22219 | }); |
| 22220 | const intermediate_ty = if (operand_ty.zigTypeTag(zcu) == .optional) blk: { |
| 22221 | break :blk try pt.optionalType(intermediate_ptr_ty.toIntern()); |
| 22222 | } else intermediate_ptr_ty; |
| 22223 | break :ptr try block.addInst(.{ |
| 22224 | .tag = .addrspace_cast, |
| 22225 | .data = .{ .ty_op = .{ |
| 22226 | .ty = intermediate_ty, |
| 22227 | .operand = pre_addrspace_cast, |
| 22228 | } }, |
| 22229 | }); |
| 22230 | }; |
| 22231 | |
| 22232 | // Whether we need to know if the (slice) operand has `len == 0`. |
| 22233 | const need_operand_len_is_zero = src_info.flags.size == .slice and |
| 22234 | dest_info.flags.size == .slice and |
| 22235 | (need_null_check or need_align_check); |
| 22236 | // Whether we need to get the (slice) operand's `len`. |
| 22237 | const need_operand_len = need_len: { |
| 22238 | if (src_info.flags.size != .slice) break :need_len false; |
| 22239 | if (dest_info.flags.size != .slice) break :need_len false; |
| 22240 | if (need_operand_len_is_zero) break :need_len true; |
| 22241 | if (flags.addrspace_cast or slice_needs_len_change) break :need_len true; |
| 22242 | break :need_len false; |
| 22243 | }; |
| 22244 | // `.none` if `!need_operand_len`. |
| 22245 | const operand_len: Air.Inst.Ref = len: { |
| 22246 | if (!need_operand_len) break :len .none; |
| 22247 | break :len try block.addTyOp(.slice_len, .usize, operand); |
| 22248 | }; |
| 22249 | // `.none` if `!need_operand_len_is_zero`. |
| 22250 | const operand_len_is_zero: Air.Inst.Ref = zero: { |
| 22251 | if (!need_operand_len_is_zero) break :zero .none; |
| 22252 | assert(need_operand_len); |
| 22253 | break :zero try block.addBinOp(.cmp_eq, operand_len, .zero_usize); |
| 22254 | }; |
| 22255 | |
| 22256 | // `operand_ptr` converted to an integer, for safety checks. |
| 22257 | const operand_ptr_int: Air.Inst.Ref = if (need_null_check or need_align_check) i: { |
| 22258 | assert(need_operand_ptr); |
| 22259 | break :i try block.addTyOp(.int_from_ptr, .usize, operand_ptr); |
| 22260 | } else .none; |
| 22261 | |
| 22262 | if (need_null_check) { |
| 22263 | assert(operand_ptr_int != .none); |
| 22264 | const ptr_is_non_zero = try block.addBinOp(.cmp_neq, operand_ptr_int, .zero_usize); |
| 22265 | const ok = if (src_info.flags.size == .slice and dest_info.flags.size == .slice) ok: { |
| 22266 | break :ok try block.addBinOp(.bit_or, operand_len_is_zero, ptr_is_non_zero); |
| 22267 | } else ptr_is_non_zero; |
| 22268 | try sema.addSafetyCheck(block, src, ok, .cast_to_null); |
| 22269 | } |
| 22270 | if (need_align_check) { |
| 22271 | assert(operand_ptr_int != .none); |
| 22272 | const align_mask = try pt.intRef(.usize, mask: { |
| 22273 | const target_ptr_mask = Type.fromInterned(dest_info.child).fnPtrMaskOrNull(zcu) orelse ~@as(u64, 0); |
| 22274 | break :mask (dest_align.toByteUnits().? - 1) & target_ptr_mask; |
| 22275 | }); |
| 22276 | const ptr_masked = try block.addBinOp(.bit_and, operand_ptr_int, align_mask); |
| 22277 | const is_aligned = try block.addBinOp(.cmp_eq, ptr_masked, .zero_usize); |
| 22278 | const ok = if (src_info.flags.size == .slice and dest_info.flags.size == .slice) ok: { |
| 22279 | break :ok try block.addBinOp(.bit_or, operand_len_is_zero, is_aligned); |
| 22280 | } else is_aligned; |
| 22281 | try sema.addSafetyCheck(block, src, ok, .incorrect_alignment); |
| 22282 | } |
| 22283 | |
| 22284 | if (dest_info.flags.size == .slice) { |
| 22285 | if (src_info.flags.size == .slice and !flags.addrspace_cast and !slice_needs_len_change) { |
| 22286 | // Fast path: just pointer cast! |
| 22287 | return block.addTyOp(.ptr_cast, dest_ty, operand); |
| 22288 | } |
| 22289 | |
| 22290 | // We need to deconstruct the slice (if applicable) and reconstruct it. |
| 22291 | assert(need_operand_ptr); |
| 22292 | |
| 22293 | const result_len: Air.Inst.Ref = switch (dest_slice_len.?) { |
| 22294 | .undef => .undef_usize, |
| 22295 | .constant => |n| try pt.intRef(.usize, n), |
| 22296 | .equal_runtime_src_slice => len: { |
| 22297 | assert(need_operand_len); |
| 22298 | break :len operand_len; |
| 22299 | }, |
| 22300 | .change_runtime_src_slice => |change| len: { |
| 22301 | assert(need_operand_len); |
| 22302 | // If `mul / div` is a whole number, then just multiply the length by it. |
| 22303 | if (std.math.divExact(u64, change.bytes_per_src, change.bytes_per_dest)) |dest_per_src| { |
| 22304 | const multiplier = try pt.intRef(.usize, dest_per_src); |
| 22305 | break :len try block.addBinOp(.mul, operand_len, multiplier); |
| 22306 | } else |err| switch (err) { |
| 22307 | error.DivisionByZero => unreachable, |
| 22308 | error.UnexpectedRemainder => {}, // fall through to code below |
| 22309 | } |
| 22310 | // If `div / mul` is a whole number, then just divide the length by it. |
| 22311 | // This incurs a safety check. |
| 22312 | if (std.math.divExact(u64, change.bytes_per_dest, change.bytes_per_src)) |src_per_dest| { |
| 22313 | const divisor = try pt.intRef(.usize, src_per_dest); |
| 22314 | if (block.wantSafety()) { |
| 22315 | // Check that the element count divides neatly. |
| 22316 | const remainder = try block.addBinOp(.rem, operand_len, divisor); |
| 22317 | const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize); |
| 22318 | try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len}); |
| 22319 | } |
| 22320 | break :len try block.addBinOp(.div_exact, operand_len, divisor); |
| 22321 | } else |err| switch (err) { |
| 22322 | error.DivisionByZero => unreachable, |
| 22323 | error.UnexpectedRemainder => {}, // fall through to code below |
| 22324 | } |
| 22325 | // Fallback: the elements don't divide easily. We'll multiply *and* divide. This incurs a safety check. |
| 22326 | const total_bytes_ref = try block.addBinOp(.mul, operand_len, try pt.intRef(.usize, change.bytes_per_src)); |
| 22327 | const bytes_per_dest_ref = try pt.intRef(.usize, change.bytes_per_dest); |
| 22328 | if (block.wantSafety()) { |
| 22329 | // Check that `total_bytes_ref` divides neatly into `bytes_per_dest_ref`. |
| 22330 | const remainder = try block.addBinOp(.rem, total_bytes_ref, bytes_per_dest_ref); |
| 22331 | const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize); |
| 22332 | try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len}); |
| 22333 | } |
| 22334 | break :len try block.addBinOp(.div_exact, total_bytes_ref, bytes_per_dest_ref); |
| 22335 | }, |
| 22336 | }; |
| 22337 | |
| 22338 | const operand_ptr_ty = sema.typeOf(operand_ptr); |
| 22339 | const want_ptr_ty = switch (dest_ty.zigTypeTag(zcu)) { |
| 22340 | .optional => try pt.optionalType(dest_ty.childType(zcu).slicePtrFieldType(zcu).toIntern()), |
| 22341 | .pointer => dest_ty.slicePtrFieldType(zcu), |
| 22342 | else => unreachable, |
| 22343 | }; |
| 22344 | const coerced_ptr = if (operand_ptr_ty.toIntern() != want_ptr_ty.toIntern()) ptr: { |
| 22345 | break :ptr try block.addTyOp(.ptr_cast, want_ptr_ty, operand_ptr); |
| 22346 | } else operand_ptr; |
| 22347 | |
| 22348 | return block.addInst(.{ |
| 22349 | .tag = .slice, |
| 22350 | .data = .{ .ty_pl = .{ |
| 22351 | .ty = dest_ty, |
| 22352 | .payload = try sema.addExtra(Air.Bin{ |
| 22353 | .lhs = coerced_ptr, |
| 22354 | .rhs = result_len, |
| 22355 | }), |
| 22356 | } }, |
| 22357 | }); |
| 22358 | } else { |
| 22359 | assert(need_operand_ptr); |
| 22360 | // We just need a ptr_cast, if even that (we might only have needed the `addrspace_cast`). |
| 22361 | const result = if (sema.typeOf(operand_ptr).toIntern() == dest_ty.toIntern()) |
| 22362 | operand_ptr |
| 22363 | else |
| 22364 | try block.addTyOp(.ptr_cast, dest_ty, operand_ptr); |
| 22365 | |
| 22366 | try sema.checkKnownAllocPtr(block, operand, result); |
| 22367 | return result; |
| 22368 | } |
| 22369 | } |
| 22370 | |
| 22371 | fn zirPtrCastNoDest(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 22372 | const pt = sema.pt; |
| 22373 | const zcu = pt.zcu; |
| 22374 | const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?; |
| 22375 | const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(FlagsInt, @truncate(extended.small))); |
| 22376 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 22377 | const src = block.nodeOffset(extra.node); |
| 22378 | const operand_src = block.src(.{ .node_offset_ptrcast_operand = extra.node }); |
| 22379 | const operand = sema.resolveInst(extra.operand); |
| 22380 | const operand_ty = sema.typeOf(operand); |
| 22381 | try sema.checkPtrOperand(block, operand_src, operand_ty); |
| 22382 | |
| 22383 | var ptr_info = operand_ty.ptrInfo(zcu); |
| 22384 | if (flags.const_cast) ptr_info.flags.is_const = false; |
| 22385 | if (flags.volatile_cast) ptr_info.flags.is_volatile = false; |
| 22386 | |
| 22387 | const dest_ty = blk: { |
| 22388 | const dest_ty = try pt.ptrType(ptr_info); |
| 22389 | if (operand_ty.zigTypeTag(zcu) == .optional) { |
| 22390 | break :blk try pt.optionalType(dest_ty.toIntern()); |
| 22391 | } |
| 22392 | break :blk dest_ty; |
| 22393 | }; |
| 22394 | |
| 22395 | if (sema.resolveValue(operand)) |operand_val| { |
| 22396 | return Air.internedToRef((try pt.getCoerced(operand_val, dest_ty)).toIntern()); |
| 22397 | } |
| 22398 | |
| 22399 | try sema.requireRuntimeBlock(block, src, null); |
| 22400 | const new_ptr = try block.addTyOp(.ptr_cast, dest_ty, operand); |
| 22401 | try sema.checkKnownAllocPtr(block, operand, new_ptr); |
| 22402 | return new_ptr; |
| 22403 | } |
| 22404 | |
| 22405 | fn zirTruncate(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 22406 | const pt = sema.pt; |
| 22407 | const zcu = pt.zcu; |
| 22408 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 22409 | const src = block.nodeOffset(inst_data.src_node); |
| 22410 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 22411 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 22412 | const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@truncate"); |
| 22413 | const dest_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, dest_ty, src); |
| 22414 | const operand = sema.resolveInst(extra.rhs); |
| 22415 | const operand_ty = sema.typeOf(operand); |
| 22416 | const operand_scalar_ty = try sema.checkIntOrVectorAllowComptime(block, operand_ty, operand_src); |
| 22417 | |
| 22418 | const operand_is_vector = operand_ty.zigTypeTag(zcu) == .vector; |
| 22419 | const dest_is_vector = dest_ty.zigTypeTag(zcu) == .vector; |
| 22420 | if (operand_is_vector != dest_is_vector) { |
| 22421 | return sema.failWithTypeMismatch(block, operand_src, dest_ty, operand_ty); |
| 22422 | } |
| 22423 | |
| 22424 | if (dest_scalar_ty.zigTypeTag(zcu) == .comptime_int) { |
| 22425 | return sema.coerce(block, dest_ty, operand, operand_src); |
| 22426 | } |
| 22427 | |
| 22428 | if (try dest_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 22429 | |
| 22430 | const dest_info = dest_scalar_ty.intInfo(zcu); |
| 22431 | |
| 22432 | if (operand_scalar_ty.zigTypeTag(zcu) != .comptime_int) { |
| 22433 | const operand_info = operand_ty.intInfo(zcu); |
| 22434 | |
| 22435 | if (operand_info.signedness != dest_info.signedness) { |
| 22436 | return sema.fail(block, operand_src, "expected {s} integer type, found '{f}'", .{ |
| 22437 | @tagName(dest_info.signedness), operand_ty.fmt(pt), |
| 22438 | }); |
| 22439 | } |
| 22440 | if (dest_info.bits >= operand_info.bits) { |
| 22441 | return sema.coerce(block, dest_ty, operand, operand_src); |
| 22442 | } |
| 22443 | } |
| 22444 | |
| 22445 | if (sema.resolveValue(operand)) |val| { |
| 22446 | const result_val = try arith.truncate(sema, val, operand_ty, dest_ty, dest_info.signedness, dest_info.bits); |
| 22447 | return Air.internedToRef(result_val.toIntern()); |
| 22448 | } |
| 22449 | |
| 22450 | try sema.requireRuntimeBlock(block, src, operand_src); |
| 22451 | return block.addTyOp(.trunc, dest_ty, operand); |
| 22452 | } |
| 22453 | |
| 22454 | fn zirBitCount( |
| 22455 | sema: *Sema, |
| 22456 | block: *Block, |
| 22457 | inst: Zir.Inst.Index, |
| 22458 | air_tag: Air.Inst.Tag, |
| 22459 | comptime comptimeOp: fn (val: Value, ty: Type, zcu: *Zcu) u64, |
| 22460 | ) CompileError!Air.Inst.Ref { |
| 22461 | const pt = sema.pt; |
| 22462 | const zcu = pt.zcu; |
| 22463 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 22464 | const src = block.nodeOffset(inst_data.src_node); |
| 22465 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 22466 | const operand = sema.resolveInst(inst_data.operand); |
| 22467 | const operand_ty = sema.typeOf(operand); |
| 22468 | _ = try sema.checkIntOrVector(block, operand, operand_src); |
| 22469 | const bits = operand_ty.intInfo(zcu).bits; |
| 22470 | |
| 22471 | const result_scalar_ty = try pt.smallestUnsignedInt(bits); |
| 22472 | switch (operand_ty.zigTypeTag(zcu)) { |
| 22473 | .vector => { |
| 22474 | const vec_len = operand_ty.vectorLen(zcu); |
| 22475 | const result_ty = try pt.vectorType(.{ |
| 22476 | .len = vec_len, |
| 22477 | .child = result_scalar_ty.toIntern(), |
| 22478 | }); |
| 22479 | if (sema.resolveValue(operand)) |val| { |
| 22480 | if (val.isUndef(zcu)) return pt.undefRef(result_ty); |
| 22481 | |
| 22482 | const elems = try sema.arena.alloc(InternPool.Index, vec_len); |
| 22483 | const scalar_ty = operand_ty.scalarType(zcu); |
| 22484 | for (elems, 0..) |*elem, i| { |
| 22485 | const elem_val = try val.elemValue(pt, i); |
| 22486 | const count = comptimeOp(elem_val, scalar_ty, zcu); |
| 22487 | elem.* = (try pt.intValue(result_scalar_ty, count)).toIntern(); |
| 22488 | } |
| 22489 | return Air.internedToRef((try pt.aggregateValue(result_ty, elems)).toIntern()); |
| 22490 | } else { |
| 22491 | try sema.requireRuntimeBlock(block, src, operand_src); |
| 22492 | return block.addTyOp(air_tag, result_ty, operand); |
| 22493 | } |
| 22494 | }, |
| 22495 | .int => { |
| 22496 | if (sema.resolveValue(operand)) |val| { |
| 22497 | if (val.isUndef(zcu)) return pt.undefRef(result_scalar_ty); |
| 22498 | return pt.intRef(result_scalar_ty, comptimeOp(val, operand_ty, zcu)); |
| 22499 | } else { |
| 22500 | try sema.requireRuntimeBlock(block, src, operand_src); |
| 22501 | return block.addTyOp(air_tag, result_scalar_ty, operand); |
| 22502 | } |
| 22503 | }, |
| 22504 | else => unreachable, |
| 22505 | } |
| 22506 | } |
| 22507 | |
| 22508 | fn zirByteSwap(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 22509 | const pt = sema.pt; |
| 22510 | const zcu = pt.zcu; |
| 22511 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 22512 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 22513 | const operand = sema.resolveInst(inst_data.operand); |
| 22514 | const operand_ty = sema.typeOf(operand); |
| 22515 | const scalar_ty = try sema.checkIntOrVector(block, operand, operand_src); |
| 22516 | const bits = scalar_ty.intInfo(zcu).bits; |
| 22517 | if (bits % 8 != 0) { |
| 22518 | return sema.fail( |
| 22519 | block, |
| 22520 | operand_src, |
| 22521 | "@byteSwap requires the number of bits to be evenly divisible by 8, but {f} has {d} bits", |
| 22522 | .{ scalar_ty.fmt(pt), bits }, |
| 22523 | ); |
| 22524 | } |
| 22525 | if (sema.resolveValue(operand)) |operand_val| { |
| 22526 | return .fromValue(try arith.byteSwap(sema, operand_val, operand_ty)); |
| 22527 | } |
| 22528 | return block.addTyOp(.byte_swap, operand_ty, operand); |
| 22529 | } |
| 22530 | |
| 22531 | fn zirBitReverse(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 22532 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 22533 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 22534 | const operand = sema.resolveInst(inst_data.operand); |
| 22535 | const operand_ty = sema.typeOf(operand); |
| 22536 | _ = try sema.checkIntOrVector(block, operand, operand_src); |
| 22537 | |
| 22538 | if (sema.resolveValue(operand)) |operand_val| { |
| 22539 | return .fromValue(try arith.bitReverse(sema, operand_val, operand_ty)); |
| 22540 | } |
| 22541 | return block.addTyOp(.bit_reverse, operand_ty, operand); |
| 22542 | } |
| 22543 | |
| 22544 | fn zirBitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 22545 | const offset = try sema.bitOffsetOf(block, inst); |
| 22546 | return sema.pt.intRef(.comptime_int, offset); |
| 22547 | } |
| 22548 | |
| 22549 | fn zirOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 22550 | const offset = try sema.bitOffsetOf(block, inst); |
| 22551 | // TODO reminder to make this a compile error for packed structs |
| 22552 | return sema.pt.intRef(.comptime_int, offset / 8); |
| 22553 | } |
| 22554 | |
| 22555 | fn bitOffsetOf(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!u64 { |
| 22556 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 22557 | const src = block.src(.{ .node_offset_bin_op = inst_data.src_node }); |
| 22558 | const ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 22559 | const field_name_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 22560 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 22561 | |
| 22562 | const ty = try sema.resolveType(block, ty_src, extra.lhs); |
| 22563 | const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.rhs, .{ .simple = .field_name }); |
| 22564 | |
| 22565 | try sema.ensureLayoutResolved(ty, ty_src, .field_queried); |
| 22566 | |
| 22567 | const pt = sema.pt; |
| 22568 | const zcu = pt.zcu; |
| 22569 | const ip = &zcu.intern_pool; |
| 22570 | switch (ty.zigTypeTag(zcu)) { |
| 22571 | .@"struct" => {}, |
| 22572 | else => return sema.fail(block, ty_src, "expected struct type, found '{f}'", .{ty.fmt(pt)}), |
| 22573 | } |
| 22574 | |
| 22575 | const field_index = if (ty.isTuple(zcu)) blk: { |
| 22576 | if (field_name.eqlSlice("len", ip)) { |
| 22577 | return sema.fail(block, src, "no offset available for 'len' field of tuple", .{}); |
| 22578 | } |
| 22579 | break :blk try sema.tupleFieldIndex(block, ty, field_name, field_name_src); |
| 22580 | } else try sema.structFieldIndex(block, ty, field_name, field_name_src); |
| 22581 | |
| 22582 | if (ty.structFieldIsComptime(field_index, zcu)) { |
| 22583 | return sema.fail(block, src, "no offset available for comptime field", .{}); |
| 22584 | } |
| 22585 | |
| 22586 | switch (ty.containerLayout(zcu)) { |
| 22587 | .@"packed" => { |
| 22588 | var bit_sum: u64 = 0; |
| 22589 | const struct_type = ip.loadStructType(ty.toIntern()); |
| 22590 | for (0..struct_type.field_types.len) |i| { |
| 22591 | if (i == field_index) { |
| 22592 | return bit_sum; |
| 22593 | } |
| 22594 | const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); |
| 22595 | bit_sum += field_ty.bitSize(zcu); |
| 22596 | } else unreachable; |
| 22597 | }, |
| 22598 | else => return ty.structFieldOffset(field_index, zcu) * 8, |
| 22599 | } |
| 22600 | } |
| 22601 | |
| 22602 | fn checkNamespaceType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!void { |
| 22603 | const pt = sema.pt; |
| 22604 | const zcu = pt.zcu; |
| 22605 | switch (ty.zigTypeTag(zcu)) { |
| 22606 | .@"struct", .@"enum", .@"union", .@"opaque" => return, |
| 22607 | else => return sema.fail(block, src, "expected struct, enum, union, or opaque; found '{f}'", .{ty.fmt(pt)}), |
| 22608 | } |
| 22609 | } |
| 22610 | |
| 22611 | /// Returns `true` if the type was a comptime_int. |
| 22612 | fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { |
| 22613 | const pt = sema.pt; |
| 22614 | const zcu = pt.zcu; |
| 22615 | switch (ty.zigTypeTag(zcu)) { |
| 22616 | .comptime_int => return true, |
| 22617 | .int => return false, |
| 22618 | else => return sema.fail(block, src, "expected integer type, found '{f}'", .{ty.fmt(pt)}), |
| 22619 | } |
| 22620 | } |
| 22621 | |
| 22622 | fn checkInvalidPtrIntArithmetic( |
| 22623 | sema: *Sema, |
| 22624 | block: *Block, |
| 22625 | src: LazySrcLoc, |
| 22626 | ty: Type, |
| 22627 | ) CompileError!void { |
| 22628 | const pt = sema.pt; |
| 22629 | const zcu = pt.zcu; |
| 22630 | switch (ty.zigTypeTag(zcu)) { |
| 22631 | .pointer => switch (ty.ptrSize(zcu)) { |
| 22632 | .one, .slice => return, |
| 22633 | .many, .c => return sema.failWithInvalidPtrArithmetic(block, src, "pointer-integer", "addition and subtraction"), |
| 22634 | }, |
| 22635 | else => return, |
| 22636 | } |
| 22637 | } |
| 22638 | |
| 22639 | fn checkArithmeticOp( |
| 22640 | sema: *Sema, |
| 22641 | block: *Block, |
| 22642 | src: LazySrcLoc, |
| 22643 | scalar_tag: std.lang.TypeId, |
| 22644 | lhs_zig_ty_tag: std.lang.TypeId, |
| 22645 | rhs_zig_ty_tag: std.lang.TypeId, |
| 22646 | zir_tag: Zir.Inst.Tag, |
| 22647 | ) CompileError!void { |
| 22648 | const is_int = scalar_tag == .int or scalar_tag == .comptime_int; |
| 22649 | const is_float = scalar_tag == .float or scalar_tag == .comptime_float; |
| 22650 | |
| 22651 | if (!is_int and !(is_float and floatOpAllowed(zir_tag))) { |
| 22652 | return sema.fail(block, src, "invalid operands to binary expression: '{s}' and '{s}'", .{ |
| 22653 | @tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag), |
| 22654 | }); |
| 22655 | } |
| 22656 | } |
| 22657 | |
| 22658 | fn checkPtrOperand( |
| 22659 | sema: *Sema, |
| 22660 | block: *Block, |
| 22661 | ty_src: LazySrcLoc, |
| 22662 | ty: Type, |
| 22663 | ) CompileError!void { |
| 22664 | const pt = sema.pt; |
| 22665 | const zcu = pt.zcu; |
| 22666 | switch (ty.zigTypeTag(zcu)) { |
| 22667 | .pointer => return, |
| 22668 | .@"fn" => { |
| 22669 | const msg = msg: { |
| 22670 | const msg = try sema.errMsg( |
| 22671 | ty_src, |
| 22672 | "expected pointer, found '{f}'", |
| 22673 | .{ty.fmt(pt)}, |
| 22674 | ); |
| 22675 | errdefer msg.destroy(sema.gpa); |
| 22676 | |
| 22677 | try sema.errNote(ty_src, msg, "use '&' to obtain a function pointer", .{}); |
| 22678 | |
| 22679 | break :msg msg; |
| 22680 | }; |
| 22681 | return sema.failWithOwnedErrorMsg(block, msg); |
| 22682 | }, |
| 22683 | .optional => if (ty.childType(zcu).zigTypeTag(zcu) == .pointer) return, |
| 22684 | else => {}, |
| 22685 | } |
| 22686 | return sema.fail(block, ty_src, "expected pointer type, found '{f}'", .{ty.fmt(pt)}); |
| 22687 | } |
| 22688 | |
| 22689 | fn checkPtrType( |
| 22690 | sema: *Sema, |
| 22691 | block: *Block, |
| 22692 | ty_src: LazySrcLoc, |
| 22693 | ty: Type, |
| 22694 | allow_slice: bool, |
| 22695 | ) CompileError!void { |
| 22696 | const pt = sema.pt; |
| 22697 | const zcu = pt.zcu; |
| 22698 | switch (ty.zigTypeTag(zcu)) { |
| 22699 | .pointer => if (allow_slice or !ty.isSlice(zcu)) return, |
| 22700 | .@"fn" => { |
| 22701 | const msg = msg: { |
| 22702 | const msg = try sema.errMsg( |
| 22703 | ty_src, |
| 22704 | "expected pointer type, found '{f}'", |
| 22705 | .{ty.fmt(pt)}, |
| 22706 | ); |
| 22707 | errdefer msg.destroy(sema.gpa); |
| 22708 | |
| 22709 | try sema.errNote(ty_src, msg, "use '*const ' to make a function pointer type", .{}); |
| 22710 | |
| 22711 | break :msg msg; |
| 22712 | }; |
| 22713 | return sema.failWithOwnedErrorMsg(block, msg); |
| 22714 | }, |
| 22715 | .optional => if (ty.childType(zcu).zigTypeTag(zcu) == .pointer) return, |
| 22716 | else => {}, |
| 22717 | } |
| 22718 | return sema.fail(block, ty_src, "expected pointer type, found '{f}'", .{ty.fmt(pt)}); |
| 22719 | } |
| 22720 | |
| 22721 | fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { |
| 22722 | const pt = sema.pt; |
| 22723 | const zcu = pt.zcu; |
| 22724 | |
| 22725 | if (block.isComptime() or block.is_typeof) return; |
| 22726 | if (zcu.intern_pool.indexToKey(ty.toIntern()) == .ptr_type) { |
| 22727 | const target = zcu.getTarget(); |
| 22728 | const as = ty.ptrAddressSpace(zcu); |
| 22729 | if (target_util.shouldBlockPointerOps(target, as)) { |
| 22730 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 22731 | const msg = try sema.errMsg(src, "illegal operation on logical pointer of type '{f}'", .{ty.fmt(pt)}); |
| 22732 | errdefer msg.destroy(sema.gpa); |
| 22733 | try sema.errNote( |
| 22734 | src, |
| 22735 | msg, |
| 22736 | "pointers with address space '{t}' do not support arithmetic or indexing on target {t}-{t}", |
| 22737 | .{ as, target.cpu.arch.family(), target.os.tag }, |
| 22738 | ); |
| 22739 | break :msg msg; |
| 22740 | }); |
| 22741 | } |
| 22742 | } |
| 22743 | } |
| 22744 | |
| 22745 | fn checkLogicalPtrCast( |
| 22746 | sema: *Sema, |
| 22747 | block: *Block, |
| 22748 | src: LazySrcLoc, |
| 22749 | operand_ty: Type, |
| 22750 | dest_ty: Type, |
| 22751 | ) CompileError!void { |
| 22752 | const pt = sema.pt; |
| 22753 | const zcu = pt.zcu; |
| 22754 | const src_info = operand_ty.ptrInfo(zcu); |
| 22755 | const dest_info = dest_ty.ptrInfo(zcu); |
| 22756 | const src_child: Type = .fromInterned(src_info.child); |
| 22757 | const dest_child: Type = .fromInterned(dest_info.child); |
| 22758 | |
| 22759 | if (block.isComptime() or block.is_typeof) return; |
| 22760 | switch (zcu.getTarget().os.tag) { |
| 22761 | .vulkan, .opengl => {}, |
| 22762 | else => return, |
| 22763 | } |
| 22764 | if (src_info.flags.address_space == .physical_storage_buffer) return; |
| 22765 | if (!dest_child.hasRuntimeBits(zcu)) return; |
| 22766 | |
| 22767 | var cur = src_child; |
| 22768 | while (cur.toIntern() != dest_info.child) { |
| 22769 | cur = switch (cur.zigTypeTag(zcu)) { |
| 22770 | .array, .vector => cur.childType(zcu), |
| 22771 | .@"struct" => field: { |
| 22772 | for (0..cur.structFieldCount(zcu)) |i| { |
| 22773 | const field_ty = cur.fieldType(i, zcu); |
| 22774 | if (field_ty.hasRuntimeBits(zcu) and cur.structFieldOffset(i, zcu) == 0) break :field field_ty; |
| 22775 | } |
| 22776 | break :field null; |
| 22777 | }, |
| 22778 | else => null, |
| 22779 | } orelse return sema.failWithOwnedErrorMsg(block, msg: { |
| 22780 | const msg = try sema.errMsg(src, "cannot cast pointer '{f}' to '{f}'", .{ operand_ty.fmt(pt), dest_ty.fmt(pt) }); |
| 22781 | errdefer msg.destroy(sema.gpa); |
| 22782 | try sema.errNote(src, msg, "'{f}' must appear at offset 0 inside '{f}'", .{ dest_child.fmt(pt), src_child.fmt(pt) }); |
| 22783 | break :msg msg; |
| 22784 | }); |
| 22785 | } |
| 22786 | } |
| 22787 | |
| 22788 | fn checkVectorElemType( |
| 22789 | sema: *Sema, |
| 22790 | block: *Block, |
| 22791 | ty_src: LazySrcLoc, |
| 22792 | ty: Type, |
| 22793 | ) CompileError!void { |
| 22794 | const pt = sema.pt; |
| 22795 | const zcu = pt.zcu; |
| 22796 | switch (ty.zigTypeTag(zcu)) { |
| 22797 | .int, .float, .bool => return, |
| 22798 | .optional, .pointer => if (ty.isPtrAtRuntime(zcu)) return, |
| 22799 | else => {}, |
| 22800 | } |
| 22801 | return sema.fail(block, ty_src, "expected integer, float, bool, or pointer for the vector element type; found '{f}'", .{ty.fmt(pt)}); |
| 22802 | } |
| 22803 | |
| 22804 | fn checkFloatType( |
| 22805 | sema: *Sema, |
| 22806 | block: *Block, |
| 22807 | ty_src: LazySrcLoc, |
| 22808 | ty: Type, |
| 22809 | ) CompileError!void { |
| 22810 | const pt = sema.pt; |
| 22811 | const zcu = pt.zcu; |
| 22812 | switch (ty.zigTypeTag(zcu)) { |
| 22813 | .comptime_int, .comptime_float, .float => {}, |
| 22814 | else => return sema.fail(block, ty_src, "expected float type, found '{f}'", .{ty.fmt(pt)}), |
| 22815 | } |
| 22816 | } |
| 22817 | |
| 22818 | fn checkNumericType( |
| 22819 | sema: *Sema, |
| 22820 | block: *Block, |
| 22821 | ty_src: LazySrcLoc, |
| 22822 | ty: Type, |
| 22823 | ) CompileError!void { |
| 22824 | const pt = sema.pt; |
| 22825 | const zcu = pt.zcu; |
| 22826 | switch (ty.zigTypeTag(zcu)) { |
| 22827 | .comptime_float, .float, .comptime_int, .int => {}, |
| 22828 | .vector => switch (ty.childType(zcu).zigTypeTag(zcu)) { |
| 22829 | .comptime_float, .float, .comptime_int, .int => {}, |
| 22830 | else => |t| return sema.fail(block, ty_src, "expected number, found '{t}'", .{t}), |
| 22831 | }, |
| 22832 | else => return sema.fail(block, ty_src, "expected number, found '{f}'", .{ty.fmt(pt)}), |
| 22833 | } |
| 22834 | } |
| 22835 | |
| 22836 | /// Returns the casted pointer. |
| 22837 | fn checkAtomicPtrOperand( |
| 22838 | sema: *Sema, |
| 22839 | block: *Block, |
| 22840 | elem_ty: Type, |
| 22841 | elem_ty_src: LazySrcLoc, |
| 22842 | ptr: Air.Inst.Ref, |
| 22843 | ptr_src: LazySrcLoc, |
| 22844 | ptr_const: bool, |
| 22845 | ) CompileError!Air.Inst.Ref { |
| 22846 | const pt = sema.pt; |
| 22847 | const zcu = pt.zcu; |
| 22848 | try sema.ensureLayoutResolved(elem_ty, elem_ty_src, .ptr_access); |
| 22849 | var diag: Zcu.AtomicPtrAlignmentDiagnostics = .{}; |
| 22850 | const alignment = zcu.atomicPtrAlignment(elem_ty, &diag) catch |err| switch (err) { |
| 22851 | error.OutOfMemory => |e| return e, |
| 22852 | error.FloatTooBig => return sema.fail( |
| 22853 | block, |
| 22854 | elem_ty_src, |
| 22855 | "expected {d}-bit float type or smaller; found {d}-bit float type", |
| 22856 | .{ diag.max_bits, diag.bits }, |
| 22857 | ), |
| 22858 | error.IntTooBig => return sema.fail( |
| 22859 | block, |
| 22860 | elem_ty_src, |
| 22861 | "expected {d}-bit integer type or smaller; found {d}-bit integer type", |
| 22862 | .{ diag.max_bits, diag.bits }, |
| 22863 | ), |
| 22864 | error.BadType => return sema.fail( |
| 22865 | block, |
| 22866 | elem_ty_src, |
| 22867 | "expected bool, integer, float, enum, packed struct, or pointer type; found '{f}'", |
| 22868 | .{elem_ty.fmt(pt)}, |
| 22869 | ), |
| 22870 | }; |
| 22871 | |
| 22872 | var wanted_ptr_data: InternPool.Key.PtrType = .{ |
| 22873 | .child = elem_ty.toIntern(), |
| 22874 | .flags = .{ |
| 22875 | .alignment = alignment, |
| 22876 | .is_const = ptr_const, |
| 22877 | }, |
| 22878 | }; |
| 22879 | |
| 22880 | const ptr_ty = sema.typeOf(ptr); |
| 22881 | const ptr_data = switch (ptr_ty.zigTypeTag(zcu)) { |
| 22882 | .pointer => ptr_ty.ptrInfo(zcu), |
| 22883 | else => { |
| 22884 | const wanted_ptr_ty = try pt.ptrType(wanted_ptr_data); |
| 22885 | _ = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); |
| 22886 | unreachable; |
| 22887 | }, |
| 22888 | }; |
| 22889 | |
| 22890 | wanted_ptr_data.flags.address_space = ptr_data.flags.address_space; |
| 22891 | wanted_ptr_data.flags.is_allowzero = ptr_data.flags.is_allowzero; |
| 22892 | wanted_ptr_data.flags.is_volatile = ptr_data.flags.is_volatile; |
| 22893 | |
| 22894 | const wanted_ptr_ty = try pt.ptrType(wanted_ptr_data); |
| 22895 | const casted_ptr = try sema.coerce(block, wanted_ptr_ty, ptr, ptr_src); |
| 22896 | |
| 22897 | return casted_ptr; |
| 22898 | } |
| 22899 | |
| 22900 | fn checkPtrIsNotComptimeMutable( |
| 22901 | sema: *Sema, |
| 22902 | block: *Block, |
| 22903 | ptr_val: Value, |
| 22904 | ptr_src: LazySrcLoc, |
| 22905 | operand_src: LazySrcLoc, |
| 22906 | ) CompileError!void { |
| 22907 | _ = operand_src; |
| 22908 | if (sema.isComptimeMutablePtr(ptr_val)) { |
| 22909 | return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{}); |
| 22910 | } |
| 22911 | } |
| 22912 | |
| 22913 | fn checkIntOrVector( |
| 22914 | sema: *Sema, |
| 22915 | block: *Block, |
| 22916 | operand: Air.Inst.Ref, |
| 22917 | operand_src: LazySrcLoc, |
| 22918 | ) CompileError!Type { |
| 22919 | const pt = sema.pt; |
| 22920 | const zcu = pt.zcu; |
| 22921 | const operand_ty = sema.typeOf(operand); |
| 22922 | switch (operand_ty.zigTypeTag(zcu)) { |
| 22923 | .int => return operand_ty, |
| 22924 | .vector => { |
| 22925 | const elem_ty = operand_ty.childType(zcu); |
| 22926 | switch (elem_ty.zigTypeTag(zcu)) { |
| 22927 | .int => return elem_ty, |
| 22928 | else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{f}'", .{ |
| 22929 | elem_ty.fmt(pt), |
| 22930 | }), |
| 22931 | } |
| 22932 | }, |
| 22933 | else => return sema.fail(block, operand_src, "expected integer or vector, found '{f}'", .{ |
| 22934 | operand_ty.fmt(pt), |
| 22935 | }), |
| 22936 | } |
| 22937 | } |
| 22938 | |
| 22939 | fn checkIntOrVectorAllowComptime( |
| 22940 | sema: *Sema, |
| 22941 | block: *Block, |
| 22942 | operand_ty: Type, |
| 22943 | operand_src: LazySrcLoc, |
| 22944 | ) CompileError!Type { |
| 22945 | const pt = sema.pt; |
| 22946 | const zcu = pt.zcu; |
| 22947 | switch (operand_ty.zigTypeTag(zcu)) { |
| 22948 | .int, .comptime_int => return operand_ty, |
| 22949 | .vector => { |
| 22950 | const elem_ty = operand_ty.childType(zcu); |
| 22951 | switch (elem_ty.zigTypeTag(zcu)) { |
| 22952 | .int, .comptime_int => return elem_ty, |
| 22953 | else => return sema.fail(block, operand_src, "expected vector of integers; found vector of '{f}'", .{ |
| 22954 | elem_ty.fmt(pt), |
| 22955 | }), |
| 22956 | } |
| 22957 | }, |
| 22958 | else => return sema.fail(block, operand_src, "expected integer or vector, found '{f}'", .{ |
| 22959 | operand_ty.fmt(pt), |
| 22960 | }), |
| 22961 | } |
| 22962 | } |
| 22963 | |
| 22964 | const SimdBinOp = struct { |
| 22965 | len: ?usize, |
| 22966 | /// Coerced to `result_ty`. |
| 22967 | lhs: Air.Inst.Ref, |
| 22968 | /// Coerced to `result_ty`. |
| 22969 | rhs: Air.Inst.Ref, |
| 22970 | lhs_val: ?Value, |
| 22971 | rhs_val: ?Value, |
| 22972 | /// Only different than `scalar_ty` when it is a vector operation. |
| 22973 | result_ty: Type, |
| 22974 | scalar_ty: Type, |
| 22975 | }; |
| 22976 | |
| 22977 | fn checkSimdBinOp( |
| 22978 | sema: *Sema, |
| 22979 | block: *Block, |
| 22980 | src: LazySrcLoc, |
| 22981 | uncasted_lhs: Air.Inst.Ref, |
| 22982 | uncasted_rhs: Air.Inst.Ref, |
| 22983 | lhs_src: LazySrcLoc, |
| 22984 | rhs_src: LazySrcLoc, |
| 22985 | ) CompileError!SimdBinOp { |
| 22986 | const pt = sema.pt; |
| 22987 | const zcu = pt.zcu; |
| 22988 | const lhs_ty = sema.typeOf(uncasted_lhs); |
| 22989 | const rhs_ty = sema.typeOf(uncasted_rhs); |
| 22990 | |
| 22991 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 22992 | const vec_len: ?usize = if (lhs_ty.zigTypeTag(zcu) == .vector) lhs_ty.vectorLen(zcu) else null; |
| 22993 | const result_ty = try sema.resolvePeerTypes(block, src, &.{ uncasted_lhs, uncasted_rhs }, .{ |
| 22994 | .override = &[_]?LazySrcLoc{ lhs_src, rhs_src }, |
| 22995 | }); |
| 22996 | const lhs = try sema.coerce(block, result_ty, uncasted_lhs, lhs_src); |
| 22997 | const rhs = try sema.coerce(block, result_ty, uncasted_rhs, rhs_src); |
| 22998 | |
| 22999 | return SimdBinOp{ |
| 23000 | .len = vec_len, |
| 23001 | .lhs = lhs, |
| 23002 | .rhs = rhs, |
| 23003 | .lhs_val = sema.resolveValue(lhs), |
| 23004 | .rhs_val = sema.resolveValue(rhs), |
| 23005 | .result_ty = result_ty, |
| 23006 | .scalar_ty = result_ty.scalarType(zcu), |
| 23007 | }; |
| 23008 | } |
| 23009 | |
| 23010 | fn checkVectorizableBinaryOperands( |
| 23011 | sema: *Sema, |
| 23012 | block: *Block, |
| 23013 | src: LazySrcLoc, |
| 23014 | lhs_ty: Type, |
| 23015 | rhs_ty: Type, |
| 23016 | lhs_src: LazySrcLoc, |
| 23017 | rhs_src: LazySrcLoc, |
| 23018 | ) CompileError!void { |
| 23019 | const pt = sema.pt; |
| 23020 | const zcu = pt.zcu; |
| 23021 | const lhs_zig_ty_tag = lhs_ty.zigTypeTag(zcu); |
| 23022 | const rhs_zig_ty_tag = rhs_ty.zigTypeTag(zcu); |
| 23023 | if (lhs_zig_ty_tag != .vector and rhs_zig_ty_tag != .vector) return; |
| 23024 | |
| 23025 | const lhs_is_vector = switch (lhs_zig_ty_tag) { |
| 23026 | .vector, .array => true, |
| 23027 | else => false, |
| 23028 | }; |
| 23029 | const rhs_is_vector = switch (rhs_zig_ty_tag) { |
| 23030 | .vector, .array => true, |
| 23031 | else => false, |
| 23032 | }; |
| 23033 | |
| 23034 | if (lhs_is_vector and rhs_is_vector) { |
| 23035 | const lhs_len = lhs_ty.arrayLen(zcu); |
| 23036 | const rhs_len = rhs_ty.arrayLen(zcu); |
| 23037 | if (lhs_len != rhs_len) { |
| 23038 | const msg = msg: { |
| 23039 | const msg = try sema.errMsg(src, "vector length mismatch", .{}); |
| 23040 | errdefer msg.destroy(sema.gpa); |
| 23041 | try sema.errNote(lhs_src, msg, "length {d} here", .{lhs_len}); |
| 23042 | try sema.errNote(rhs_src, msg, "length {d} here", .{rhs_len}); |
| 23043 | break :msg msg; |
| 23044 | }; |
| 23045 | return sema.failWithOwnedErrorMsg(block, msg); |
| 23046 | } |
| 23047 | } else { |
| 23048 | const msg = msg: { |
| 23049 | const msg = try sema.errMsg(src, "mixed scalar and vector operands: '{f}' and '{f}'", .{ |
| 23050 | lhs_ty.fmt(pt), rhs_ty.fmt(pt), |
| 23051 | }); |
| 23052 | errdefer msg.destroy(sema.gpa); |
| 23053 | if (lhs_is_vector) { |
| 23054 | try sema.errNote(lhs_src, msg, "vector here", .{}); |
| 23055 | try sema.errNote(rhs_src, msg, "scalar here", .{}); |
| 23056 | } else { |
| 23057 | try sema.errNote(lhs_src, msg, "scalar here", .{}); |
| 23058 | try sema.errNote(rhs_src, msg, "vector here", .{}); |
| 23059 | } |
| 23060 | break :msg msg; |
| 23061 | }; |
| 23062 | return sema.failWithOwnedErrorMsg(block, msg); |
| 23063 | } |
| 23064 | } |
| 23065 | |
| 23066 | fn checkAllScalarsDefined(sema: *Sema, block: *Block, src: LazySrcLoc, val: Value) CompileError!void { |
| 23067 | const zcu = sema.pt.zcu; |
| 23068 | switch (zcu.intern_pool.indexToKey(val.toIntern())) { |
| 23069 | .int, .float => {}, |
| 23070 | .undef => return sema.failWithUseOfUndef(block, src, null), |
| 23071 | .aggregate => |agg| { |
| 23072 | assert(Type.fromInterned(agg.ty).zigTypeTag(zcu) == .vector); |
| 23073 | for (agg.storage.values(), 0..) |elem_val, elem_idx| { |
| 23074 | if (Value.fromInterned(elem_val).isUndef(zcu)) |
| 23075 | return sema.failWithUseOfUndef(block, src, elem_idx); |
| 23076 | } |
| 23077 | }, |
| 23078 | else => unreachable, |
| 23079 | } |
| 23080 | } |
| 23081 | |
| 23082 | fn resolveExportOptions( |
| 23083 | sema: *Sema, |
| 23084 | block: *Block, |
| 23085 | src: LazySrcLoc, |
| 23086 | zir_ref: Zir.Inst.Ref, |
| 23087 | ) CompileError!Zcu.Export.Options { |
| 23088 | const pt = sema.pt; |
| 23089 | const zcu = pt.zcu; |
| 23090 | const comp = zcu.comp; |
| 23091 | const gpa = comp.gpa; |
| 23092 | const io = comp.io; |
| 23093 | const ip = &zcu.intern_pool; |
| 23094 | |
| 23095 | const export_options_ty = try sema.getStdLangType(src, .ExportOptions); |
| 23096 | const air_ref = sema.resolveInst(zir_ref); |
| 23097 | const options = try sema.coerce(block, export_options_ty, air_ref, src); |
| 23098 | |
| 23099 | const name_src = block.src(.{ .init_field_name = src.offset.node_offset_builtin_call_arg.builtin_call_node }); |
| 23100 | const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node }); |
| 23101 | const section_src = block.src(.{ .init_field_section = src.offset.node_offset_builtin_call_arg.builtin_call_node }); |
| 23102 | const visibility_src = block.src(.{ .init_field_visibility = src.offset.node_offset_builtin_call_arg.builtin_call_node }); |
| 23103 | |
| 23104 | const name_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "name", .no_embedded_nulls), name_src); |
| 23105 | const name = try sema.toConstString(block, name_src, name_operand, .{ .simple = .export_options }); |
| 23106 | |
| 23107 | const linkage_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "linkage", .no_embedded_nulls), linkage_src); |
| 23108 | const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_operand, .{ .simple = .export_options }); |
| 23109 | const linkage = try sema.interpretStdLangType(block, linkage_src, linkage_val, std.lang.GlobalLinkage); |
| 23110 | |
| 23111 | const section_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "section", .no_embedded_nulls), section_src); |
| 23112 | const section_opt_val = try sema.resolveConstDefinedValue(block, section_src, section_operand, .{ .simple = .export_options }); |
| 23113 | const section = if (section_opt_val.optionalValue(zcu)) |section_val| |
| 23114 | try sema.toConstString(block, section_src, Air.internedToRef(section_val.toIntern()), .{ .simple = .export_options }) |
| 23115 | else |
| 23116 | null; |
| 23117 | |
| 23118 | const visibility_operand = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "visibility", .no_embedded_nulls), visibility_src); |
| 23119 | const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_operand, .{ .simple = .export_options }); |
| 23120 | const visibility = try sema.interpretStdLangType(block, visibility_src, visibility_val, std.lang.SymbolVisibility); |
| 23121 | |
| 23122 | if (name.len < 1) { |
| 23123 | return sema.fail(block, name_src, "exported symbol name cannot be empty", .{}); |
| 23124 | } |
| 23125 | |
| 23126 | if (visibility != .default and linkage == .internal) { |
| 23127 | return sema.fail(block, visibility_src, "symbol '{s}' exported with internal linkage has non-default visibility {s}", .{ |
| 23128 | name, @tagName(visibility), |
| 23129 | }); |
| 23130 | } |
| 23131 | |
| 23132 | return .{ |
| 23133 | .name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls), |
| 23134 | .linkage = linkage, |
| 23135 | .section = try ip.getOrPutStringOpt(gpa, io, pt.tid, section, .no_embedded_nulls), |
| 23136 | .visibility = visibility, |
| 23137 | }; |
| 23138 | } |
| 23139 | |
| 23140 | fn resolveStdLangEnum( |
| 23141 | sema: *Sema, |
| 23142 | block: *Block, |
| 23143 | src: LazySrcLoc, |
| 23144 | zir_ref: Zir.Inst.Ref, |
| 23145 | comptime name: Zcu.StdLangDecl, |
| 23146 | reason: ComptimeReason, |
| 23147 | ) CompileError!@field(std.lang, @tagName(name)) { |
| 23148 | const ty = try sema.getStdLangType(src, name); |
| 23149 | const air_ref = sema.resolveInst(zir_ref); |
| 23150 | const coerced = try sema.coerce(block, ty, air_ref, src); |
| 23151 | const val = try sema.resolveConstDefinedValue(block, src, coerced, reason); |
| 23152 | return sema.interpretStdLangType(block, src, val, @field(std.lang, @tagName(name))); |
| 23153 | } |
| 23154 | |
| 23155 | fn resolveAtomicOrder( |
| 23156 | sema: *Sema, |
| 23157 | block: *Block, |
| 23158 | src: LazySrcLoc, |
| 23159 | zir_ref: Zir.Inst.Ref, |
| 23160 | reason: ComptimeReason, |
| 23161 | ) CompileError!std.lang.AtomicOrder { |
| 23162 | return sema.resolveStdLangEnum(block, src, zir_ref, .AtomicOrder, reason); |
| 23163 | } |
| 23164 | |
| 23165 | fn resolveAtomicRmwOp( |
| 23166 | sema: *Sema, |
| 23167 | block: *Block, |
| 23168 | src: LazySrcLoc, |
| 23169 | zir_ref: Zir.Inst.Ref, |
| 23170 | ) CompileError!std.lang.AtomicRmwOp { |
| 23171 | return sema.resolveStdLangEnum(block, src, zir_ref, .AtomicRmwOp, .{ .simple = .operand_atomicRmw_operation }); |
| 23172 | } |
| 23173 | |
| 23174 | fn zirCmpxchg( |
| 23175 | sema: *Sema, |
| 23176 | block: *Block, |
| 23177 | extended: Zir.Inst.Extended.InstData, |
| 23178 | ) CompileError!Air.Inst.Ref { |
| 23179 | const pt = sema.pt; |
| 23180 | const zcu = pt.zcu; |
| 23181 | const extra = sema.code.extraData(Zir.Inst.Cmpxchg, extended.operand).data; |
| 23182 | const air_tag: Air.Inst.Tag = switch (extended.small) { |
| 23183 | 0 => .cmpxchg_weak, |
| 23184 | 1 => .cmpxchg_strong, |
| 23185 | else => unreachable, |
| 23186 | }; |
| 23187 | const src = block.nodeOffset(extra.node); |
| 23188 | // zig fmt: off |
| 23189 | const elem_ty_src = block.builtinCallArgSrc(extra.node, 0); |
| 23190 | const ptr_src = block.builtinCallArgSrc(extra.node, 1); |
| 23191 | const expected_src = block.builtinCallArgSrc(extra.node, 2); |
| 23192 | const new_value_src = block.builtinCallArgSrc(extra.node, 3); |
| 23193 | const success_order_src = block.builtinCallArgSrc(extra.node, 4); |
| 23194 | const failure_order_src = block.builtinCallArgSrc(extra.node, 5); |
| 23195 | // zig fmt: on |
| 23196 | const expected_value = sema.resolveInst(extra.expected_value); |
| 23197 | const elem_ty = sema.typeOf(expected_value); |
| 23198 | if (elem_ty.zigTypeTag(zcu) == .float) { |
| 23199 | return sema.fail( |
| 23200 | block, |
| 23201 | elem_ty_src, |
| 23202 | "expected bool, integer, enum, packed struct, or pointer type; found '{f}'", |
| 23203 | .{elem_ty.fmt(pt)}, |
| 23204 | ); |
| 23205 | } |
| 23206 | const uncasted_ptr = sema.resolveInst(extra.ptr); |
| 23207 | const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); |
| 23208 | const new_value = try sema.coerce(block, elem_ty, sema.resolveInst(extra.new_value), new_value_src); |
| 23209 | const success_order = try sema.resolveAtomicOrder(block, success_order_src, extra.success_order, .{ .simple = .atomic_order }); |
| 23210 | const failure_order = try sema.resolveAtomicOrder(block, failure_order_src, extra.failure_order, .{ .simple = .atomic_order }); |
| 23211 | |
| 23212 | if (@backingInt(success_order) < @backingInt(std.lang.AtomicOrder.monotonic)) { |
| 23213 | return sema.fail(block, success_order_src, "success atomic ordering must be monotonic or stricter", .{}); |
| 23214 | } |
| 23215 | if (@backingInt(failure_order) < @backingInt(std.lang.AtomicOrder.monotonic)) { |
| 23216 | return sema.fail(block, failure_order_src, "failure atomic ordering must be monotonic or stricter", .{}); |
| 23217 | } |
| 23218 | if (@backingInt(failure_order) > @backingInt(success_order)) { |
| 23219 | return sema.fail(block, failure_order_src, "failure atomic ordering must be no stricter than success", .{}); |
| 23220 | } |
| 23221 | if (failure_order == .release or failure_order == .acq_rel) { |
| 23222 | return sema.fail(block, failure_order_src, "failure atomic ordering must not be release or acq_rel", .{}); |
| 23223 | } |
| 23224 | |
| 23225 | const result_ty = try pt.optionalType(elem_ty.toIntern()); |
| 23226 | |
| 23227 | // special case zero bit types |
| 23228 | if (elem_ty.classify(zcu) == .one_possible_value) { |
| 23229 | return .fromValue(try pt.nullValue(result_ty)); |
| 23230 | } |
| 23231 | |
| 23232 | const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { |
| 23233 | if (sema.resolveValue(expected_value)) |expected_val| { |
| 23234 | if (sema.resolveValue(new_value)) |new_val| { |
| 23235 | if (expected_val.isUndef(zcu) or new_val.isUndef(zcu)) { |
| 23236 | // TODO: this should probably cause the memory stored at the pointer |
| 23237 | // to become undef as well |
| 23238 | return pt.undefRef(result_ty); |
| 23239 | } |
| 23240 | const ptr_ty = sema.typeOf(ptr); |
| 23241 | const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; |
| 23242 | const result_val = try pt.intern(.{ .opt = .{ |
| 23243 | .ty = result_ty.toIntern(), |
| 23244 | .val = if (stored_val.eql(expected_val, elem_ty, zcu)) blk: { |
| 23245 | try sema.storePtr(block, src, ptr, new_value); |
| 23246 | break :blk .none; |
| 23247 | } else stored_val.toIntern(), |
| 23248 | } }); |
| 23249 | return Air.internedToRef(result_val); |
| 23250 | } else break :rs new_value_src; |
| 23251 | } else break :rs expected_src; |
| 23252 | } else ptr_src; |
| 23253 | |
| 23254 | const flags: u32 = @as(u32, @backingInt(success_order)) | |
| 23255 | (@as(u32, @backingInt(failure_order)) << 3); |
| 23256 | |
| 23257 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 23258 | return block.addInst(.{ |
| 23259 | .tag = air_tag, |
| 23260 | .data = .{ .ty_pl = .{ |
| 23261 | .ty = result_ty, |
| 23262 | .payload = try sema.addExtra(Air.Cmpxchg{ |
| 23263 | .ptr = ptr, |
| 23264 | .expected_value = expected_value, |
| 23265 | .new_value = new_value, |
| 23266 | .flags = flags, |
| 23267 | }), |
| 23268 | } }, |
| 23269 | }); |
| 23270 | } |
| 23271 | |
| 23272 | fn zirSplat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 23273 | const pt = sema.pt; |
| 23274 | const zcu = pt.zcu; |
| 23275 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 23276 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 23277 | const src = block.nodeOffset(inst_data.src_node); |
| 23278 | const scalar_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 23279 | const dest_ty = try sema.resolveDestType(block, src, extra.lhs, .remove_eu_opt, "@splat"); |
| 23280 | |
| 23281 | switch (dest_ty.zigTypeTag(zcu)) { |
| 23282 | .array, .vector => {}, |
| 23283 | else => return sema.fail(block, src, "expected array or vector type, found '{f}'", .{dest_ty.fmt(pt)}), |
| 23284 | } |
| 23285 | |
| 23286 | const operand = sema.resolveInst(extra.rhs); |
| 23287 | const scalar_ty = dest_ty.childType(zcu); |
| 23288 | const scalar = try sema.coerce(block, scalar_ty, operand, scalar_src); |
| 23289 | |
| 23290 | const len = try sema.usizeCast(block, src, dest_ty.arrayLen(zcu)); |
| 23291 | |
| 23292 | // If the length is 0, the result is comptime-known even if the operand isn't. |
| 23293 | if (len == 0) return .fromValue(try pt.aggregateValue(dest_ty, &.{})); |
| 23294 | |
| 23295 | const maybe_sentinel = dest_ty.sentinel(zcu); |
| 23296 | |
| 23297 | if (sema.resolveValue(scalar)) |scalar_val| { |
| 23298 | full: { |
| 23299 | if (dest_ty.zigTypeTag(zcu) == .vector) break :full; |
| 23300 | const sentinel = maybe_sentinel orelse break :full; |
| 23301 | if (sentinel.toIntern() == scalar_val.toIntern()) break :full; |
| 23302 | // This is a array with non-zero length and a sentinel which does not match the element. |
| 23303 | // We have to use the full `elems` representation. |
| 23304 | const elems = try sema.arena.alloc(InternPool.Index, len + 1); |
| 23305 | @memset(elems[0..len], scalar_val.toIntern()); |
| 23306 | elems[len] = sentinel.toIntern(); |
| 23307 | return .fromValue(try pt.aggregateValue(dest_ty, elems)); |
| 23308 | } |
| 23309 | return .fromValue(try pt.aggregateSplatValue(dest_ty, scalar_val)); |
| 23310 | } |
| 23311 | |
| 23312 | try sema.requireRuntimeBlock(block, src, scalar_src); |
| 23313 | |
| 23314 | switch (dest_ty.zigTypeTag(zcu)) { |
| 23315 | .vector, .array => return block.addTyOp(.splat, dest_ty, scalar), |
| 23316 | else => unreachable, |
| 23317 | } |
| 23318 | } |
| 23319 | |
| 23320 | fn zirReduce(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 23321 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 23322 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 23323 | const op_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 23324 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 23325 | const operation = try sema.resolveStdLangEnum(block, op_src, extra.lhs, .ReduceOp, .{ .simple = .operand_reduce_operation }); |
| 23326 | const operand = sema.resolveInst(extra.rhs); |
| 23327 | const operand_ty = sema.typeOf(operand); |
| 23328 | const pt = sema.pt; |
| 23329 | const zcu = pt.zcu; |
| 23330 | |
| 23331 | if (operand_ty.zigTypeTag(zcu) != .vector) { |
| 23332 | return sema.fail(block, operand_src, "expected vector, found '{f}'", .{operand_ty.fmt(pt)}); |
| 23333 | } |
| 23334 | |
| 23335 | const scalar_ty = operand_ty.childType(zcu); |
| 23336 | |
| 23337 | // Type-check depending on operation. |
| 23338 | switch (operation) { |
| 23339 | .And, .Or, .Xor => switch (scalar_ty.zigTypeTag(zcu)) { |
| 23340 | .int, .bool => {}, |
| 23341 | else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or boolean operand; found '{f}'", .{ |
| 23342 | @tagName(operation), operand_ty.fmt(pt), |
| 23343 | }), |
| 23344 | }, |
| 23345 | .Min, .Max, .Add, .Mul => switch (scalar_ty.zigTypeTag(zcu)) { |
| 23346 | .int, .float => {}, |
| 23347 | else => return sema.fail(block, operand_src, "@reduce operation '{s}' requires integer or float operand; found '{f}'", .{ |
| 23348 | @tagName(operation), operand_ty.fmt(pt), |
| 23349 | }), |
| 23350 | }, |
| 23351 | } |
| 23352 | |
| 23353 | const vec_len = operand_ty.vectorLen(zcu); |
| 23354 | if (vec_len == 0) { |
| 23355 | // TODO re-evaluate if we should introduce a "neutral value" for some operations, |
| 23356 | // e.g. zero for add and one for mul. |
| 23357 | return sema.fail(block, operand_src, "@reduce operation requires a vector with nonzero length", .{}); |
| 23358 | } |
| 23359 | |
| 23360 | if (sema.resolveValue(operand)) |operand_val| { |
| 23361 | if (operand_val.isUndef(zcu)) return pt.undefRef(scalar_ty); |
| 23362 | |
| 23363 | var accum: Value = try operand_val.elemValue(pt, 0); |
| 23364 | var i: u32 = 1; |
| 23365 | while (i < vec_len) : (i += 1) { |
| 23366 | const elem_val = try operand_val.elemValue(pt, i); |
| 23367 | accum = switch (operation) { |
| 23368 | // zig fmt: off |
| 23369 | .And => try arith.bitwiseBin (sema, scalar_ty, accum, elem_val, .@"and"), |
| 23370 | .Or => try arith.bitwiseBin (sema, scalar_ty, accum, elem_val, .@"or"), |
| 23371 | .Xor => try arith.bitwiseBin (sema, scalar_ty, accum, elem_val, .xor), |
| 23372 | .Min => Value.numberMin ( accum, elem_val, zcu), |
| 23373 | .Max => Value.numberMax ( accum, elem_val, zcu), |
| 23374 | .Add => try arith.addMaybeWrap(sema, scalar_ty, accum, elem_val), |
| 23375 | .Mul => try arith.mulMaybeWrap(sema, scalar_ty, accum, elem_val), |
| 23376 | // zig fmt: on |
| 23377 | }; |
| 23378 | } |
| 23379 | return Air.internedToRef(accum.toIntern()); |
| 23380 | } |
| 23381 | |
| 23382 | try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), operand_src); |
| 23383 | return block.addReduce(operand, operation); |
| 23384 | } |
| 23385 | |
| 23386 | fn zirShuffle(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 23387 | const pt = sema.pt; |
| 23388 | const zcu = pt.zcu; |
| 23389 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 23390 | const extra = sema.code.extraData(Zir.Inst.Shuffle, inst_data.payload_index).data; |
| 23391 | const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 23392 | const mask_src = block.builtinCallArgSrc(inst_data.src_node, 3); |
| 23393 | |
| 23394 | const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); |
| 23395 | try sema.checkVectorElemType(block, elem_ty_src, elem_ty); |
| 23396 | const a = sema.resolveInst(extra.a); |
| 23397 | const b = sema.resolveInst(extra.b); |
| 23398 | var mask = sema.resolveInst(extra.mask); |
| 23399 | var mask_ty = sema.typeOf(mask); |
| 23400 | |
| 23401 | const mask_len = switch (sema.typeOf(mask).zigTypeTag(zcu)) { |
| 23402 | .array, .vector => sema.typeOf(mask).arrayLen(zcu), |
| 23403 | else => return sema.fail(block, mask_src, "expected vector or array, found '{f}'", .{sema.typeOf(mask).fmt(pt)}), |
| 23404 | }; |
| 23405 | mask_ty = try pt.vectorType(.{ |
| 23406 | .len = @intCast(mask_len), |
| 23407 | .child = .i32_type, |
| 23408 | }); |
| 23409 | mask = try sema.coerce(block, mask_ty, mask, mask_src); |
| 23410 | const mask_val = try sema.resolveConstValue(block, mask_src, mask, .{ .simple = .operand_shuffle_mask }); |
| 23411 | return sema.analyzeShuffle(block, inst_data.src_node, elem_ty, a, b, mask_val, @intCast(mask_len)); |
| 23412 | } |
| 23413 | |
| 23414 | fn analyzeShuffle( |
| 23415 | sema: *Sema, |
| 23416 | block: *Block, |
| 23417 | src_node: std.zig.Ast.Node.Offset, |
| 23418 | elem_ty: Type, |
| 23419 | a_uncoerced: Air.Inst.Ref, |
| 23420 | b_uncoerced: Air.Inst.Ref, |
| 23421 | mask: Value, |
| 23422 | mask_len: u32, |
| 23423 | ) CompileError!Air.Inst.Ref { |
| 23424 | const pt = sema.pt; |
| 23425 | const zcu = pt.zcu; |
| 23426 | const a_src = block.builtinCallArgSrc(src_node, 1); |
| 23427 | const b_src = block.builtinCallArgSrc(src_node, 2); |
| 23428 | const mask_src = block.builtinCallArgSrc(src_node, 3); |
| 23429 | |
| 23430 | // If the type of `a` is `@TypeOf(undefined)`, i.e. the argument is untyped, |
| 23431 | // this is 0, because it is an error to index into this vector. |
| 23432 | const a_len: u32 = switch (sema.typeOf(a_uncoerced).zigTypeTag(zcu)) { |
| 23433 | .array, .vector => @intCast(sema.typeOf(a_uncoerced).arrayLen(zcu)), |
| 23434 | .undefined => 0, |
| 23435 | else => return sema.fail(block, a_src, "expected vector of '{f}', found '{f}'", .{ |
| 23436 | elem_ty.fmt(pt), sema.typeOf(a_uncoerced).fmt(pt), |
| 23437 | }), |
| 23438 | }; |
| 23439 | const a_ty = try pt.vectorType(.{ .len = a_len, .child = elem_ty.toIntern() }); |
| 23440 | const a_coerced = try sema.coerce(block, a_ty, a_uncoerced, a_src); |
| 23441 | |
| 23442 | // If the type of `b` is `@TypeOf(undefined)`, i.e. the argument is untyped, this is 0, because it is an error to index into this vector. |
| 23443 | const b_len: u32 = switch (sema.typeOf(b_uncoerced).zigTypeTag(zcu)) { |
| 23444 | .array, .vector => @intCast(sema.typeOf(b_uncoerced).arrayLen(zcu)), |
| 23445 | .undefined => 0, |
| 23446 | else => return sema.fail(block, b_src, "expected vector of '{f}', found '{f}'", .{ |
| 23447 | elem_ty.fmt(pt), sema.typeOf(b_uncoerced).fmt(pt), |
| 23448 | }), |
| 23449 | }; |
| 23450 | const b_ty = try pt.vectorType(.{ .len = b_len, .child = elem_ty.toIntern() }); |
| 23451 | const b_coerced = try sema.coerce(block, b_ty, b_uncoerced, b_src); |
| 23452 | |
| 23453 | const result_ty = try pt.vectorType(.{ .len = mask_len, .child = elem_ty.toIntern() }); |
| 23454 | |
| 23455 | // We're going to pre-emptively reserve space in `sema.air_extra`. The reason for this is we need |
| 23456 | // a `u32` buffer of length `mask_len` anyway, and putting it in `sema.air_extra` avoids a copy |
| 23457 | // in the runtime case. If the result is comptime-known, we'll shrink `air_extra` back. |
| 23458 | const air_extra_idx: u32 = @intCast(sema.air_extra.items.len); |
| 23459 | const air_mask_buf = try sema.air_extra.addManyAsSlice(sema.gpa, mask_len); |
| 23460 | |
| 23461 | // We want to interpret that buffer in `air_extra` in a few ways. Initially, we'll consider its |
| 23462 | // elements as `Air.Inst.ShuffleTwoMask`, essentially representing the raw mask values; then, we'll |
| 23463 | // convert it to `InternPool.Index` or `Air.Inst.ShuffleOneMask` if there are comptime-known operands. |
| 23464 | const mask_ip_index: []InternPool.Index = @ptrCast(air_mask_buf); |
| 23465 | const mask_shuffle_one: []Air.ShuffleOneMask = @ptrCast(air_mask_buf); |
| 23466 | const mask_shuffle_two: []Air.ShuffleTwoMask = @ptrCast(air_mask_buf); |
| 23467 | |
| 23468 | // Initial loop: check mask elements, populate `mask_shuffle_two`. |
| 23469 | var a_used = false; |
| 23470 | var b_used = false; |
| 23471 | for (mask_shuffle_two, 0..mask_len) |*out, mask_idx| { |
| 23472 | const mask_val = try mask.elemValue(pt, mask_idx); |
| 23473 | if (mask_val.isUndef(zcu)) { |
| 23474 | out.* = .undef; |
| 23475 | continue; |
| 23476 | } |
| 23477 | // Safe because mask elements are `i32` and we already checked for undef: |
| 23478 | const raw = mask_val.toSignedInt(zcu); |
| 23479 | if (raw >= 0) { |
| 23480 | const idx: u32 = @intCast(raw); |
| 23481 | a_used = true; |
| 23482 | out.* = .aElem(idx); |
| 23483 | if (idx >= a_len) return sema.failWithOwnedErrorMsg(block, msg: { |
| 23484 | const msg = try sema.errMsg(mask_src, "mask element at index '{d}' selects out-of-bounds index", .{mask_idx}); |
| 23485 | errdefer msg.destroy(sema.gpa); |
| 23486 | try sema.errNote(a_src, msg, "index '{d}' exceeds bounds of '{f}' given here", .{ idx, a_ty.fmt(pt) }); |
| 23487 | if (idx < b_len) { |
| 23488 | try sema.errNote(b_src, msg, "use '~@as(u32, {d})' to index into second vector given here", .{idx}); |
| 23489 | } |
| 23490 | break :msg msg; |
| 23491 | }); |
| 23492 | } else { |
| 23493 | const idx: u32 = @intCast(~raw); |
| 23494 | b_used = true; |
| 23495 | out.* = .bElem(idx); |
| 23496 | if (idx >= b_len) return sema.failWithOwnedErrorMsg(block, msg: { |
| 23497 | const msg = try sema.errMsg(mask_src, "mask element at index '{d}' selects out-of-bounds index", .{mask_idx}); |
| 23498 | errdefer msg.destroy(sema.gpa); |
| 23499 | try sema.errNote(b_src, msg, "index '{d}' exceeds bounds of '{f}' given here", .{ idx, b_ty.fmt(pt) }); |
| 23500 | break :msg msg; |
| 23501 | }); |
| 23502 | } |
| 23503 | } |
| 23504 | |
| 23505 | const maybe_a_val = sema.resolveValue(a_coerced); |
| 23506 | const maybe_b_val = sema.resolveValue(b_coerced); |
| 23507 | |
| 23508 | const a_rt = a_used and maybe_a_val == null; |
| 23509 | const b_rt = b_used and maybe_b_val == null; |
| 23510 | |
| 23511 | if (a_rt and b_rt) { |
| 23512 | // Both operands are needed and runtime-known. We need a `[]ShuffleTwomask`... which is |
| 23513 | // exactly what we already have in `mask_shuffle_two`! So, we're basically done already. |
| 23514 | // We just need to append the two operands. |
| 23515 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, 2); |
| 23516 | sema.appendRefsAssumeCapacity(&.{ a_coerced, b_coerced }); |
| 23517 | return block.addInst(.{ |
| 23518 | .tag = .shuffle_two, |
| 23519 | .data = .{ .ty_pl = .{ |
| 23520 | .ty = result_ty, |
| 23521 | .payload = air_extra_idx, |
| 23522 | } }, |
| 23523 | }); |
| 23524 | } else if (a_rt) { |
| 23525 | // We need to convert the `ShuffleTwoMask` values to `ShuffleOneMask`. |
| 23526 | for (mask_shuffle_two, mask_shuffle_one) |in, *out| { |
| 23527 | out.* = switch (in.unwrap()) { |
| 23528 | .undef => .value(try pt.undefValue(elem_ty)), |
| 23529 | .a_elem => |idx| .elem(idx), |
| 23530 | .b_elem => |idx| .value(try maybe_b_val.?.elemValue(pt, idx)), |
| 23531 | }; |
| 23532 | } |
| 23533 | // Now just append our single runtime operand, and we're done. |
| 23534 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, 1); |
| 23535 | sema.appendRefsAssumeCapacity(&.{a_coerced}); |
| 23536 | return block.addInst(.{ |
| 23537 | .tag = .shuffle_one, |
| 23538 | .data = .{ .ty_pl = .{ |
| 23539 | .ty = result_ty, |
| 23540 | .payload = air_extra_idx, |
| 23541 | } }, |
| 23542 | }); |
| 23543 | } else if (b_rt) { |
| 23544 | // We need to convert the `ShuffleTwoMask` values to `ShuffleOneMask`. |
| 23545 | for (mask_shuffle_two, mask_shuffle_one) |in, *out| { |
| 23546 | out.* = switch (in.unwrap()) { |
| 23547 | .undef => .value(try pt.undefValue(elem_ty)), |
| 23548 | .a_elem => |idx| .value(try maybe_a_val.?.elemValue(pt, idx)), |
| 23549 | .b_elem => |idx| .elem(idx), |
| 23550 | }; |
| 23551 | } |
| 23552 | // Now just append our single runtime operand, and we're done. |
| 23553 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, 1); |
| 23554 | sema.appendRefsAssumeCapacity(&.{b_coerced}); |
| 23555 | return block.addInst(.{ |
| 23556 | .tag = .shuffle_one, |
| 23557 | .data = .{ .ty_pl = .{ |
| 23558 | .ty = result_ty, |
| 23559 | .payload = air_extra_idx, |
| 23560 | } }, |
| 23561 | }); |
| 23562 | } else { |
| 23563 | // The result will be comptime-known. We must convert the `ShuffleTwoMask` values to |
| 23564 | // `InternPool.Index` values using the known operands. |
| 23565 | for (mask_shuffle_two, mask_ip_index) |in, *out| { |
| 23566 | const val: Value = switch (in.unwrap()) { |
| 23567 | // Special case zero bit types: there is no undefined value for OPV elements. |
| 23568 | // Only affects the case where `!a_rt and !b_rt` since `a_coerced` and `b_coerced`'s types are also OPV for OPV elements. |
| 23569 | .undef => try elem_ty.onePossibleValue(pt) orelse try pt.undefValue(elem_ty), |
| 23570 | .a_elem => |idx| try maybe_a_val.?.elemValue(pt, idx), |
| 23571 | .b_elem => |idx| try maybe_b_val.?.elemValue(pt, idx), |
| 23572 | }; |
| 23573 | out.* = val.toIntern(); |
| 23574 | } |
| 23575 | const res = try pt.aggregateValue(result_ty, mask_ip_index); |
| 23576 | // We have a comptime-known result, so didn't need `air_mask_buf` -- remove it from `sema.air_extra`. |
| 23577 | assert(sema.air_extra.items.len == air_extra_idx + air_mask_buf.len); |
| 23578 | sema.air_extra.shrinkRetainingCapacity(air_extra_idx); |
| 23579 | return Air.internedToRef(res.toIntern()); |
| 23580 | } |
| 23581 | } |
| 23582 | |
| 23583 | fn zirSelect(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 23584 | const pt = sema.pt; |
| 23585 | const zcu = pt.zcu; |
| 23586 | const extra = sema.code.extraData(Zir.Inst.Select, extended.operand).data; |
| 23587 | |
| 23588 | const src = block.nodeOffset(extra.node); |
| 23589 | const elem_ty_src = block.builtinCallArgSrc(extra.node, 0); |
| 23590 | const pred_src = block.builtinCallArgSrc(extra.node, 1); |
| 23591 | const a_src = block.builtinCallArgSrc(extra.node, 2); |
| 23592 | const b_src = block.builtinCallArgSrc(extra.node, 3); |
| 23593 | |
| 23594 | const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); |
| 23595 | try sema.checkVectorElemType(block, elem_ty_src, elem_ty); |
| 23596 | const pred_uncoerced = sema.resolveInst(extra.pred); |
| 23597 | const pred_ty = sema.typeOf(pred_uncoerced); |
| 23598 | |
| 23599 | const vec_len_u64 = switch (pred_ty.zigTypeTag(zcu)) { |
| 23600 | .vector, .array => pred_ty.arrayLen(zcu), |
| 23601 | else => return sema.fail(block, pred_src, "expected vector or array, found '{f}'", .{pred_ty.fmt(pt)}), |
| 23602 | }; |
| 23603 | const vec_len: u32 = @intCast(try sema.usizeCast(block, pred_src, vec_len_u64)); |
| 23604 | |
| 23605 | const bool_vec_ty = try pt.vectorType(.{ |
| 23606 | .len = vec_len, |
| 23607 | .child = .bool_type, |
| 23608 | }); |
| 23609 | const pred = try sema.coerce(block, bool_vec_ty, pred_uncoerced, pred_src); |
| 23610 | |
| 23611 | const vec_ty = try pt.vectorType(.{ |
| 23612 | .len = vec_len, |
| 23613 | .child = elem_ty.toIntern(), |
| 23614 | }); |
| 23615 | const a = try sema.coerce(block, vec_ty, sema.resolveInst(extra.a), a_src); |
| 23616 | const b = try sema.coerce(block, vec_ty, sema.resolveInst(extra.b), b_src); |
| 23617 | |
| 23618 | // special case zero bit types |
| 23619 | if (try vec_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 23620 | |
| 23621 | const maybe_pred = sema.resolveValue(pred); |
| 23622 | const maybe_a = sema.resolveValue(a); |
| 23623 | const maybe_b = sema.resolveValue(b); |
| 23624 | |
| 23625 | const runtime_src = if (maybe_pred) |pred_val| rs: { |
| 23626 | if (pred_val.isUndef(zcu)) return pt.undefRef(vec_ty); |
| 23627 | |
| 23628 | if (maybe_a) |a_val| { |
| 23629 | if (a_val.isUndef(zcu)) return pt.undefRef(vec_ty); |
| 23630 | |
| 23631 | if (maybe_b) |b_val| { |
| 23632 | if (b_val.isUndef(zcu)) return pt.undefRef(vec_ty); |
| 23633 | |
| 23634 | const elems = try sema.gpa.alloc(InternPool.Index, vec_len); |
| 23635 | defer sema.gpa.free(elems); |
| 23636 | for (elems, 0..) |*elem, i| { |
| 23637 | const pred_elem_val = try pred_val.elemValue(pt, i); |
| 23638 | const should_choose_a = pred_elem_val.toBool(); |
| 23639 | elem.* = (try (if (should_choose_a) a_val else b_val).elemValue(pt, i)).toIntern(); |
| 23640 | } |
| 23641 | |
| 23642 | return Air.internedToRef((try pt.aggregateValue(vec_ty, elems)).toIntern()); |
| 23643 | } else { |
| 23644 | break :rs b_src; |
| 23645 | } |
| 23646 | } else { |
| 23647 | if (maybe_b) |b_val| { |
| 23648 | if (b_val.isUndef(zcu)) return pt.undefRef(vec_ty); |
| 23649 | } |
| 23650 | break :rs a_src; |
| 23651 | } |
| 23652 | } else rs: { |
| 23653 | if (maybe_a) |a_val| { |
| 23654 | if (a_val.isUndef(zcu)) return pt.undefRef(vec_ty); |
| 23655 | } |
| 23656 | if (maybe_b) |b_val| { |
| 23657 | if (b_val.isUndef(zcu)) return pt.undefRef(vec_ty); |
| 23658 | } |
| 23659 | break :rs pred_src; |
| 23660 | }; |
| 23661 | |
| 23662 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 23663 | return block.addInst(.{ |
| 23664 | .tag = .select, |
| 23665 | .data = .{ .pl_op = .{ |
| 23666 | .operand = pred, |
| 23667 | .payload = try block.sema.addExtra(Air.Bin{ |
| 23668 | .lhs = a, |
| 23669 | .rhs = b, |
| 23670 | }), |
| 23671 | } }, |
| 23672 | }); |
| 23673 | } |
| 23674 | |
| 23675 | fn zirAtomicLoad(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 23676 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 23677 | const extra = sema.code.extraData(Zir.Inst.AtomicLoad, inst_data.payload_index).data; |
| 23678 | // zig fmt: off |
| 23679 | const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 23680 | const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 23681 | const order_src = block.builtinCallArgSrc(inst_data.src_node, 2); |
| 23682 | // zig fmt: on |
| 23683 | const elem_ty = try sema.resolveType(block, elem_ty_src, extra.elem_type); |
| 23684 | const uncasted_ptr = sema.resolveInst(extra.ptr); |
| 23685 | const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, true); |
| 23686 | const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order }); |
| 23687 | |
| 23688 | try sema.ensureLayoutResolved(elem_ty, elem_ty_src, .ptr_access); |
| 23689 | |
| 23690 | switch (order) { |
| 23691 | .release, .acq_rel => { |
| 23692 | return sema.fail( |
| 23693 | block, |
| 23694 | order_src, |
| 23695 | "@atomicLoad atomic ordering must not be release or acq_rel", |
| 23696 | .{}, |
| 23697 | ); |
| 23698 | }, |
| 23699 | else => {}, |
| 23700 | } |
| 23701 | |
| 23702 | if (try elem_ty.onePossibleValue(sema.pt)) |opv| return .fromValue(opv); |
| 23703 | |
| 23704 | if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { |
| 23705 | if (try sema.pointerDeref(block, ptr_src, ptr_val, sema.typeOf(ptr))) |elem_val| { |
| 23706 | return Air.internedToRef(elem_val.toIntern()); |
| 23707 | } |
| 23708 | } |
| 23709 | |
| 23710 | try sema.requireRuntimeBlock(block, block.nodeOffset(inst_data.src_node), ptr_src); |
| 23711 | return block.addInst(.{ |
| 23712 | .tag = .atomic_load, |
| 23713 | .data = .{ .atomic_load = .{ |
| 23714 | .ptr = ptr, |
| 23715 | .order = order, |
| 23716 | } }, |
| 23717 | }); |
| 23718 | } |
| 23719 | |
| 23720 | fn zirAtomicRmw(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 23721 | const pt = sema.pt; |
| 23722 | const zcu = pt.zcu; |
| 23723 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 23724 | const extra = sema.code.extraData(Zir.Inst.AtomicRmw, inst_data.payload_index).data; |
| 23725 | const src = block.nodeOffset(inst_data.src_node); |
| 23726 | // zig fmt: off |
| 23727 | const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 23728 | const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 23729 | const op_src = block.builtinCallArgSrc(inst_data.src_node, 2); |
| 23730 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 3); |
| 23731 | const order_src = block.builtinCallArgSrc(inst_data.src_node, 4); |
| 23732 | // zig fmt: on |
| 23733 | const operand = sema.resolveInst(extra.operand); |
| 23734 | const elem_ty = sema.typeOf(operand); |
| 23735 | const uncasted_ptr = sema.resolveInst(extra.ptr); |
| 23736 | const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); |
| 23737 | const op = try sema.resolveAtomicRmwOp(block, op_src, extra.operation); |
| 23738 | |
| 23739 | switch (elem_ty.zigTypeTag(zcu)) { |
| 23740 | .@"enum" => if (op != .Xchg) { |
| 23741 | return sema.fail(block, op_src, "@atomicRmw with enum only allowed with .Xchg", .{}); |
| 23742 | }, |
| 23743 | .bool => if (op != .Xchg) { |
| 23744 | return sema.fail(block, op_src, "@atomicRmw with bool only allowed with .Xchg", .{}); |
| 23745 | }, |
| 23746 | .float => switch (op) { |
| 23747 | .Xchg, .Add, .Sub, .Max, .Min => {}, |
| 23748 | else => return sema.fail(block, op_src, "@atomicRmw with float only allowed with .Xchg, .Add, .Sub, .Max, and .Min", .{}), |
| 23749 | }, |
| 23750 | else => {}, |
| 23751 | } |
| 23752 | const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order }); |
| 23753 | |
| 23754 | if (order == .unordered) { |
| 23755 | return sema.fail(block, order_src, "@atomicRmw atomic ordering must not be unordered", .{}); |
| 23756 | } |
| 23757 | |
| 23758 | // special case zero bit types |
| 23759 | if (try elem_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 23760 | |
| 23761 | const runtime_src = if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| rs: { |
| 23762 | const maybe_operand_val = sema.resolveValue(operand); |
| 23763 | const operand_val = maybe_operand_val orelse { |
| 23764 | try sema.checkPtrIsNotComptimeMutable(block, ptr_val, ptr_src, operand_src); |
| 23765 | break :rs operand_src; |
| 23766 | }; |
| 23767 | if (sema.isComptimeMutablePtr(ptr_val)) { |
| 23768 | const ptr_ty = sema.typeOf(ptr); |
| 23769 | const stored_val = (try sema.pointerDeref(block, ptr_src, ptr_val, ptr_ty)) orelse break :rs ptr_src; |
| 23770 | const new_val = switch (op) { |
| 23771 | // zig fmt: off |
| 23772 | .Xchg => operand_val, |
| 23773 | .Add => try arith.addMaybeWrap(sema, elem_ty, stored_val, operand_val), |
| 23774 | .Sub => try arith.subMaybeWrap(sema, elem_ty, stored_val, operand_val), |
| 23775 | .And => try arith.bitwiseBin (sema, elem_ty, stored_val, operand_val, .@"and"), |
| 23776 | .Nand => try arith.bitwiseBin (sema, elem_ty, stored_val, operand_val, .nand), |
| 23777 | .Or => try arith.bitwiseBin (sema, elem_ty, stored_val, operand_val, .@"or"), |
| 23778 | .Xor => try arith.bitwiseBin (sema, elem_ty, stored_val, operand_val, .xor), |
| 23779 | .Max => Value.numberMax ( stored_val, operand_val, zcu), |
| 23780 | .Min => Value.numberMin ( stored_val, operand_val, zcu), |
| 23781 | // zig fmt: on |
| 23782 | }; |
| 23783 | try sema.storePtrVal(block, src, ptr_val, new_val, elem_ty); |
| 23784 | return Air.internedToRef(stored_val.toIntern()); |
| 23785 | } else break :rs ptr_src; |
| 23786 | } else ptr_src; |
| 23787 | |
| 23788 | const flags: u32 = @as(u32, @backingInt(order)) | (@as(u32, @backingInt(op)) << 3); |
| 23789 | |
| 23790 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 23791 | return block.addInst(.{ |
| 23792 | .tag = .atomic_rmw, |
| 23793 | .data = .{ .pl_op = .{ |
| 23794 | .operand = ptr, |
| 23795 | .payload = try sema.addExtra(Air.AtomicRmw{ |
| 23796 | .operand = operand, |
| 23797 | .flags = flags, |
| 23798 | }), |
| 23799 | } }, |
| 23800 | }); |
| 23801 | } |
| 23802 | |
| 23803 | fn zirAtomicStore(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 23804 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 23805 | const extra = sema.code.extraData(Zir.Inst.AtomicStore, inst_data.payload_index).data; |
| 23806 | const src = block.nodeOffset(inst_data.src_node); |
| 23807 | // zig fmt: off |
| 23808 | const elem_ty_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 23809 | const ptr_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 23810 | const operand_src = block.builtinCallArgSrc(inst_data.src_node, 2); |
| 23811 | const order_src = block.builtinCallArgSrc(inst_data.src_node, 3); |
| 23812 | // zig fmt: on |
| 23813 | const operand = sema.resolveInst(extra.operand); |
| 23814 | const elem_ty = sema.typeOf(operand); |
| 23815 | const uncasted_ptr = sema.resolveInst(extra.ptr); |
| 23816 | const ptr = try sema.checkAtomicPtrOperand(block, elem_ty, elem_ty_src, uncasted_ptr, ptr_src, false); |
| 23817 | const order = try sema.resolveAtomicOrder(block, order_src, extra.ordering, .{ .simple = .atomic_order }); |
| 23818 | |
| 23819 | const air_tag: Air.Inst.Tag = switch (order) { |
| 23820 | .acquire, .acq_rel => { |
| 23821 | return sema.fail( |
| 23822 | block, |
| 23823 | order_src, |
| 23824 | "@atomicStore atomic ordering must not be acquire or acq_rel", |
| 23825 | .{}, |
| 23826 | ); |
| 23827 | }, |
| 23828 | .unordered => .atomic_store_unordered, |
| 23829 | .monotonic => .atomic_store_monotonic, |
| 23830 | .release => .atomic_store_release, |
| 23831 | .seq_cst => .atomic_store_seq_cst, |
| 23832 | }; |
| 23833 | |
| 23834 | return sema.storePtr2(block, src, ptr, ptr_src, operand, operand_src, air_tag); |
| 23835 | } |
| 23836 | |
| 23837 | fn zirMulAdd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 23838 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 23839 | const extra = sema.code.extraData(Zir.Inst.MulAdd, inst_data.payload_index).data; |
| 23840 | const src = block.nodeOffset(inst_data.src_node); |
| 23841 | |
| 23842 | const mulend1_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 23843 | const mulend2_src = block.builtinCallArgSrc(inst_data.src_node, 2); |
| 23844 | const addend_src = block.builtinCallArgSrc(inst_data.src_node, 3); |
| 23845 | |
| 23846 | const addend = sema.resolveInst(extra.addend); |
| 23847 | const ty = sema.typeOf(addend); |
| 23848 | const mulend1 = try sema.coerce(block, ty, sema.resolveInst(extra.mulend1), mulend1_src); |
| 23849 | const mulend2 = try sema.coerce(block, ty, sema.resolveInst(extra.mulend2), mulend2_src); |
| 23850 | |
| 23851 | const maybe_mulend1 = sema.resolveValue(mulend1); |
| 23852 | const maybe_mulend2 = sema.resolveValue(mulend2); |
| 23853 | const maybe_addend = sema.resolveValue(addend); |
| 23854 | const pt = sema.pt; |
| 23855 | const zcu = pt.zcu; |
| 23856 | |
| 23857 | switch (ty.scalarType(zcu).zigTypeTag(zcu)) { |
| 23858 | .comptime_float, .float => {}, |
| 23859 | else => return sema.fail(block, src, "expected vector of floats or float type, found '{f}'", .{ty.fmt(pt)}), |
| 23860 | } |
| 23861 | |
| 23862 | const runtime_src = if (maybe_mulend1) |mulend1_val| rs: { |
| 23863 | if (maybe_mulend2) |mulend2_val| { |
| 23864 | if (mulend2_val.isUndef(zcu)) return pt.undefRef(ty); |
| 23865 | |
| 23866 | if (maybe_addend) |addend_val| { |
| 23867 | if (addend_val.isUndef(zcu)) return pt.undefRef(ty); |
| 23868 | const result_val = try Value.mulAdd(ty, mulend1_val, mulend2_val, addend_val, sema.arena, pt); |
| 23869 | return Air.internedToRef(result_val.toIntern()); |
| 23870 | } else { |
| 23871 | break :rs addend_src; |
| 23872 | } |
| 23873 | } else { |
| 23874 | if (maybe_addend) |addend_val| { |
| 23875 | if (addend_val.isUndef(zcu)) return pt.undefRef(ty); |
| 23876 | } |
| 23877 | break :rs mulend2_src; |
| 23878 | } |
| 23879 | } else rs: { |
| 23880 | if (maybe_mulend2) |mulend2_val| { |
| 23881 | if (mulend2_val.isUndef(zcu)) return pt.undefRef(ty); |
| 23882 | } |
| 23883 | if (maybe_addend) |addend_val| { |
| 23884 | if (addend_val.isUndef(zcu)) return pt.undefRef(ty); |
| 23885 | } |
| 23886 | break :rs mulend1_src; |
| 23887 | }; |
| 23888 | |
| 23889 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 23890 | return block.addInst(.{ |
| 23891 | .tag = .mul_add, |
| 23892 | .data = .{ .pl_op = .{ |
| 23893 | .operand = addend, |
| 23894 | .payload = try sema.addExtra(Air.Bin{ |
| 23895 | .lhs = mulend1, |
| 23896 | .rhs = mulend2, |
| 23897 | }), |
| 23898 | } }, |
| 23899 | }); |
| 23900 | } |
| 23901 | |
| 23902 | fn zirBuiltinCall(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 23903 | const pt = sema.pt; |
| 23904 | const zcu = pt.zcu; |
| 23905 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 23906 | const modifier_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 23907 | const func_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 23908 | const args_src = block.builtinCallArgSrc(inst_data.src_node, 2); |
| 23909 | const call_src = block.nodeOffset(inst_data.src_node); |
| 23910 | |
| 23911 | const extra = sema.code.extraData(Zir.Inst.BuiltinCall, inst_data.payload_index).data; |
| 23912 | const func = sema.resolveInst(extra.callee); |
| 23913 | |
| 23914 | const modifier_ty = try sema.getStdLangType(call_src, .CallModifier); |
| 23915 | const air_ref = sema.resolveInst(extra.modifier); |
| 23916 | const modifier_ref = try sema.coerce(block, modifier_ty, air_ref, modifier_src); |
| 23917 | const modifier_val = try sema.resolveConstDefinedValue(block, modifier_src, modifier_ref, .{ .simple = .call_modifier }); |
| 23918 | var modifier = try sema.interpretStdLangType(block, modifier_src, modifier_val, std.lang.CallModifier); |
| 23919 | switch (modifier) { |
| 23920 | // These can be upgraded to comptime or nosuspend calls. |
| 23921 | .auto, .never_tail, .no_suspend => { |
| 23922 | if (block.isComptime()) { |
| 23923 | if (modifier == .never_tail) { |
| 23924 | return sema.fail(block, modifier_src, "unable to perform 'never_tail' call at compile-time", .{}); |
| 23925 | } |
| 23926 | modifier = .compile_time; |
| 23927 | } else if (extra.flags.is_nosuspend) { |
| 23928 | modifier = .no_suspend; |
| 23929 | } |
| 23930 | }, |
| 23931 | // These can be upgraded to comptime. nosuspend bit can be safely ignored. |
| 23932 | .always_inline, .compile_time => { |
| 23933 | _ = (try sema.resolveDefinedValue(block, func_src, func)) orelse { |
| 23934 | return sema.fail(block, func_src, "modifier '{s}' requires a comptime-known function", .{@tagName(modifier)}); |
| 23935 | }; |
| 23936 | |
| 23937 | if (block.isComptime()) { |
| 23938 | modifier = .compile_time; |
| 23939 | } |
| 23940 | }, |
| 23941 | .always_tail => { |
| 23942 | if (block.isComptime()) { |
| 23943 | modifier = .compile_time; |
| 23944 | } |
| 23945 | }, |
| 23946 | .never_inline => { |
| 23947 | if (block.isComptime()) { |
| 23948 | return sema.fail(block, modifier_src, "unable to perform 'never_inline' call at compile-time", .{}); |
| 23949 | } |
| 23950 | }, |
| 23951 | } |
| 23952 | |
| 23953 | const args = sema.resolveInst(extra.args); |
| 23954 | |
| 23955 | const args_ty = sema.typeOf(args); |
| 23956 | if (!args_ty.isTuple(zcu)) { |
| 23957 | return sema.fail(block, args_src, "expected a tuple, found '{f}'", .{args_ty.fmt(pt)}); |
| 23958 | } |
| 23959 | |
| 23960 | const resolved_args: []Air.Inst.Ref = try sema.arena.alloc(Air.Inst.Ref, args_ty.structFieldCount(zcu)); |
| 23961 | for (resolved_args, 0..) |*resolved, i| { |
| 23962 | resolved.* = try sema.tupleFieldValByIndex(block, args, @intCast(i), args_ty); |
| 23963 | } |
| 23964 | |
| 23965 | const callee_ty = sema.typeOf(func); |
| 23966 | const func_ty = try sema.checkCallArgumentCount(block, func, func_src, callee_ty, resolved_args.len, false); |
| 23967 | const ensure_result_used = extra.flags.ensure_result_used; |
| 23968 | return sema.analyzeCall( |
| 23969 | block, |
| 23970 | func, |
| 23971 | func_ty, |
| 23972 | func_src, |
| 23973 | call_src, |
| 23974 | modifier, |
| 23975 | ensure_result_used, |
| 23976 | .{ .call_builtin = .{ |
| 23977 | .call_node_offset = inst_data.src_node, |
| 23978 | .args = resolved_args, |
| 23979 | } }, |
| 23980 | null, |
| 23981 | .@"@call", |
| 23982 | ); |
| 23983 | } |
| 23984 | |
| 23985 | fn zirFieldParentPtr(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 23986 | const pt = sema.pt; |
| 23987 | const zcu = pt.zcu; |
| 23988 | const ip = &zcu.intern_pool; |
| 23989 | |
| 23990 | const extra = sema.code.extraData(Zir.Inst.FieldParentPtr, extended.operand).data; |
| 23991 | const FlagsInt = @typeInfo(Zir.Inst.FullPtrCastFlags).@"struct".backing_integer.?; |
| 23992 | const flags: Zir.Inst.FullPtrCastFlags = @bitCast(@as(FlagsInt, @truncate(extended.small))); |
| 23993 | assert(!flags.ptr_cast); |
| 23994 | const inst_src = block.nodeOffset(extra.src_node); |
| 23995 | const field_name_src = block.builtinCallArgSrc(extra.src_node, 0); |
| 23996 | const field_ptr_src = block.builtinCallArgSrc(extra.src_node, 1); |
| 23997 | |
| 23998 | const maybe_opt_parent_ptr_ty = try sema.resolveDestType(block, inst_src, extra.parent_ptr_type, .remove_eu, "@fieldParentPtr"); |
| 23999 | try sema.checkPtrType(block, inst_src, maybe_opt_parent_ptr_ty, true); |
| 24000 | const parent_ptr_ty = switch (maybe_opt_parent_ptr_ty.zigTypeTag(zcu)) { |
| 24001 | .optional => maybe_opt_parent_ptr_ty.optionalChild(zcu), |
| 24002 | .pointer => maybe_opt_parent_ptr_ty, |
| 24003 | else => unreachable, |
| 24004 | }; |
| 24005 | const parent_ptr_info = parent_ptr_ty.ptrInfo(zcu); |
| 24006 | if (parent_ptr_info.flags.size != .one) { |
| 24007 | return sema.fail(block, inst_src, "expected single pointer type, found '{f}'", .{parent_ptr_ty.fmt(pt)}); |
| 24008 | } |
| 24009 | const parent_ty: Type = .fromInterned(parent_ptr_info.child); |
| 24010 | try sema.ensureLayoutResolved(parent_ty, inst_src, .field_used); |
| 24011 | switch (parent_ty.zigTypeTag(zcu)) { |
| 24012 | .@"struct", .@"union" => {}, |
| 24013 | else => return sema.fail(block, inst_src, "expected pointer to struct or union type, found '{f}'", .{parent_ptr_ty.fmt(pt)}), |
| 24014 | } |
| 24015 | |
| 24016 | const field_name = try sema.resolveConstStringIntern(block, field_name_src, extra.field_name, .{ .simple = .field_name }); |
| 24017 | const field_index = switch (parent_ty.zigTypeTag(zcu)) { |
| 24018 | .@"struct" => blk: { |
| 24019 | if (parent_ty.isTuple(zcu)) { |
| 24020 | if (field_name.eqlSlice("len", ip)) { |
| 24021 | return sema.fail(block, inst_src, "cannot get @fieldParentPtr of 'len' field of tuple", .{}); |
| 24022 | } |
| 24023 | break :blk try sema.tupleFieldIndex(block, parent_ty, field_name, field_name_src); |
| 24024 | } else { |
| 24025 | break :blk try sema.structFieldIndex(block, parent_ty, field_name, field_name_src); |
| 24026 | } |
| 24027 | }, |
| 24028 | .@"union" => try sema.unionFieldIndex(block, parent_ty, field_name, field_name_src), |
| 24029 | else => unreachable, |
| 24030 | }; |
| 24031 | if (parent_ty.zigTypeTag(zcu) == .@"struct" and parent_ty.structFieldIsComptime(field_index, zcu)) { |
| 24032 | return sema.fail(block, field_name_src, "cannot get @fieldParentPtr of a comptime field", .{}); |
| 24033 | } |
| 24034 | |
| 24035 | const field_ptr = sema.resolveInst(extra.field_ptr); |
| 24036 | const field_ptr_ty = sema.typeOf(field_ptr); |
| 24037 | try sema.checkPtrOperand(block, field_ptr_src, field_ptr_ty); |
| 24038 | |
| 24039 | const hypothetical_field_ptr_ty = try parent_ptr_ty.fieldPtrType(field_index, pt); |
| 24040 | const casted_field_ptr = try sema.ptrCastFull( |
| 24041 | block, |
| 24042 | flags, |
| 24043 | inst_src, |
| 24044 | field_ptr, |
| 24045 | field_ptr_src, |
| 24046 | hypothetical_field_ptr_ty, |
| 24047 | "@fieldParentPtr", |
| 24048 | ); |
| 24049 | |
| 24050 | const unaligned_parent_ptr_ty = try pt.ptrType(info: { |
| 24051 | var info = parent_ptr_info; |
| 24052 | info.flags.alignment = hypothetical_field_ptr_ty.ptrAlignment(zcu); |
| 24053 | break :info info; |
| 24054 | }); |
| 24055 | |
| 24056 | const unaligned_parent_ptr: Air.Inst.Ref = if (try sema.resolveDefinedValue( |
| 24057 | block, |
| 24058 | field_ptr_src, |
| 24059 | casted_field_ptr, |
| 24060 | )) |field_ptr_val| switch (parent_ty.containerLayout(zcu)) { |
| 24061 | .@"packed" => .fromValue(try pt.getCoerced(field_ptr_val, unaligned_parent_ptr_ty)), |
| 24062 | .@"extern" => switch (parent_ty.zigTypeTag(zcu)) { |
| 24063 | .@"struct" => .fromValue(try sema.ptrSubtract( |
| 24064 | block, |
| 24065 | field_ptr_src, |
| 24066 | field_ptr_val, |
| 24067 | parent_ty.structFieldOffset(field_index, zcu), |
| 24068 | unaligned_parent_ptr_ty, |
| 24069 | )), |
| 24070 | .@"union" => .fromValue(try pt.getCoerced(field_ptr_val, unaligned_parent_ptr_ty)), |
| 24071 | else => unreachable, |
| 24072 | }, |
| 24073 | .auto => result: { |
| 24074 | const opt_field: ?InternPool.Key.Ptr.BaseAddr.BaseIndex = opt_field: { |
| 24075 | const ptr = switch (ip.indexToKey(field_ptr_val.toIntern())) { |
| 24076 | .ptr => |ptr| ptr, |
| 24077 | else => break :opt_field null, |
| 24078 | }; |
| 24079 | if (ptr.byte_offset != 0) break :opt_field null; |
| 24080 | break :opt_field switch (ptr.base_addr) { |
| 24081 | .field => |field| field, |
| 24082 | else => null, |
| 24083 | }; |
| 24084 | }; |
| 24085 | |
| 24086 | const field = opt_field orelse { |
| 24087 | return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{}); |
| 24088 | }; |
| 24089 | |
| 24090 | if (Value.fromInterned(field.base).typeOf(zcu).childType(zcu).toIntern() != parent_ty.toIntern()) { |
| 24091 | return sema.fail(block, field_ptr_src, "pointer value not based on parent struct", .{}); |
| 24092 | } |
| 24093 | |
| 24094 | if (field.index != field_index) { |
| 24095 | return sema.fail(block, inst_src, "field '{f}' has index '{d}' but pointer value is index '{d}' of struct '{f}'", .{ |
| 24096 | field_name.fmt(ip), field_index, field.index, parent_ty.fmt(pt), |
| 24097 | }); |
| 24098 | } |
| 24099 | break :result .fromValue(try pt.getCoerced(.fromInterned(field.base), unaligned_parent_ptr_ty)); |
| 24100 | }, |
| 24101 | } else result: { |
| 24102 | break :result try block.addInst(.{ |
| 24103 | .tag = .field_parent_ptr, |
| 24104 | .data = .{ .ty_pl = .{ |
| 24105 | .ty = unaligned_parent_ptr_ty, |
| 24106 | .payload = try block.sema.addExtra(Air.FieldParentPtr{ |
| 24107 | .field_ptr = casted_field_ptr, |
| 24108 | .field_index = @intCast(field_index), |
| 24109 | }), |
| 24110 | } }, |
| 24111 | }); |
| 24112 | }; |
| 24113 | |
| 24114 | // There's one more error condition: if the hypothetical field pointer type has a lower |
| 24115 | // alignment than the parent pointer type, then we need an `@alignCast`. Note that the earlier |
| 24116 | // `ptrCastFull` may *also* have "used" the `@alignCast`; that would be a case where the field |
| 24117 | // is naturally less aligned than the rest of the struct, *and* the field pointer is itself |
| 24118 | // underaligned compared to the field alignment. For example, `struct { a: u32, b: u16 }` with |
| 24119 | // a field pointer of type `*align(1) u16`. |
| 24120 | switch (hypothetical_field_ptr_ty.ptrAlignment(zcu).order(parent_ptr_ty.ptrAlignment(zcu))) { |
| 24121 | .gt => unreachable, // getting a field pointer can never increase alignment |
| 24122 | .eq => return sema.coerce(block, maybe_opt_parent_ptr_ty, unaligned_parent_ptr, inst_src), |
| 24123 | .lt => if (flags.align_cast) { |
| 24124 | // Go through `ptrCastFull` for the safety check. |
| 24125 | return sema.ptrCastFull( |
| 24126 | block, |
| 24127 | flags, |
| 24128 | inst_src, |
| 24129 | unaligned_parent_ptr, |
| 24130 | inst_src, |
| 24131 | maybe_opt_parent_ptr_ty, |
| 24132 | "@fieldParentPtr", |
| 24133 | ); |
| 24134 | } else return sema.failWithOwnedErrorMsg(block, msg: { |
| 24135 | const msg = try sema.errMsg(inst_src, "@fieldParentPtr increases pointer alignment", .{}); |
| 24136 | errdefer msg.destroy(sema.gpa); |
| 24137 | try sema.errNote(inst_src, msg, "parent pointer type '{f}' has alignment '{d}'", .{ |
| 24138 | parent_ptr_ty.fmt(pt), |
| 24139 | parent_ptr_ty.abiAlignment(zcu), |
| 24140 | }); |
| 24141 | if (parent_ty.isTuple(zcu)) { |
| 24142 | try sema.errNote(field_ptr_src, msg, "tuple field '{d}' limits alignment to '{d}'", .{ |
| 24143 | field_index, |
| 24144 | field_ptr_ty.ptrAlignment(zcu), |
| 24145 | }); |
| 24146 | } else { |
| 24147 | try sema.errNote(parent_ty.srcLoc(zcu), msg, "{t} field '{f}' limits alignment to '{d}'", .{ |
| 24148 | parent_ty.zigTypeTag(zcu), |
| 24149 | switch (parent_ty.zigTypeTag(zcu)) { |
| 24150 | .@"struct" => parent_ty.structFieldName(field_index, zcu).unwrap().?.fmt(ip), |
| 24151 | .@"union" => parent_ty.unionTagTypeHypothetical(zcu).enumFieldName(field_index, zcu).fmt(ip), |
| 24152 | else => unreachable, |
| 24153 | }, |
| 24154 | field_ptr_ty.ptrAlignment(zcu), |
| 24155 | }); |
| 24156 | } |
| 24157 | try sema.errNote(inst_src, msg, "use @alignCast to assert pointer alignment", .{}); |
| 24158 | break :msg msg; |
| 24159 | }), |
| 24160 | } |
| 24161 | } |
| 24162 | |
| 24163 | fn ptrSubtract(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, byte_subtract: u64, new_ty: Type) !Value { |
| 24164 | const pt = sema.pt; |
| 24165 | const zcu = pt.zcu; |
| 24166 | if (byte_subtract == 0) return pt.getCoerced(ptr_val, new_ty); |
| 24167 | const ptr = switch (zcu.intern_pool.indexToKey(ptr_val.toIntern())) { |
| 24168 | .undef => return sema.failWithUseOfUndef(block, src, null), |
| 24169 | .ptr => |ptr| ptr, |
| 24170 | else => unreachable, |
| 24171 | }; |
| 24172 | if (ptr.byte_offset < byte_subtract) { |
| 24173 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 24174 | const msg = try sema.errMsg(src, "pointer computation here causes illegal behavior", .{}); |
| 24175 | errdefer msg.destroy(sema.gpa); |
| 24176 | try sema.errNote(src, msg, "resulting pointer exceeds bounds of containing value which may trigger overflow", .{}); |
| 24177 | break :msg msg; |
| 24178 | }); |
| 24179 | } |
| 24180 | return Value.fromInterned(try pt.intern(.{ .ptr = .{ |
| 24181 | .ty = new_ty.toIntern(), |
| 24182 | .base_addr = ptr.base_addr, |
| 24183 | .byte_offset = ptr.byte_offset - byte_subtract, |
| 24184 | } })); |
| 24185 | } |
| 24186 | |
| 24187 | fn zirMinMax( |
| 24188 | sema: *Sema, |
| 24189 | block: *Block, |
| 24190 | inst: Zir.Inst.Index, |
| 24191 | comptime air_tag: Air.Inst.Tag, |
| 24192 | ) CompileError!Air.Inst.Ref { |
| 24193 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 24194 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 24195 | const src = block.nodeOffset(inst_data.src_node); |
| 24196 | const lhs_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 24197 | const rhs_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 24198 | const lhs = sema.resolveInst(extra.lhs); |
| 24199 | const rhs = sema.resolveInst(extra.rhs); |
| 24200 | return sema.analyzeMinMax(block, src, air_tag, &.{ lhs, rhs }, &.{ lhs_src, rhs_src }); |
| 24201 | } |
| 24202 | |
| 24203 | fn zirMinMaxMulti( |
| 24204 | sema: *Sema, |
| 24205 | block: *Block, |
| 24206 | extended: Zir.Inst.Extended.InstData, |
| 24207 | comptime air_tag: Air.Inst.Tag, |
| 24208 | ) CompileError!Air.Inst.Ref { |
| 24209 | const extra = sema.code.extraData(Zir.Inst.NodeMultiOp, extended.operand); |
| 24210 | const src_node = extra.data.src_node; |
| 24211 | const src = block.nodeOffset(src_node); |
| 24212 | const operands = sema.code.refSlice(extra.end, extended.small); |
| 24213 | |
| 24214 | const air_refs = try sema.arena.alloc(Air.Inst.Ref, operands.len); |
| 24215 | const operand_srcs = try sema.arena.alloc(LazySrcLoc, operands.len); |
| 24216 | |
| 24217 | for (operands, air_refs, operand_srcs, 0..) |zir_ref, *air_ref, *op_src, i| { |
| 24218 | op_src.* = block.builtinCallArgSrc(src_node, @intCast(i)); |
| 24219 | air_ref.* = sema.resolveInst(zir_ref); |
| 24220 | } |
| 24221 | |
| 24222 | return sema.analyzeMinMax(block, src, air_tag, air_refs, operand_srcs); |
| 24223 | } |
| 24224 | |
| 24225 | fn analyzeMinMax( |
| 24226 | sema: *Sema, |
| 24227 | block: *Block, |
| 24228 | src: LazySrcLoc, |
| 24229 | comptime air_tag: Air.Inst.Tag, |
| 24230 | operands: []const Air.Inst.Ref, |
| 24231 | operand_srcs: []const LazySrcLoc, |
| 24232 | ) CompileError!Air.Inst.Ref { |
| 24233 | assert(operands.len == operand_srcs.len); |
| 24234 | assert(operands.len > 0); |
| 24235 | |
| 24236 | const pt = sema.pt; |
| 24237 | const zcu = pt.zcu; |
| 24238 | |
| 24239 | // This function has the signature `fn (Value, Value, *Zcu) Value`. |
| 24240 | // It is only used on scalar values, although the values may have different types. |
| 24241 | // If either operand is undef, it returns undef. |
| 24242 | const opFunc = switch (air_tag) { |
| 24243 | .min => Value.numberMin, |
| 24244 | .max => Value.numberMax, |
| 24245 | else => comptime unreachable, |
| 24246 | }; |
| 24247 | |
| 24248 | if (operands.len == 1) { |
| 24249 | try sema.checkNumericType(block, operand_srcs[0], sema.typeOf(operands[0])); |
| 24250 | return operands[0]; |
| 24251 | } |
| 24252 | |
| 24253 | // First, basic type validation; we'll make sure all the operands are numeric and agree on vector length. |
| 24254 | // This value will be `null` for a scalar type, otherwise the length of the vector type. |
| 24255 | const vector_len: ?u64 = vec_len: { |
| 24256 | const first_operand_ty = sema.typeOf(operands[0]); |
| 24257 | try sema.checkNumericType(block, operand_srcs[0], first_operand_ty); |
| 24258 | if (first_operand_ty.zigTypeTag(zcu) == .vector) { |
| 24259 | const vec_len = first_operand_ty.vectorLen(zcu); |
| 24260 | for (operands[1..], operand_srcs[1..]) |operand, operand_src| { |
| 24261 | const operand_ty = sema.typeOf(operand); |
| 24262 | try sema.checkNumericType(block, operand_src, operand_ty); |
| 24263 | if (operand_ty.zigTypeTag(zcu) != .vector) { |
| 24264 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 24265 | const msg = try sema.errMsg(operand_src, "expected vector, found '{f}'", .{operand_ty.fmt(pt)}); |
| 24266 | errdefer msg.destroy(zcu.gpa); |
| 24267 | try sema.errNote(operand_srcs[0], msg, "vector operand here", .{}); |
| 24268 | break :msg msg; |
| 24269 | }); |
| 24270 | } |
| 24271 | if (operand_ty.vectorLen(zcu) != vec_len) { |
| 24272 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 24273 | const msg = try sema.errMsg(operand_src, "expected vector of length '{d}', found '{f}'", .{ vec_len, operand_ty.fmt(pt) }); |
| 24274 | errdefer msg.destroy(zcu.gpa); |
| 24275 | try sema.errNote(operand_srcs[0], msg, "vector of length '{d}' here", .{vec_len}); |
| 24276 | break :msg msg; |
| 24277 | }); |
| 24278 | } |
| 24279 | } |
| 24280 | break :vec_len vec_len; |
| 24281 | } else { |
| 24282 | for (operands[1..], operand_srcs[1..]) |operand, operand_src| { |
| 24283 | const operand_ty = sema.typeOf(operand); |
| 24284 | try sema.checkNumericType(block, operand_src, operand_ty); |
| 24285 | if (operand_ty.zigTypeTag(zcu) == .vector) { |
| 24286 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 24287 | const msg = try sema.errMsg(operand_srcs[0], "expected vector, found '{f}'", .{first_operand_ty.fmt(pt)}); |
| 24288 | errdefer msg.destroy(zcu.gpa); |
| 24289 | try sema.errNote(operand_src, msg, "vector operand here", .{}); |
| 24290 | break :msg msg; |
| 24291 | }); |
| 24292 | } |
| 24293 | } |
| 24294 | break :vec_len null; |
| 24295 | } |
| 24296 | }; |
| 24297 | |
| 24298 | // Now we want to look at the scalar types. If any is a float, our result will be a float. This |
| 24299 | // union is in "priority" order: `float` overrides `comptime_float` overrides `int`. |
| 24300 | const TypeStrat = union(enum) { |
| 24301 | float: Type, |
| 24302 | comptime_float, |
| 24303 | int: struct { |
| 24304 | /// If this is still `true` at the end, we will just use a `comptime_int`. |
| 24305 | all_comptime_int: bool, |
| 24306 | // These two fields tells us about the *result* type, which is refined based on operand types. |
| 24307 | // e.g. `@max(u32, i64)` results in a `u63`, because the result is >=0 and <=maxInt(i64). |
| 24308 | result_min: Value, |
| 24309 | result_max: Value, |
| 24310 | // These two fields tell us the *intermediate* type to use for actually computing the min/max. |
| 24311 | // e.g. `@max(u32, i64)` uses an intermediate `i64`, because it can fit all our operands. |
| 24312 | operand_min: Value, |
| 24313 | operand_max: Value, |
| 24314 | }, |
| 24315 | none, |
| 24316 | }; |
| 24317 | var cur_strat: TypeStrat = .none; |
| 24318 | for (operands) |operand| { |
| 24319 | const operand_scalar_ty = sema.typeOf(operand).scalarType(zcu); |
| 24320 | const want_strat: TypeStrat = switch (operand_scalar_ty.zigTypeTag(zcu)) { |
| 24321 | .comptime_int => s: { |
| 24322 | const val = sema.resolveValue(operand).?; |
| 24323 | if (val.isUndef(zcu)) break :s .none; |
| 24324 | break :s .{ .int = .{ |
| 24325 | .all_comptime_int = true, |
| 24326 | .result_min = val, |
| 24327 | .result_max = val, |
| 24328 | .operand_min = val, |
| 24329 | .operand_max = val, |
| 24330 | } }; |
| 24331 | }, |
| 24332 | .comptime_float => .comptime_float, |
| 24333 | .float => .{ .float = operand_scalar_ty }, |
| 24334 | .int => s: { |
| 24335 | // If the *value* is comptime-known, we will use that to get tighter bounds. If #3806 |
| 24336 | // is accepted and implemented, so that integer literals have a tightly-bounded ranged |
| 24337 | // integer type (and `comptime_int` ceases to exist), this block should probably go away |
| 24338 | // (replaced with just the simple calls to `Type.minInt`/`Type.maxInt`) so that we only |
| 24339 | // use the input *types* to determine the result type. |
| 24340 | const min: Value, const max: Value = bounds: { |
| 24341 | if (sema.resolveValue(operand)) |operand_val| { |
| 24342 | if (vector_len) |len| { |
| 24343 | var min = try operand_val.elemValue(pt, 0); |
| 24344 | var max = min; |
| 24345 | for (1..@intCast(len)) |elem_idx| { |
| 24346 | const elem_val = try operand_val.elemValue(pt, elem_idx); |
| 24347 | min = Value.numberMin(min, elem_val, zcu); |
| 24348 | max = Value.numberMax(max, elem_val, zcu); |
| 24349 | } |
| 24350 | if (!min.isUndef(zcu) and !max.isUndef(zcu)) { |
| 24351 | break :bounds .{ min, max }; |
| 24352 | } |
| 24353 | } else { |
| 24354 | if (!operand_val.isUndef(zcu)) { |
| 24355 | break :bounds .{ operand_val, operand_val }; |
| 24356 | } |
| 24357 | } |
| 24358 | } |
| 24359 | break :bounds .{ |
| 24360 | try operand_scalar_ty.minInt(pt, operand_scalar_ty), |
| 24361 | try operand_scalar_ty.maxInt(pt, operand_scalar_ty), |
| 24362 | }; |
| 24363 | }; |
| 24364 | break :s .{ .int = .{ |
| 24365 | .all_comptime_int = false, |
| 24366 | .result_min = min, |
| 24367 | .result_max = max, |
| 24368 | .operand_min = min, |
| 24369 | .operand_max = max, |
| 24370 | } }; |
| 24371 | }, |
| 24372 | else => unreachable, |
| 24373 | }; |
| 24374 | if (@backingInt(want_strat) < @backingInt(cur_strat)) { |
| 24375 | // `want_strat` overrides `cur_strat`. |
| 24376 | cur_strat = want_strat; |
| 24377 | } else if (@backingInt(want_strat) == @backingInt(cur_strat)) { |
| 24378 | // The behavior depends on the tag. |
| 24379 | switch (cur_strat) { |
| 24380 | .none, .comptime_float => {}, // no payload, so nop |
| 24381 | .float => |cur_float| { |
| 24382 | const want_float = want_strat.float; |
| 24383 | // Select the larger bit size. If the bit size is the same, select whichever is not c_longdouble. |
| 24384 | const cur_bits = cur_float.floatBits(zcu.getTarget()); |
| 24385 | const want_bits = want_float.floatBits(zcu.getTarget()); |
| 24386 | if (want_bits > cur_bits or |
| 24387 | (want_bits == cur_bits and |
| 24388 | cur_float.toIntern() == .c_longdouble_type and |
| 24389 | want_float.toIntern() != .c_longdouble_type)) |
| 24390 | { |
| 24391 | cur_strat = want_strat; |
| 24392 | } |
| 24393 | }, |
| 24394 | .int => |*cur_int| { |
| 24395 | const want_int = want_strat.int; |
| 24396 | if (!want_int.all_comptime_int) cur_int.all_comptime_int = false; |
| 24397 | cur_int.result_min = opFunc(cur_int.result_min, want_int.result_min, zcu); |
| 24398 | cur_int.result_max = opFunc(cur_int.result_max, want_int.result_max, zcu); |
| 24399 | cur_int.operand_min = Value.numberMin(cur_int.operand_min, want_int.operand_min, zcu); |
| 24400 | cur_int.operand_max = Value.numberMax(cur_int.operand_max, want_int.operand_max, zcu); |
| 24401 | }, |
| 24402 | } |
| 24403 | } |
| 24404 | } |
| 24405 | |
| 24406 | // Use `cur_strat` to actually resolve the result type (and intermediate type). |
| 24407 | const result_scalar_ty: Type, const intermediate_scalar_ty: Type = switch (cur_strat) { |
| 24408 | .float => |ty| .{ ty, ty }, |
| 24409 | .comptime_float => .{ .comptime_float, .comptime_float }, |
| 24410 | .int => |int| if (int.all_comptime_int) .{ |
| 24411 | .comptime_int, |
| 24412 | .comptime_int, |
| 24413 | } else .{ |
| 24414 | try pt.intFittingRange(int.result_min, int.result_max), |
| 24415 | try pt.intFittingRange(int.operand_min, int.operand_max), |
| 24416 | }, |
| 24417 | .none => .{ .comptime_int, .comptime_int }, // all undef comptime ints |
| 24418 | }; |
| 24419 | const result_ty: Type = if (vector_len) |l| try pt.vectorType(.{ |
| 24420 | .len = @intCast(l), |
| 24421 | .child = result_scalar_ty.toIntern(), |
| 24422 | }) else result_scalar_ty; |
| 24423 | const intermediate_ty: Type = if (vector_len) |l| try pt.vectorType(.{ |
| 24424 | .len = @intCast(l), |
| 24425 | .child = intermediate_scalar_ty.toIntern(), |
| 24426 | }) else intermediate_scalar_ty; |
| 24427 | |
| 24428 | // We might have refined all the way down to an OPV type---check now. |
| 24429 | if (try result_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 24430 | |
| 24431 | // This value, if not `null`, will have type `intermediate_ty`. |
| 24432 | const comptime_part: ?Value = ct: { |
| 24433 | // Contains the comptime-known scalar result values. |
| 24434 | // Values are scalars with no particular type. |
| 24435 | // `elems.len` is `vector_len orelse 1`. |
| 24436 | const elems: []InternPool.Index = try sema.arena.alloc( |
| 24437 | InternPool.Index, |
| 24438 | try sema.usizeCast(block, src, vector_len orelse 1), |
| 24439 | ); |
| 24440 | // If `false`, we've not seen any comptime-known operand yet, so `elems` contains `undefined`. |
| 24441 | // Otherwise, `elems` is populated with the comptime-known results so far. |
| 24442 | var elems_populated = false; |
| 24443 | // Populated when we see a runtime-known operand. |
| 24444 | var opt_runtime_src: ?LazySrcLoc = null; |
| 24445 | |
| 24446 | for (operands, operand_srcs) |operand, operand_src| { |
| 24447 | const operand_val = sema.resolveValue(operand) orelse { |
| 24448 | if (opt_runtime_src == null) opt_runtime_src = operand_src; |
| 24449 | continue; |
| 24450 | }; |
| 24451 | if (vector_len) |len| { |
| 24452 | // Vector case; apply `opFunc` to each element. |
| 24453 | if (elems_populated) { |
| 24454 | for (elems, 0..@intCast(len)) |*elem, elem_idx| { |
| 24455 | const new_elem = try operand_val.elemValue(pt, elem_idx); |
| 24456 | elem.* = opFunc(.fromInterned(elem.*), new_elem, zcu).toIntern(); |
| 24457 | } |
| 24458 | } else { |
| 24459 | elems_populated = true; |
| 24460 | for (elems, 0..@intCast(len)) |*elem_out, elem_idx| { |
| 24461 | elem_out.* = (try operand_val.elemValue(pt, elem_idx)).toIntern(); |
| 24462 | } |
| 24463 | } |
| 24464 | } else { |
| 24465 | // Scalar case; just apply `opFunc`. |
| 24466 | if (elems_populated) { |
| 24467 | elems[0] = opFunc(.fromInterned(elems[0]), operand_val, zcu).toIntern(); |
| 24468 | } else { |
| 24469 | elems_populated = true; |
| 24470 | elems[0] = operand_val.toIntern(); |
| 24471 | } |
| 24472 | } |
| 24473 | } |
| 24474 | const runtime_src = opt_runtime_src orelse { |
| 24475 | // The result is comptime-known. Coerce each element to its scalar type. |
| 24476 | assert(elems_populated); |
| 24477 | for (elems) |*elem| { |
| 24478 | if (Value.fromInterned(elem.*).isUndef(zcu)) { |
| 24479 | elem.* = (try pt.undefValue(result_scalar_ty)).toIntern(); |
| 24480 | } else { |
| 24481 | // This coercion will always succeed, because `result_scalar_ty` can definitely hold the result. |
| 24482 | const coerced_ref = try sema.coerce(block, result_scalar_ty, Air.internedToRef(elem.*), .unneeded); |
| 24483 | elem.* = coerced_ref.toInterned().?; |
| 24484 | } |
| 24485 | } |
| 24486 | if (vector_len == null) return Air.internedToRef(elems[0]); |
| 24487 | return Air.internedToRef((try pt.aggregateValue(result_ty, elems)).toIntern()); |
| 24488 | }; |
| 24489 | _ = runtime_src; |
| 24490 | // The result is runtime-known. |
| 24491 | // Coerce each element to the intermediate scalar type, unless there were no comptime-known operands. |
| 24492 | if (!elems_populated) break :ct null; |
| 24493 | for (elems) |*elem| { |
| 24494 | if (Value.fromInterned(elem.*).isUndef(zcu)) { |
| 24495 | elem.* = (try pt.undefValue(intermediate_scalar_ty)).toIntern(); |
| 24496 | } else { |
| 24497 | // This coercion will always succeed, because `intermediate_scalar_ty` can definitely hold all operands. |
| 24498 | const coerced_ref = try sema.coerce(block, intermediate_scalar_ty, Air.internedToRef(elem.*), .unneeded); |
| 24499 | elem.* = coerced_ref.toInterned().?; |
| 24500 | } |
| 24501 | } |
| 24502 | break :ct if (vector_len != null) |
| 24503 | try pt.aggregateValue(intermediate_ty, elems) |
| 24504 | else |
| 24505 | .fromInterned(elems[0]); |
| 24506 | }; |
| 24507 | |
| 24508 | // Time to emit the runtime operations. All runtime-known peers are coerced to `intermediate_ty`, and we cast down to `result_ty` at the end. |
| 24509 | |
| 24510 | // `.none` indicates no result so far. |
| 24511 | var cur_result: Air.Inst.Ref = if (comptime_part) |val| Air.internedToRef(val.toIntern()) else .none; |
| 24512 | for (operands, operand_srcs) |operand, operand_src| { |
| 24513 | if (try sema.isComptimeKnown(operand)) continue; // already in `comptime_part` |
| 24514 | // This coercion could fail; e.g. coercing a runtime integer peer to a `comptime_float` in a case like `@min(runtime_int, 1.5)`. |
| 24515 | const operand_coerced = try sema.coerce(block, intermediate_ty, operand, operand_src); |
| 24516 | if (cur_result == .none) { |
| 24517 | cur_result = operand_coerced; |
| 24518 | } else { |
| 24519 | cur_result = try block.addBinOp(air_tag, cur_result, operand_coerced); |
| 24520 | } |
| 24521 | } |
| 24522 | |
| 24523 | assert(cur_result != .none); |
| 24524 | assert(sema.typeOf(cur_result).toIntern() == intermediate_ty.toIntern()); |
| 24525 | |
| 24526 | // If there is a comptime-known undef operand, we actually return comptime-known undef -- but we had to do the runtime stuff to check for coercion errors. |
| 24527 | if (comptime_part) |val| { |
| 24528 | if (val.isUndef(zcu)) { |
| 24529 | return pt.undefRef(result_ty); |
| 24530 | } |
| 24531 | } |
| 24532 | |
| 24533 | if (result_ty.toIntern() == intermediate_ty.toIntern()) { |
| 24534 | // No final cast needed; we're all done. |
| 24535 | return cur_result; |
| 24536 | } |
| 24537 | |
| 24538 | // A final cast is needed. The only case where `intermediate_ty` is different is for integers, |
| 24539 | // where we have refined the range, so we should be doing an intcast. |
| 24540 | assert(intermediate_scalar_ty.zigTypeTag(zcu) == .int); |
| 24541 | assert(result_scalar_ty.zigTypeTag(zcu) == .int); |
| 24542 | return block.addTyOp(.int_cast, result_ty, cur_result); |
| 24543 | } |
| 24544 | |
| 24545 | fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !Air.Inst.Ref { |
| 24546 | const pt = sema.pt; |
| 24547 | const zcu = pt.zcu; |
| 24548 | const ptr_ty = sema.typeOf(ptr); |
| 24549 | const info = ptr_ty.ptrInfo(zcu); |
| 24550 | if (info.flags.size == .one) { |
| 24551 | // Already an array pointer. |
| 24552 | return ptr; |
| 24553 | } |
| 24554 | const new_ty = try pt.ptrType(.{ |
| 24555 | .child = (try pt.arrayType(.{ |
| 24556 | .len = len, |
| 24557 | .sentinel = info.sentinel, |
| 24558 | .child = info.child, |
| 24559 | })).toIntern(), |
| 24560 | .flags = .{ |
| 24561 | .alignment = info.flags.alignment, |
| 24562 | .is_const = info.flags.is_const, |
| 24563 | .is_volatile = info.flags.is_volatile, |
| 24564 | .is_allowzero = info.flags.is_allowzero, |
| 24565 | .address_space = info.flags.address_space, |
| 24566 | }, |
| 24567 | }); |
| 24568 | const non_slice_ptr = if (info.flags.size == .slice) |
| 24569 | try block.addTyOp(.slice_ptr, ptr_ty.slicePtrFieldType(zcu), ptr) |
| 24570 | else |
| 24571 | ptr; |
| 24572 | return block.addTyOp(.ptr_cast, new_ty, non_slice_ptr); |
| 24573 | } |
| 24574 | |
| 24575 | fn zirMemcpy( |
| 24576 | sema: *Sema, |
| 24577 | block: *Block, |
| 24578 | inst: Zir.Inst.Index, |
| 24579 | air_tag: Air.Inst.Tag, |
| 24580 | check_aliasing: bool, |
| 24581 | ) CompileError!void { |
| 24582 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 24583 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 24584 | const src = block.nodeOffset(inst_data.src_node); |
| 24585 | const dest_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 24586 | const src_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 24587 | const dest_ptr = sema.resolveInst(extra.lhs); |
| 24588 | const src_ptr = sema.resolveInst(extra.rhs); |
| 24589 | const dest_ty = sema.typeOf(dest_ptr); |
| 24590 | const src_ty = sema.typeOf(src_ptr); |
| 24591 | const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr); |
| 24592 | const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr); |
| 24593 | const pt = sema.pt; |
| 24594 | const zcu = pt.zcu; |
| 24595 | |
| 24596 | if (dest_ty.isConstPtr(zcu)) { |
| 24597 | return sema.fail(block, dest_src, "cannot copy to constant pointer", .{}); |
| 24598 | } |
| 24599 | |
| 24600 | if (dest_len == .none and src_len == .none) { |
| 24601 | const msg = msg: { |
| 24602 | const msg = try sema.errMsg(src, "unknown copy length", .{}); |
| 24603 | errdefer msg.destroy(sema.gpa); |
| 24604 | try sema.errNote(dest_src, msg, "destination type '{f}' provides no length", .{ |
| 24605 | dest_ty.fmt(pt), |
| 24606 | }); |
| 24607 | try sema.errNote(src_src, msg, "source type '{f}' provides no length", .{ |
| 24608 | src_ty.fmt(pt), |
| 24609 | }); |
| 24610 | break :msg msg; |
| 24611 | }; |
| 24612 | return sema.failWithOwnedErrorMsg(block, msg); |
| 24613 | } |
| 24614 | |
| 24615 | const dest_elem_ty = dest_ty.indexableElem(zcu); |
| 24616 | const src_elem_ty = src_ty.indexableElem(zcu); |
| 24617 | |
| 24618 | try sema.ensureLayoutResolved(dest_elem_ty, dest_src, .ptr_access); |
| 24619 | try sema.ensureLayoutResolved(src_elem_ty, src_src, .ptr_access); |
| 24620 | |
| 24621 | const imc = try sema.coerceInMemoryAllowed( |
| 24622 | block, |
| 24623 | dest_elem_ty, |
| 24624 | src_elem_ty, |
| 24625 | false, |
| 24626 | zcu.getTarget(), |
| 24627 | dest_src, |
| 24628 | src_src, |
| 24629 | null, |
| 24630 | ); |
| 24631 | if (imc != .ok) return sema.failWithOwnedErrorMsg(block, msg: { |
| 24632 | const msg = try sema.errMsg( |
| 24633 | src, |
| 24634 | "pointer element type '{f}' cannot coerce into element type '{f}'", |
| 24635 | .{ src_elem_ty.fmt(pt), dest_elem_ty.fmt(pt) }, |
| 24636 | ); |
| 24637 | errdefer msg.destroy(sema.gpa); |
| 24638 | try imc.report(sema, src, msg); |
| 24639 | break :msg msg; |
| 24640 | }); |
| 24641 | |
| 24642 | var len_val: ?Value = null; |
| 24643 | |
| 24644 | if (dest_len != .none and src_len != .none) check: { |
| 24645 | // If we can check at compile-time, no need for runtime safety. |
| 24646 | if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| { |
| 24647 | len_val = dest_len_val; |
| 24648 | if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { |
| 24649 | if (!(try sema.valuesEqual(dest_len_val, src_len_val, .usize))) { |
| 24650 | const msg = msg: { |
| 24651 | const msg = try sema.errMsg(src, "non-matching copy lengths", .{}); |
| 24652 | errdefer msg.destroy(sema.gpa); |
| 24653 | try sema.errNote(dest_src, msg, "length {f} here", .{ |
| 24654 | dest_len_val.fmtValueSema(pt, sema), |
| 24655 | }); |
| 24656 | try sema.errNote(src_src, msg, "length {f} here", .{ |
| 24657 | src_len_val.fmtValueSema(pt, sema), |
| 24658 | }); |
| 24659 | break :msg msg; |
| 24660 | }; |
| 24661 | return sema.failWithOwnedErrorMsg(block, msg); |
| 24662 | } |
| 24663 | break :check; |
| 24664 | } |
| 24665 | } else if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { |
| 24666 | len_val = src_len_val; |
| 24667 | } |
| 24668 | |
| 24669 | if (block.wantSafety()) { |
| 24670 | const ok = try block.addBinOp(.cmp_eq, dest_len, src_len); |
| 24671 | try sema.addSafetyCheck(block, src, ok, .copy_len_mismatch); |
| 24672 | } |
| 24673 | } else if (dest_len != .none) { |
| 24674 | if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| { |
| 24675 | len_val = dest_len_val; |
| 24676 | } |
| 24677 | } else if (src_len != .none) { |
| 24678 | if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { |
| 24679 | len_val = src_len_val; |
| 24680 | } |
| 24681 | } |
| 24682 | |
| 24683 | zero_bit: { |
| 24684 | const src_comptime = src_elem_ty.comptimeOnly(zcu); |
| 24685 | const dest_comptime = dest_elem_ty.comptimeOnly(zcu); |
| 24686 | assert(src_comptime == dest_comptime); // IMC |
| 24687 | if (src_comptime) break :zero_bit; |
| 24688 | |
| 24689 | const src_has_bits = src_elem_ty.hasRuntimeBits(zcu); |
| 24690 | const dest_has_bits = dest_elem_ty.hasRuntimeBits(zcu); |
| 24691 | assert(src_has_bits == dest_has_bits); // IMC |
| 24692 | if (src_has_bits) break :zero_bit; |
| 24693 | |
| 24694 | // The element type is zero-bit. We've done all validation (aside from the aliasing check, |
| 24695 | // which we must skip) so we're done. |
| 24696 | return; |
| 24697 | } |
| 24698 | |
| 24699 | const runtime_src = rs: { |
| 24700 | const dest_ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src; |
| 24701 | const src_ptr_val = try sema.resolveDefinedValue(block, src_src, src_ptr) orelse break :rs src_src; |
| 24702 | |
| 24703 | const raw_dest_ptr = if (dest_ty.isSlice(zcu)) dest_ptr_val.slicePtr(zcu) else dest_ptr_val; |
| 24704 | const raw_src_ptr = if (src_ty.isSlice(zcu)) src_ptr_val.slicePtr(zcu) else src_ptr_val; |
| 24705 | |
| 24706 | const len_u64 = len_val.?.toUnsignedInt(zcu); |
| 24707 | |
| 24708 | if (check_aliasing) { |
| 24709 | if (Value.doPointersOverlap( |
| 24710 | raw_src_ptr, |
| 24711 | raw_dest_ptr, |
| 24712 | len_u64, |
| 24713 | zcu, |
| 24714 | )) return sema.fail(block, src, "'@memcpy' arguments alias", .{}); |
| 24715 | } |
| 24716 | |
| 24717 | if (!sema.isComptimeMutablePtr(dest_ptr_val)) break :rs dest_src; |
| 24718 | |
| 24719 | // Because comptime pointer access is a somewhat expensive operation, we implement @memcpy |
| 24720 | // as one load and store of an array, rather than N loads and stores of individual elements. |
| 24721 | |
| 24722 | const array_ty = try pt.arrayType(.{ |
| 24723 | .child = dest_elem_ty.toIntern(), |
| 24724 | .len = len_u64, |
| 24725 | }); |
| 24726 | |
| 24727 | const dest_array_ptr_ty = try pt.ptrType(info: { |
| 24728 | var info = dest_ty.ptrInfo(zcu); |
| 24729 | info.flags.size = .one; |
| 24730 | info.child = array_ty.toIntern(); |
| 24731 | info.sentinel = .none; |
| 24732 | break :info info; |
| 24733 | }); |
| 24734 | const src_array_ptr_ty = try pt.ptrType(info: { |
| 24735 | var info = src_ty.ptrInfo(zcu); |
| 24736 | info.flags.size = .one; |
| 24737 | info.child = array_ty.toIntern(); |
| 24738 | info.sentinel = .none; |
| 24739 | break :info info; |
| 24740 | }); |
| 24741 | |
| 24742 | const coerced_dest_ptr = try pt.getCoerced(raw_dest_ptr, dest_array_ptr_ty); |
| 24743 | const coerced_src_ptr = try pt.getCoerced(raw_src_ptr, src_array_ptr_ty); |
| 24744 | |
| 24745 | const array_val = try sema.pointerDeref(block, src_src, coerced_src_ptr, src_array_ptr_ty) orelse break :rs src_src; |
| 24746 | try sema.storePtrVal(block, dest_src, coerced_dest_ptr, array_val, array_ty); |
| 24747 | return; |
| 24748 | }; |
| 24749 | |
| 24750 | // If the length is comptime-known, then upgrade src and destination types |
| 24751 | // into pointer-to-array. At this point we know they are both pointers |
| 24752 | // already. |
| 24753 | var new_dest_ptr = dest_ptr; |
| 24754 | var new_src_ptr = src_ptr; |
| 24755 | if (len_val) |val| { |
| 24756 | const len = val.toUnsignedInt(zcu); |
| 24757 | if (len == 0) { |
| 24758 | // This AIR instruction guarantees length > 0 if it is comptime-known. |
| 24759 | return; |
| 24760 | } |
| 24761 | new_dest_ptr = try upgradeToArrayPtr(sema, block, dest_ptr, len); |
| 24762 | new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len); |
| 24763 | } |
| 24764 | |
| 24765 | if (dest_len != .none) { |
| 24766 | // Change the src from slice to a many pointer, to avoid multiple ptr |
| 24767 | // slice extractions in AIR instructions. |
| 24768 | const new_src_ptr_ty = sema.typeOf(new_src_ptr); |
| 24769 | if (new_src_ptr_ty.isSlice(zcu)) { |
| 24770 | new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty); |
| 24771 | } |
| 24772 | } else if (dest_len == .none and len_val == null) { |
| 24773 | // Change the dest to a slice, since its type must have the length. |
| 24774 | const dest_ptr_ptr = try sema.analyzeRef(block, dest_src, new_dest_ptr, .none); |
| 24775 | new_dest_ptr = try sema.analyzeSlice(block, dest_src, dest_ptr_ptr, .zero, src_len, .none, LazySrcLoc.unneeded, dest_src, dest_src, dest_src, false); |
| 24776 | const new_src_ptr_ty = sema.typeOf(new_src_ptr); |
| 24777 | if (new_src_ptr_ty.isSlice(zcu)) { |
| 24778 | new_src_ptr = try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty); |
| 24779 | } |
| 24780 | } |
| 24781 | |
| 24782 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 24783 | try sema.validateRuntimeValue(block, dest_src, dest_ptr); |
| 24784 | try sema.validateRuntimeValue(block, src_src, src_ptr); |
| 24785 | |
| 24786 | // Aliasing safety check. |
| 24787 | if (check_aliasing and block.wantSafety()) { |
| 24788 | const len = if (len_val) |v| |
| 24789 | Air.internedToRef(v.toIntern()) |
| 24790 | else if (dest_len != .none) |
| 24791 | dest_len |
| 24792 | else |
| 24793 | src_len; |
| 24794 | |
| 24795 | // Extract raw pointer from dest slice. The AIR instructions could support them, but |
| 24796 | // it would cause redundant machine code instructions. |
| 24797 | const new_dest_ptr_ty = sema.typeOf(new_dest_ptr); |
| 24798 | const raw_dest_ptr = if (new_dest_ptr_ty.isSlice(zcu)) |
| 24799 | try sema.analyzeSlicePtr(block, dest_src, new_dest_ptr, new_dest_ptr_ty) |
| 24800 | else if (new_dest_ptr_ty.ptrSize(zcu) == .one) ptr: { |
| 24801 | var dest_manyptr_ty_key = zcu.intern_pool.indexToKey(new_dest_ptr_ty.toIntern()).ptr_type; |
| 24802 | assert(dest_manyptr_ty_key.flags.size == .one); |
| 24803 | dest_manyptr_ty_key.child = dest_elem_ty.toIntern(); |
| 24804 | dest_manyptr_ty_key.flags.size = .many; |
| 24805 | break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrType(dest_manyptr_ty_key), new_dest_ptr, dest_src); |
| 24806 | } else new_dest_ptr; |
| 24807 | |
| 24808 | const new_src_ptr_ty = sema.typeOf(new_src_ptr); |
| 24809 | const raw_src_ptr = if (new_src_ptr_ty.isSlice(zcu)) |
| 24810 | try sema.analyzeSlicePtr(block, src_src, new_src_ptr, new_src_ptr_ty) |
| 24811 | else if (new_src_ptr_ty.ptrSize(zcu) == .one) ptr: { |
| 24812 | var src_manyptr_ty_key = zcu.intern_pool.indexToKey(new_src_ptr_ty.toIntern()).ptr_type; |
| 24813 | assert(src_manyptr_ty_key.flags.size == .one); |
| 24814 | src_manyptr_ty_key.child = src_elem_ty.toIntern(); |
| 24815 | src_manyptr_ty_key.flags.size = .many; |
| 24816 | break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrType(src_manyptr_ty_key), new_src_ptr, src_src); |
| 24817 | } else new_src_ptr; |
| 24818 | |
| 24819 | // ok1: dest >= src + len |
| 24820 | // ok2: src >= dest + len |
| 24821 | const src_plus_len = try sema.analyzePtrArithmetic(block, src, raw_src_ptr, len, .ptr_add, src); |
| 24822 | const dest_plus_len = try sema.analyzePtrArithmetic(block, src, raw_dest_ptr, len, .ptr_add, src); |
| 24823 | const ok1 = try block.addBinOp(.cmp_gte, raw_dest_ptr, src_plus_len); |
| 24824 | const ok2 = try block.addBinOp(.cmp_gte, new_src_ptr, dest_plus_len); |
| 24825 | const ok = try block.addBinOp(.bit_or, ok1, ok2); |
| 24826 | try sema.addSafetyCheck(block, src, ok, .memcpy_alias); |
| 24827 | } |
| 24828 | |
| 24829 | _ = try block.addInst(.{ |
| 24830 | .tag = air_tag, |
| 24831 | .data = .{ .bin_op = .{ |
| 24832 | .lhs = new_dest_ptr, |
| 24833 | .rhs = new_src_ptr, |
| 24834 | } }, |
| 24835 | }); |
| 24836 | } |
| 24837 | |
| 24838 | fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { |
| 24839 | const pt = sema.pt; |
| 24840 | const zcu = pt.zcu; |
| 24841 | const comp = zcu.comp; |
| 24842 | const gpa = comp.gpa; |
| 24843 | const io = comp.io; |
| 24844 | const ip = &zcu.intern_pool; |
| 24845 | |
| 24846 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 24847 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 24848 | const src = block.nodeOffset(inst_data.src_node); |
| 24849 | const dest_src = block.builtinCallArgSrc(inst_data.src_node, 0); |
| 24850 | const value_src = block.builtinCallArgSrc(inst_data.src_node, 1); |
| 24851 | const dest_ptr = sema.resolveInst(extra.lhs); |
| 24852 | const uncoerced_elem = sema.resolveInst(extra.rhs); |
| 24853 | const dest_ptr_ty = sema.typeOf(dest_ptr); |
| 24854 | try checkMemOperand(sema, block, dest_src, dest_ptr_ty); |
| 24855 | |
| 24856 | if (dest_ptr_ty.isConstPtr(zcu)) { |
| 24857 | return sema.fail(block, dest_src, "cannot memset constant pointer", .{}); |
| 24858 | } |
| 24859 | |
| 24860 | const dest_elem_ty: Type = dest_elem_ty: { |
| 24861 | const ptr_info = dest_ptr_ty.ptrInfo(zcu); |
| 24862 | switch (ptr_info.flags.size) { |
| 24863 | .slice => break :dest_elem_ty .fromInterned(ptr_info.child), |
| 24864 | .one => { |
| 24865 | if (Type.fromInterned(ptr_info.child).zigTypeTag(zcu) == .array) { |
| 24866 | break :dest_elem_ty Type.fromInterned(ptr_info.child).childType(zcu); |
| 24867 | } |
| 24868 | }, |
| 24869 | .many, .c => {}, |
| 24870 | } |
| 24871 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 24872 | const msg = try sema.errMsg(src, "unknown @memset length", .{}); |
| 24873 | errdefer msg.destroy(sema.gpa); |
| 24874 | try sema.errNote(dest_src, msg, "destination type '{f}' provides no length", .{ |
| 24875 | dest_ptr_ty.fmt(pt), |
| 24876 | }); |
| 24877 | break :msg msg; |
| 24878 | }); |
| 24879 | }; |
| 24880 | |
| 24881 | const elem = try sema.coerce(block, dest_elem_ty, uncoerced_elem, value_src); |
| 24882 | |
| 24883 | const comptime_only_elem = switch (dest_elem_ty.classify(zcu)) { |
| 24884 | .no_possible_value => unreachable, // `elem` is a value of this type |
| 24885 | .one_possible_value => return, // no work to do |
| 24886 | .runtime => false, |
| 24887 | .partially_comptime, .fully_comptime => true, |
| 24888 | }; |
| 24889 | |
| 24890 | const runtime_src = rs: { |
| 24891 | const len_air_ref = try sema.fieldVal(block, src, dest_ptr, try ip.getOrPutString(gpa, io, pt.tid, "len", .no_embedded_nulls), dest_src); |
| 24892 | const len_val = (try sema.resolveDefinedValue(block, dest_src, len_air_ref)) orelse break :rs dest_src; |
| 24893 | const len_u64 = len_val.toUnsignedInt(zcu); |
| 24894 | const len = try sema.usizeCast(block, dest_src, len_u64); |
| 24895 | if (len == 0) { |
| 24896 | // This AIR instruction guarantees length > 0 if it is comptime-known. |
| 24897 | return; |
| 24898 | } |
| 24899 | |
| 24900 | const ptr_val = try sema.resolveDefinedValue(block, dest_src, dest_ptr) orelse break :rs dest_src; |
| 24901 | if (!sema.isComptimeMutablePtr(ptr_val)) break :rs dest_src; |
| 24902 | const elem_val = sema.resolveValue(elem) orelse break :rs value_src; |
| 24903 | const array_ty = try pt.arrayType(.{ |
| 24904 | .child = dest_elem_ty.toIntern(), |
| 24905 | .len = len_u64, |
| 24906 | }); |
| 24907 | const array_val = try pt.aggregateSplatValue(array_ty, elem_val); |
| 24908 | const array_ptr_ty = ty: { |
| 24909 | var info = dest_ptr_ty.ptrInfo(zcu); |
| 24910 | info.flags.size = .one; |
| 24911 | info.child = array_ty.toIntern(); |
| 24912 | break :ty try pt.ptrType(info); |
| 24913 | }; |
| 24914 | const raw_ptr_val = if (dest_ptr_ty.isSlice(zcu)) ptr_val.slicePtr(zcu) else ptr_val; |
| 24915 | const array_ptr_val = try pt.getCoerced(raw_ptr_val, array_ptr_ty); |
| 24916 | return sema.storePtrVal(block, src, array_ptr_val, array_val, array_ty); |
| 24917 | }; |
| 24918 | |
| 24919 | if (comptime_only_elem) { |
| 24920 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 24921 | const msg = try sema.errMsg(src, "cannot store comptime-only element '{f}' at runtime", .{dest_elem_ty.fmt(pt)}); |
| 24922 | errdefer msg.destroy(sema.gpa); |
| 24923 | try sema.errNote(dest_src, msg, "operation is runtime due to destination pointer", .{}); |
| 24924 | break :msg msg; |
| 24925 | }); |
| 24926 | } |
| 24927 | |
| 24928 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 24929 | try sema.validateRuntimeValue(block, dest_src, dest_ptr); |
| 24930 | try sema.validateRuntimeValue(block, value_src, elem); |
| 24931 | |
| 24932 | _ = try block.addInst(.{ |
| 24933 | .tag = if (block.wantSafety()) .memset_safe else .memset, |
| 24934 | .data = .{ .bin_op = .{ |
| 24935 | .lhs = dest_ptr, |
| 24936 | .rhs = elem, |
| 24937 | } }, |
| 24938 | }); |
| 24939 | } |
| 24940 | |
| 24941 | fn zirResume(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 24942 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].un_node; |
| 24943 | const src = block.nodeOffset(inst_data.src_node); |
| 24944 | return sema.failWithUseOfAsync(block, src); |
| 24945 | } |
| 24946 | |
| 24947 | fn zirFuncFancy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 24948 | const pt = sema.pt; |
| 24949 | const zcu = pt.zcu; |
| 24950 | const comp = zcu.comp; |
| 24951 | const gpa = comp.gpa; |
| 24952 | const io = comp.io; |
| 24953 | const ip = &zcu.intern_pool; |
| 24954 | |
| 24955 | const inst_data = sema.code.instructions.items(.data)[@backingInt(inst)].pl_node; |
| 24956 | const extra = sema.code.extraData(Zir.Inst.FuncFancy, inst_data.payload_index); |
| 24957 | const target = zcu.getTarget(); |
| 24958 | |
| 24959 | const cc_src = block.src(.{ .node_offset_fn_type_cc = inst_data.src_node }); |
| 24960 | const ret_src = block.src(.{ .node_offset_fn_type_ret_ty = inst_data.src_node }); |
| 24961 | const has_body = extra.data.body_len != 0; |
| 24962 | |
| 24963 | var extra_index: usize = extra.end; |
| 24964 | |
| 24965 | const cc: std.lang.CallingConvention = if (extra.data.bits.has_cc_body) blk: { |
| 24966 | const body_len = sema.code.extra[extra_index]; |
| 24967 | extra_index += 1; |
| 24968 | const body = sema.code.bodySlice(extra_index, body_len); |
| 24969 | extra_index += body.len; |
| 24970 | |
| 24971 | const cc_ty = try sema.getStdLangType(cc_src, .CallingConvention); |
| 24972 | const val = try sema.resolveGenericBody(block, cc_src, body, inst, cc_ty, .{ .simple = .@"callconv" }); |
| 24973 | break :blk try sema.analyzeValueAsCallconv(block, cc_src, val); |
| 24974 | } else if (extra.data.bits.has_cc_ref) blk: { |
| 24975 | const cc_ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_index])); |
| 24976 | extra_index += 1; |
| 24977 | const cc_ty = try sema.getStdLangType(cc_src, .CallingConvention); |
| 24978 | const uncoerced_cc = sema.resolveInst(cc_ref); |
| 24979 | const coerced_cc = try sema.coerce(block, cc_ty, uncoerced_cc, cc_src); |
| 24980 | const cc_val = try sema.resolveConstDefinedValue(block, cc_src, coerced_cc, .{ .simple = .@"callconv" }); |
| 24981 | break :blk try sema.analyzeValueAsCallconv(block, cc_src, cc_val); |
| 24982 | } else cc: { |
| 24983 | if (has_body) { |
| 24984 | const func_decl_nav = sema.owner.unwrap().nav_val; |
| 24985 | const func_decl_ti = ip.getNav(func_decl_nav).analysis.?.zir_index; |
| 24986 | const func_decl_inst = func_decl_ti.resolve(&zcu.intern_pool) orelse { |
| 24987 | return sema.failTransitive(.{ .lost_tracking = func_decl_ti }); |
| 24988 | }; |
| 24989 | const zir_decl = sema.code.getDeclaration(func_decl_inst); |
| 24990 | if (zir_decl.linkage == .@"export") { |
| 24991 | break :cc target.cCallingConvention() orelse { |
| 24992 | // This target has no default C calling convention. We sometimes trigger a similar |
| 24993 | // error by trying to evaluate `std.lang.CallingConvention.c`, so for consistency, |
| 24994 | // let's eval that now and just get the transitive error. (It's guaranteed to error |
| 24995 | // because it does the exact `cCallingConvention` call we just did.) |
| 24996 | const cc_type = try sema.getStdLangType(cc_src, .CallingConvention); |
| 24997 | _ = try sema.namespaceLookupVal( |
| 24998 | block, |
| 24999 | LazySrcLoc.unneeded, |
| 25000 | cc_type.getNamespaceIndex(zcu), |
| 25001 | try ip.getOrPutString(gpa, io, pt.tid, "c", .no_embedded_nulls), |
| 25002 | ); |
| 25003 | // The above should have errored. |
| 25004 | @panic("std.lang is corrupt"); |
| 25005 | }; |
| 25006 | } |
| 25007 | } |
| 25008 | break :cc .auto; |
| 25009 | }; |
| 25010 | |
| 25011 | const ret_ty: Type = if (extra.data.bits.has_ret_ty_body) blk: { |
| 25012 | const body_len = sema.code.extra[extra_index]; |
| 25013 | extra_index += 1; |
| 25014 | const body = sema.code.bodySlice(extra_index, body_len); |
| 25015 | extra_index += body.len; |
| 25016 | if (extra.data.bits.ret_ty_is_generic) break :blk .generic_poison; |
| 25017 | |
| 25018 | const val = try sema.resolveGenericBody(block, ret_src, body, inst, .type, .{ .simple = .fn_ret_ty }); |
| 25019 | const ty = val.toType(); |
| 25020 | break :blk ty; |
| 25021 | } else if (extra.data.bits.has_ret_ty_ref) blk: { |
| 25022 | const ret_ty_ref: Zir.Inst.Ref = @fromBackingInt(@intCast(sema.code.extra[extra_index])); |
| 25023 | extra_index += 1; |
| 25024 | if (extra.data.bits.ret_ty_is_generic) break :blk .generic_poison; |
| 25025 | |
| 25026 | break :blk try sema.resolveType(block, ret_src, ret_ty_ref); |
| 25027 | } else .void; |
| 25028 | |
| 25029 | const noalias_bits: u32 = if (extra.data.bits.has_any_noalias) blk: { |
| 25030 | const x = sema.code.extra[extra_index]; |
| 25031 | extra_index += 1; |
| 25032 | break :blk x; |
| 25033 | } else 0; |
| 25034 | |
| 25035 | var src_locs: Zir.Inst.Func.SrcLocs = undefined; |
| 25036 | if (has_body) { |
| 25037 | extra_index += extra.data.body_len; |
| 25038 | src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; |
| 25039 | } |
| 25040 | |
| 25041 | const is_var_args = extra.data.bits.is_var_args; |
| 25042 | const is_inferred_error = extra.data.bits.is_inferred_error; |
| 25043 | const is_noinline = extra.data.bits.is_noinline; |
| 25044 | |
| 25045 | return sema.funcCommon( |
| 25046 | block, |
| 25047 | inst_data.src_node, |
| 25048 | inst, |
| 25049 | cc, |
| 25050 | ret_ty, |
| 25051 | is_var_args, |
| 25052 | is_inferred_error, |
| 25053 | has_body, |
| 25054 | src_locs, |
| 25055 | noalias_bits, |
| 25056 | is_noinline, |
| 25057 | ); |
| 25058 | } |
| 25059 | |
| 25060 | fn zirWasmMemorySize( |
| 25061 | sema: *Sema, |
| 25062 | block: *Block, |
| 25063 | extended: Zir.Inst.Extended.InstData, |
| 25064 | ) CompileError!Air.Inst.Ref { |
| 25065 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 25066 | const index_src = block.builtinCallArgSrc(extra.node, 0); |
| 25067 | const builtin_src = block.nodeOffset(extra.node); |
| 25068 | const target = sema.pt.zcu.getTarget(); |
| 25069 | if (!target.cpu.arch.isWasm()) { |
| 25070 | return sema.fail(block, builtin_src, "builtin @wasmMemorySize is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); |
| 25071 | } |
| 25072 | |
| 25073 | const index: u32 = @intCast(try sema.resolveInt(block, index_src, extra.operand, .u32, .{ .simple = .wasm_memory_index })); |
| 25074 | try sema.requireRuntimeBlock(block, builtin_src, null); |
| 25075 | return block.addInst(.{ |
| 25076 | .tag = .wasm_memory_size, |
| 25077 | .data = .{ .pl_op = .{ |
| 25078 | .operand = .none, |
| 25079 | .payload = index, |
| 25080 | } }, |
| 25081 | }); |
| 25082 | } |
| 25083 | |
| 25084 | fn zirWasmMemoryGrow( |
| 25085 | sema: *Sema, |
| 25086 | block: *Block, |
| 25087 | extended: Zir.Inst.Extended.InstData, |
| 25088 | ) CompileError!Air.Inst.Ref { |
| 25089 | const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; |
| 25090 | const builtin_src = block.nodeOffset(extra.node); |
| 25091 | const index_src = block.builtinCallArgSrc(extra.node, 0); |
| 25092 | const delta_src = block.builtinCallArgSrc(extra.node, 1); |
| 25093 | const target = sema.pt.zcu.getTarget(); |
| 25094 | if (!target.cpu.arch.isWasm()) { |
| 25095 | return sema.fail(block, builtin_src, "builtin @wasmMemoryGrow is available when targeting WebAssembly; targeted CPU architecture is {s}", .{@tagName(target.cpu.arch)}); |
| 25096 | } |
| 25097 | |
| 25098 | const index: u32 = @intCast(try sema.resolveInt(block, index_src, extra.lhs, .u32, .{ .simple = .wasm_memory_index })); |
| 25099 | const delta = try sema.coerce(block, .usize, sema.resolveInst(extra.rhs), delta_src); |
| 25100 | |
| 25101 | try sema.requireRuntimeBlock(block, builtin_src, null); |
| 25102 | return block.addInst(.{ |
| 25103 | .tag = .wasm_memory_grow, |
| 25104 | .data = .{ .pl_op = .{ |
| 25105 | .operand = delta, |
| 25106 | .payload = index, |
| 25107 | } }, |
| 25108 | }); |
| 25109 | } |
| 25110 | |
| 25111 | fn resolvePrefetchOptions( |
| 25112 | sema: *Sema, |
| 25113 | block: *Block, |
| 25114 | src: LazySrcLoc, |
| 25115 | zir_ref: Zir.Inst.Ref, |
| 25116 | ) CompileError!std.lang.PrefetchOptions { |
| 25117 | const pt = sema.pt; |
| 25118 | const zcu = pt.zcu; |
| 25119 | const comp = zcu.comp; |
| 25120 | const gpa = comp.gpa; |
| 25121 | const io = comp.io; |
| 25122 | const ip = &zcu.intern_pool; |
| 25123 | |
| 25124 | const options_ty = try sema.getStdLangType(src, .PrefetchOptions); |
| 25125 | const options = try sema.coerce(block, options_ty, sema.resolveInst(zir_ref), src); |
| 25126 | |
| 25127 | const rw_src = block.src(.{ .init_field_rw = src.offset.node_offset_builtin_call_arg.builtin_call_node }); |
| 25128 | const locality_src = block.src(.{ .init_field_locality = src.offset.node_offset_builtin_call_arg.builtin_call_node }); |
| 25129 | const cache_src = block.src(.{ .init_field_cache = src.offset.node_offset_builtin_call_arg.builtin_call_node }); |
| 25130 | |
| 25131 | const rw = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "rw", .no_embedded_nulls), rw_src); |
| 25132 | const rw_val = try sema.resolveConstDefinedValue(block, rw_src, rw, .{ .simple = .prefetch_options }); |
| 25133 | |
| 25134 | const locality = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "locality", .no_embedded_nulls), locality_src); |
| 25135 | const locality_val = try sema.resolveConstDefinedValue(block, locality_src, locality, .{ .simple = .prefetch_options }); |
| 25136 | |
| 25137 | const cache = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "cache", .no_embedded_nulls), cache_src); |
| 25138 | const cache_val = try sema.resolveConstDefinedValue(block, cache_src, cache, .{ .simple = .prefetch_options }); |
| 25139 | |
| 25140 | return std.lang.PrefetchOptions{ |
| 25141 | .rw = try sema.interpretStdLangType(block, rw_src, rw_val, std.lang.PrefetchOptions.Rw), |
| 25142 | .locality = @intCast(locality_val.toUnsignedInt(zcu)), |
| 25143 | .cache = try sema.interpretStdLangType(block, cache_src, cache_val, std.lang.PrefetchOptions.Cache), |
| 25144 | }; |
| 25145 | } |
| 25146 | |
| 25147 | fn zirPrefetch( |
| 25148 | sema: *Sema, |
| 25149 | block: *Block, |
| 25150 | extended: Zir.Inst.Extended.InstData, |
| 25151 | ) CompileError!Air.Inst.Ref { |
| 25152 | const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; |
| 25153 | const ptr_src = block.builtinCallArgSrc(extra.node, 0); |
| 25154 | const opts_src = block.builtinCallArgSrc(extra.node, 1); |
| 25155 | const ptr = sema.resolveInst(extra.lhs); |
| 25156 | try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr)); |
| 25157 | |
| 25158 | const options = try sema.resolvePrefetchOptions(block, opts_src, extra.rhs); |
| 25159 | |
| 25160 | if (!block.isComptime()) { |
| 25161 | _ = try block.addInst(.{ |
| 25162 | .tag = .prefetch, |
| 25163 | .data = .{ .prefetch = .{ |
| 25164 | .ptr = ptr, |
| 25165 | .rw = options.rw, |
| 25166 | .locality = options.locality, |
| 25167 | .cache = options.cache, |
| 25168 | } }, |
| 25169 | }); |
| 25170 | } |
| 25171 | |
| 25172 | return .void_value; |
| 25173 | } |
| 25174 | |
| 25175 | fn resolveExternOptions( |
| 25176 | sema: *Sema, |
| 25177 | block: *Block, |
| 25178 | src: LazySrcLoc, |
| 25179 | zir_ref: Zir.Inst.Ref, |
| 25180 | ) CompileError!struct { |
| 25181 | name: InternPool.NullTerminatedString, |
| 25182 | library_name: InternPool.OptionalNullTerminatedString, |
| 25183 | linkage: std.lang.GlobalLinkage, |
| 25184 | visibility: std.lang.SymbolVisibility, |
| 25185 | is_thread_local: bool, |
| 25186 | is_dll_import: bool, |
| 25187 | relocation: std.lang.ExternOptions.Relocation, |
| 25188 | decoration: ?std.lang.ExternOptions.Decoration, |
| 25189 | } { |
| 25190 | const pt = sema.pt; |
| 25191 | const zcu = pt.zcu; |
| 25192 | const comp = zcu.comp; |
| 25193 | const gpa = comp.gpa; |
| 25194 | const io = comp.io; |
| 25195 | const ip = &zcu.intern_pool; |
| 25196 | |
| 25197 | const options_inst = sema.resolveInst(zir_ref); |
| 25198 | const extern_options_ty = try sema.getStdLangType(src, .ExternOptions); |
| 25199 | const options = try sema.coerce(block, extern_options_ty, options_inst, src); |
| 25200 | |
| 25201 | const name_src = block.src(.{ .init_field_name = src.offset.node_offset_builtin_call_arg.builtin_call_node }); |
| 25202 | const library_src = block.src(.{ .init_field_library = src.offset.node_offset_builtin_call_arg.builtin_call_node }); |
| 25203 | const linkage_src = block.src(.{ .init_field_linkage = src.offset.node_offset_builtin_call_arg.builtin_call_node }); |
| 25204 | const visibility_src = block.src(.{ .init_field_visibility = src.offset.node_offset_builtin_call_arg.builtin_call_node }); |
| 25205 | const thread_local_src = block.src(.{ .init_field_thread_local = src.offset.node_offset_builtin_call_arg.builtin_call_node }); |
| 25206 | const dll_import_src = block.src(.{ .init_field_dll_import = src.offset.node_offset_builtin_call_arg.builtin_call_node }); |
| 25207 | const relocation_src = block.src(.{ .init_field_relocation = src.offset.node_offset_builtin_call_arg.builtin_call_node }); |
| 25208 | const decoration_src = block.src(.{ .init_field_decoration = src.offset.node_offset_builtin_call_arg.builtin_call_node }); |
| 25209 | |
| 25210 | const name_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "name", .no_embedded_nulls), name_src); |
| 25211 | const name = try sema.toConstString(block, name_src, name_ref, .{ .simple = .extern_options }); |
| 25212 | |
| 25213 | const library_name_inst = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "library_name", .no_embedded_nulls), library_src); |
| 25214 | const library_name_val = try sema.resolveConstDefinedValue(block, library_src, library_name_inst, .{ .simple = .extern_options }); |
| 25215 | |
| 25216 | const linkage_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "linkage", .no_embedded_nulls), linkage_src); |
| 25217 | const linkage_val = try sema.resolveConstDefinedValue(block, linkage_src, linkage_ref, .{ .simple = .extern_options }); |
| 25218 | const linkage = try sema.interpretStdLangType(block, linkage_src, linkage_val, std.lang.GlobalLinkage); |
| 25219 | |
| 25220 | const visibility_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "visibility", .no_embedded_nulls), visibility_src); |
| 25221 | const visibility_val = try sema.resolveConstDefinedValue(block, visibility_src, visibility_ref, .{ .simple = .extern_options }); |
| 25222 | const visibility = try sema.interpretStdLangType(block, visibility_src, visibility_val, std.lang.SymbolVisibility); |
| 25223 | |
| 25224 | const is_thread_local = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "is_thread_local", .no_embedded_nulls), thread_local_src); |
| 25225 | const is_thread_local_val = try sema.resolveConstDefinedValue(block, thread_local_src, is_thread_local, .{ .simple = .extern_options }); |
| 25226 | |
| 25227 | const library_name = if (library_name_val.optionalValue(zcu)) |library_name_payload| library_name: { |
| 25228 | const library_name = try sema.toConstString(block, library_src, Air.internedToRef(library_name_payload.toIntern()), .{ .simple = .extern_options }); |
| 25229 | if (library_name.len == 0) { |
| 25230 | return sema.fail(block, library_src, "library name cannot be empty", .{}); |
| 25231 | } |
| 25232 | try sema.handleExternLibName(block, library_src, library_name); |
| 25233 | break :library_name library_name; |
| 25234 | } else null; |
| 25235 | |
| 25236 | const is_dll_import_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "is_dll_import", .no_embedded_nulls), dll_import_src); |
| 25237 | const is_dll_import_val = try sema.resolveConstDefinedValue(block, dll_import_src, is_dll_import_ref, .{ .simple = .extern_options }); |
| 25238 | |
| 25239 | const relocation_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "relocation", .no_embedded_nulls), relocation_src); |
| 25240 | const relocation_val = try sema.resolveConstDefinedValue(block, relocation_src, relocation_ref, .{ .simple = .extern_options }); |
| 25241 | const relocation = try sema.interpretStdLangType(block, relocation_src, relocation_val, std.lang.ExternOptions.Relocation); |
| 25242 | |
| 25243 | const decoration_ref = try sema.fieldVal(block, src, options, try ip.getOrPutString(gpa, io, pt.tid, "decoration", .no_embedded_nulls), decoration_src); |
| 25244 | const decoration_val = try sema.resolveConstDefinedValue(block, decoration_src, decoration_ref, .{ .simple = .extern_options }); |
| 25245 | const decoration = try sema.interpretStdLangType(block, decoration_src, decoration_val, ?std.lang.ExternOptions.Decoration); |
| 25246 | |
| 25247 | if (name.len == 0) { |
| 25248 | return sema.fail(block, name_src, "extern symbol name cannot be empty", .{}); |
| 25249 | } |
| 25250 | |
| 25251 | if (linkage != .weak and linkage != .strong) { |
| 25252 | return sema.fail(block, linkage_src, "extern symbol must use strong or weak linkage", .{}); |
| 25253 | } |
| 25254 | |
| 25255 | return .{ |
| 25256 | .name = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls), |
| 25257 | .library_name = try ip.getOrPutStringOpt(gpa, io, pt.tid, library_name, .no_embedded_nulls), |
| 25258 | .linkage = linkage, |
| 25259 | .visibility = visibility, |
| 25260 | .is_thread_local = is_thread_local_val.toBool(), |
| 25261 | .is_dll_import = is_dll_import_val.toBool(), |
| 25262 | .relocation = relocation, |
| 25263 | .decoration = decoration, |
| 25264 | }; |
| 25265 | } |
| 25266 | |
| 25267 | fn zirBuiltinExtern( |
| 25268 | sema: *Sema, |
| 25269 | block: *Block, |
| 25270 | extended: Zir.Inst.Extended.InstData, |
| 25271 | ) CompileError!Air.Inst.Ref { |
| 25272 | const pt = sema.pt; |
| 25273 | const zcu = pt.zcu; |
| 25274 | const ip = &zcu.intern_pool; |
| 25275 | const extra = sema.code.extraData(Zir.Inst.BinNode, extended.operand).data; |
| 25276 | const src = block.nodeOffset(extra.node); |
| 25277 | const ty_src = block.builtinCallArgSrc(extra.node, 0); |
| 25278 | const options_src = block.builtinCallArgSrc(extra.node, 1); |
| 25279 | |
| 25280 | const ptr_ty = try sema.resolveType(block, ty_src, extra.lhs); |
| 25281 | if (!ptr_ty.isPtrAtRuntime(zcu)) { |
| 25282 | return sema.fail(block, ty_src, "expected (optional) pointer", .{}); |
| 25283 | } |
| 25284 | |
| 25285 | const ptr_info = ptr_ty.ptrInfo(zcu); |
| 25286 | |
| 25287 | const elem_ty: Type = .fromInterned(ptr_info.child); |
| 25288 | try sema.ensureLayoutResolved(elem_ty, src, .@"extern"); |
| 25289 | |
| 25290 | if (!elem_ty.validateExtern(.other, zcu)) { |
| 25291 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 25292 | const msg = try sema.errMsg(ty_src, "extern symbol cannot have type '{f}'", .{ptr_ty.fmt(pt)}); |
| 25293 | errdefer msg.destroy(sema.gpa); |
| 25294 | try sema.errNote(ty_src, msg, "pointer element type '{f}' is not extern compatible", .{elem_ty.fmt(pt)}); |
| 25295 | try sema.explainWhyTypeIsNotExtern(msg, ty_src, elem_ty, .other); |
| 25296 | break :msg msg; |
| 25297 | }); |
| 25298 | } |
| 25299 | if (elem_ty.zigTypeTag(zcu) == .@"fn" and !ptr_info.flags.is_const) { |
| 25300 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 25301 | const msg = try sema.errMsg(ty_src, "extern symbol cannot have type '{f}'", .{ptr_ty.fmt(pt)}); |
| 25302 | errdefer msg.destroy(sema.gpa); |
| 25303 | try sema.errNote(ty_src, msg, "pointer to extern function must be 'const'", .{}); |
| 25304 | break :msg msg; |
| 25305 | }); |
| 25306 | } |
| 25307 | |
| 25308 | const options = try sema.resolveExternOptions(block, options_src, extra.rhs); |
| 25309 | switch (options.linkage) { |
| 25310 | .internal => if (options.visibility != .default) { |
| 25311 | return sema.fail(block, options_src, "internal symbol cannot have non-default visibility", .{}); |
| 25312 | }, |
| 25313 | .strong, .weak => {}, |
| 25314 | .link_once => return sema.fail(block, options_src, "external symbol cannot have link once linkage", .{}), |
| 25315 | } |
| 25316 | switch (options.relocation) { |
| 25317 | .any => {}, |
| 25318 | .pcrel => if (options.visibility == .default) return sema.fail(block, options_src, "cannot require a pc-relative relocation to a symbol with default visibility", .{}), |
| 25319 | } |
| 25320 | |
| 25321 | if (options.decoration) |decoration| switch (decoration) { |
| 25322 | .flat => switch (ptr_info.flags.address_space) { |
| 25323 | .input, .output => {}, |
| 25324 | else => return sema.fail(block, options_src, "\"flat\" decoration requires \"input\" or \"output\" address space", .{}), |
| 25325 | }, |
| 25326 | .location, .descriptor => {}, |
| 25327 | }; |
| 25328 | |
| 25329 | const target = zcu.getTarget(); |
| 25330 | switch (target.os.tag) { |
| 25331 | .vulkan, .opengl => { |
| 25332 | const pointee = switch (elem_ty.zigTypeTag(zcu)) { |
| 25333 | .array => elem_ty.childType(zcu), |
| 25334 | .spirv => if (elem_ty.isSpirvRuntimeArray(zcu)) elem_ty.childType(zcu) else elem_ty, |
| 25335 | else => elem_ty, |
| 25336 | }; |
| 25337 | switch (ptr_info.flags.address_space) { |
| 25338 | .uniform, |
| 25339 | .storage_buffer, |
| 25340 | => if (ptr_info.flags.size != .one or pointee.zigTypeTag(zcu) != .@"struct") { |
| 25341 | return sema.fail(block, ty_src, "extern in '{t}' address space must be a single-item pointer to a struct", .{ptr_info.flags.address_space}); |
| 25342 | }, |
| 25343 | .push_constant => if (ptr_info.flags.size != .one or elem_ty.zigTypeTag(zcu) != .@"struct") { |
| 25344 | return sema.fail(block, ty_src, "extern in 'push_constant' address space must be a single-item pointer to a struct", .{}); |
| 25345 | }, |
| 25346 | .constant => if (target.os.tag == .vulkan and (pointee.zigTypeTag(zcu) != .spirv or pointee.isSpirvRuntimeArray(zcu))) { |
| 25347 | return sema.fail(block, ty_src, "extern in 'constant' address space must point to an opaque SPIR-V type, or to an array of one", .{}); |
| 25348 | }, |
| 25349 | else => if (elem_ty.isSpirvRuntimeArray(zcu)) { |
| 25350 | return sema.fail(block, ty_src, "SPIR-V runtime array is not allowed in the '{t}' address space", .{ptr_info.flags.address_space}); |
| 25351 | }, |
| 25352 | } |
| 25353 | }, |
| 25354 | else => {}, |
| 25355 | } |
| 25356 | |
| 25357 | // TODO: error for threadlocal functions, non-const functions, etc |
| 25358 | |
| 25359 | const extern_val = try pt.getExtern(.{ |
| 25360 | .name = options.name, |
| 25361 | .ty = elem_ty.toIntern(), |
| 25362 | .lib_name = options.library_name, |
| 25363 | .linkage = options.linkage, |
| 25364 | .visibility = options.visibility, |
| 25365 | .is_threadlocal = options.is_thread_local, |
| 25366 | .is_dll_import = options.is_dll_import, |
| 25367 | .relocation = options.relocation, |
| 25368 | .decoration = options.decoration, |
| 25369 | .is_const = ptr_info.flags.is_const, |
| 25370 | .alignment = ptr_info.flags.alignment, |
| 25371 | .@"addrspace" = ptr_info.flags.address_space, |
| 25372 | // This instruction is just for source locations. |
| 25373 | // `builtin_extern` doesn't provide enough information, and isn't currently tracked. |
| 25374 | // So, for now, just use our containing `declaration`. |
| 25375 | .zir_index = switch (sema.owner.unwrap()) { |
| 25376 | .@"comptime" => |cu| ip.getComptimeUnit(cu).zir_index, |
| 25377 | .type_layout, .struct_defaults => |owner_ty| Type.fromInterned(owner_ty).typeDeclInstAllowGeneratedTag(zcu).?, |
| 25378 | .memoized_state => unreachable, |
| 25379 | .nav_ty, .nav_val => |nav| ip.getNav(nav).analysis.?.zir_index, |
| 25380 | .func => |func| zir_index: { |
| 25381 | const func_info = zcu.funcInfo(func); |
| 25382 | const owner_func_info = if (func_info.generic_owner != .none) owner: { |
| 25383 | break :owner zcu.funcInfo(func_info.generic_owner); |
| 25384 | } else func_info; |
| 25385 | break :zir_index ip.getNav(owner_func_info.owner_nav).analysis.?.zir_index; |
| 25386 | }, |
| 25387 | }, |
| 25388 | .owner_nav = undefined, // ignored by `getExtern` |
| 25389 | .source = .builtin, |
| 25390 | }); |
| 25391 | |
| 25392 | // For a weak symbol where the given type is not nullable, make the pointer optional. |
| 25393 | const result_ptr_ty: Type = if (options.linkage == .weak and !ptr_ty.ptrAllowsZero(zcu)) ty: { |
| 25394 | break :ty try pt.optionalType(ptr_ty.toIntern()); |
| 25395 | } else ptr_ty; |
| 25396 | |
| 25397 | const uncasted_ptr = try sema.analyzeNavRef(block, src, ip.indexToKey(extern_val).@"extern".owner_nav); |
| 25398 | if (sema.resolveValue(uncasted_ptr)) |uncasted_ptr_val| { |
| 25399 | const casted_ptr_val = try pt.getCoerced(uncasted_ptr_val, result_ptr_ty); |
| 25400 | return Air.internedToRef(casted_ptr_val.toIntern()); |
| 25401 | } else { |
| 25402 | return block.addTyOp(.ptr_cast, result_ptr_ty, uncasted_ptr); |
| 25403 | } |
| 25404 | } |
| 25405 | |
| 25406 | fn zirWorkItem( |
| 25407 | sema: *Sema, |
| 25408 | block: *Block, |
| 25409 | extended: Zir.Inst.Extended.InstData, |
| 25410 | zir_tag: Zir.Inst.Extended, |
| 25411 | ) CompileError!Air.Inst.Ref { |
| 25412 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 25413 | const dimension_src = block.builtinCallArgSrc(extra.node, 0); |
| 25414 | const builtin_src = block.nodeOffset(extra.node); |
| 25415 | const target = sema.pt.zcu.getTarget(); |
| 25416 | |
| 25417 | switch (target.cpu.arch) { |
| 25418 | // TODO: Allow for other GPU targets. |
| 25419 | .amdgcn, .spirv64, .spirv32, .nvptx, .nvptx64 => {}, |
| 25420 | else => { |
| 25421 | return sema.fail(block, builtin_src, "builtin only available on GPU targets; targeted architecture is {s}", .{@tagName(target.cpu.arch)}); |
| 25422 | }, |
| 25423 | } |
| 25424 | |
| 25425 | const dimension: u32 = @intCast(try sema.resolveInt(block, dimension_src, extra.operand, .u32, .{ .simple = .work_group_dim_index })); |
| 25426 | try sema.requireRuntimeBlock(block, builtin_src, null); |
| 25427 | |
| 25428 | return block.addInst(.{ |
| 25429 | .tag = switch (zir_tag) { |
| 25430 | .work_item_id => .work_item_id, |
| 25431 | .work_group_size => .work_group_size, |
| 25432 | .work_group_id => .work_group_id, |
| 25433 | else => unreachable, |
| 25434 | }, |
| 25435 | .data = .{ .pl_op = .{ |
| 25436 | .operand = .none, |
| 25437 | .payload = dimension, |
| 25438 | } }, |
| 25439 | }); |
| 25440 | } |
| 25441 | |
| 25442 | fn zirInComptime( |
| 25443 | sema: *Sema, |
| 25444 | block: *Block, |
| 25445 | ) CompileError!Air.Inst.Ref { |
| 25446 | _ = sema; |
| 25447 | return if (block.isComptime()) .bool_true else .bool_false; |
| 25448 | } |
| 25449 | |
| 25450 | fn zirStdLangValue(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 25451 | const pt = sema.pt; |
| 25452 | const zcu = pt.zcu; |
| 25453 | const comp = zcu.comp; |
| 25454 | const gpa = comp.gpa; |
| 25455 | const io = comp.io; |
| 25456 | const ip = &zcu.intern_pool; |
| 25457 | |
| 25458 | const src_node: std.zig.Ast.Node.Offset = @fromBackingInt(@intCast(@as(i32, @bitCast(extended.operand)))); |
| 25459 | const src = block.nodeOffset(src_node); |
| 25460 | const value: Zir.Inst.StdLangValue = @fromBackingInt(@intCast(extended.small)); |
| 25461 | |
| 25462 | const std_lang_type: Zcu.StdLangDecl = switch (value) { |
| 25463 | // zig fmt: off |
| 25464 | .atomic_order => .AtomicOrder, |
| 25465 | .atomic_rmw_op => .AtomicRmwOp, |
| 25466 | .calling_convention => .CallingConvention, |
| 25467 | .address_space => .AddressSpace, |
| 25468 | .float_mode => .FloatMode, |
| 25469 | .signedness => .Signedness, |
| 25470 | .reduce_op => .ReduceOp, |
| 25471 | .call_modifier => .CallModifier, |
| 25472 | .prefetch_options => .PrefetchOptions, |
| 25473 | .export_options => .ExportOptions, |
| 25474 | .extern_options => .ExternOptions, |
| 25475 | .branch_hint => .BranchHint, |
| 25476 | .clobbers => .@"assembly.Clobbers", |
| 25477 | .pointer_size => .@"Type.Pointer.Size", |
| 25478 | .pointer_attributes => .@"Type.Pointer.Attributes", |
| 25479 | .fn_attributes, => .@"Type.Fn.Attributes", |
| 25480 | .container_layout => .@"Type.ContainerLayout", |
| 25481 | .enum_mode => .@"Type.Enum.Mode", |
| 25482 | .spirv_type_options => .@"Type.Spirv", |
| 25483 | // zig fmt: on |
| 25484 | |
| 25485 | // Values are handled here. |
| 25486 | .calling_convention_c => { |
| 25487 | const callconv_ty = try sema.getStdLangType(src, .CallingConvention); |
| 25488 | // Cannot use `Value.uninterpret` because `c` is a *declaration* whose value depends on the target. |
| 25489 | return try sema.namespaceLookupVal( |
| 25490 | block, |
| 25491 | src, |
| 25492 | callconv_ty.getNamespaceIndex(zcu), |
| 25493 | try ip.getOrPutString(gpa, io, pt.tid, "c", .no_embedded_nulls), |
| 25494 | ) orelse @panic("std.lang is corrupt"); |
| 25495 | }, |
| 25496 | .calling_convention_inline => { |
| 25497 | const callconv_ty = try sema.getStdLangType(src, .CallingConvention); |
| 25498 | return .fromValue(Value.uninterpret( |
| 25499 | @as(std.lang.CallingConvention, .@"inline"), |
| 25500 | callconv_ty, |
| 25501 | pt, |
| 25502 | ) catch |err| switch (err) { |
| 25503 | error.TypeMismatch => @panic("std.lang is corrupt"), |
| 25504 | error.OutOfMemory => |e| return e, |
| 25505 | }); |
| 25506 | }, |
| 25507 | }; |
| 25508 | return .fromType(try sema.getStdLangType(src, std_lang_type)); |
| 25509 | } |
| 25510 | |
| 25511 | fn zirInplaceArithResultTy(sema: *Sema, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 25512 | const pt = sema.pt; |
| 25513 | const zcu = pt.zcu; |
| 25514 | |
| 25515 | const lhs = sema.resolveInst(@fromBackingInt(@intCast(extended.operand))); |
| 25516 | const lhs_ty = sema.typeOf(lhs); |
| 25517 | |
| 25518 | const op: Zir.Inst.InplaceOp = @fromBackingInt(@intCast(extended.small)); |
| 25519 | const ty: Type = switch (op) { |
| 25520 | .add_eq => ty: { |
| 25521 | const ptr_size = lhs_ty.ptrSizeOrNull(zcu) orelse break :ty lhs_ty; |
| 25522 | switch (ptr_size) { |
| 25523 | .one, .slice => break :ty lhs_ty, // invalid, let it error |
| 25524 | .many, .c => break :ty .usize, // `[*]T + usize` |
| 25525 | } |
| 25526 | }, |
| 25527 | .sub_eq => ty: { |
| 25528 | const ptr_size = lhs_ty.ptrSizeOrNull(zcu) orelse break :ty lhs_ty; |
| 25529 | switch (ptr_size) { |
| 25530 | .one, .slice => break :ty lhs_ty, // invalid, let it error |
| 25531 | .many, .c => break :ty .generic_poison, // could be `[*]T - [*]T` or `[*]T - usize` |
| 25532 | } |
| 25533 | }, |
| 25534 | }; |
| 25535 | return Air.internedToRef(ty.toIntern()); |
| 25536 | } |
| 25537 | |
| 25538 | fn zirBranchHint(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!void { |
| 25539 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 25540 | const uncoerced_hint = sema.resolveInst(extra.operand); |
| 25541 | const operand_src = block.builtinCallArgSrc(extra.node, 0); |
| 25542 | |
| 25543 | const hint_ty = try sema.getStdLangType(operand_src, .BranchHint); |
| 25544 | const coerced_hint = try sema.coerce(block, hint_ty, uncoerced_hint, operand_src); |
| 25545 | const hint_val = try sema.resolveConstDefinedValue(block, operand_src, coerced_hint, .{ .simple = .operand_branchHint }); |
| 25546 | |
| 25547 | // We only apply the first hint in a branch. |
| 25548 | // This allows user-provided hints to override implicit cold hints. |
| 25549 | if (sema.branch_hint == null) { |
| 25550 | sema.branch_hint = try sema.interpretStdLangType(block, operand_src, hint_val, std.lang.BranchHint); |
| 25551 | } |
| 25552 | } |
| 25553 | |
| 25554 | fn zirFloatOpResultType(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 25555 | const pt = sema.pt; |
| 25556 | const zcu = pt.zcu; |
| 25557 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 25558 | const operand_src = block.builtinCallArgSrc(extra.node, 0); |
| 25559 | |
| 25560 | const raw_ty = try sema.resolveTypeOrPoison(block, operand_src, extra.operand) orelse return .generic_poison_type; |
| 25561 | const float_ty = raw_ty.optEuBaseType(zcu); |
| 25562 | |
| 25563 | switch (float_ty.scalarType(zcu).zigTypeTag(zcu)) { |
| 25564 | .float, .comptime_float => {}, |
| 25565 | else => return sema.fail( |
| 25566 | block, |
| 25567 | operand_src, |
| 25568 | "expected vector of floats or float type, found '{f}'", |
| 25569 | .{float_ty.fmt(sema.pt)}, |
| 25570 | ), |
| 25571 | } |
| 25572 | |
| 25573 | return .fromType(float_ty); |
| 25574 | } |
| 25575 | |
| 25576 | fn zirRoundOpType(sema: *Sema, block: *Block, extended: Zir.Inst.Extended.InstData) CompileError!Air.Inst.Ref { |
| 25577 | const pt = sema.pt; |
| 25578 | const zcu = pt.zcu; |
| 25579 | const extra = sema.code.extraData(Zir.Inst.UnNode, extended.operand).data; |
| 25580 | const operand_src = block.builtinCallArgSrc(extra.node, 0); |
| 25581 | |
| 25582 | const dest_ty = try sema.resolveTypeOrPoison(block, operand_src, extra.operand) orelse { |
| 25583 | return .generic_poison_type; |
| 25584 | }; |
| 25585 | |
| 25586 | const dest_base_ty = dest_ty.optEuBaseType(zcu); |
| 25587 | switch (dest_base_ty.scalarType(zcu).zigTypeTag(zcu)) { |
| 25588 | .float, .comptime_float => return .fromType(dest_base_ty), |
| 25589 | else => return .generic_poison_type, |
| 25590 | } |
| 25591 | } |
| 25592 | |
| 25593 | fn requireRuntimeBlock(sema: *Sema, block: *Block, src: LazySrcLoc, runtime_src: ?LazySrcLoc) !void { |
| 25594 | if (block.isComptime()) { |
| 25595 | const msg, const fail_block = msg: { |
| 25596 | const msg = try sema.errMsg(src, "unable to evaluate comptime expression", .{}); |
| 25597 | errdefer msg.destroy(sema.gpa); |
| 25598 | |
| 25599 | if (runtime_src) |some| { |
| 25600 | try sema.errNote(some, msg, "operation is runtime due to this operand", .{}); |
| 25601 | } |
| 25602 | |
| 25603 | const fail_block = try block.explainWhyBlockIsComptime(msg); |
| 25604 | |
| 25605 | break :msg .{ msg, fail_block }; |
| 25606 | }; |
| 25607 | return sema.failWithOwnedErrorMsg(fail_block, msg); |
| 25608 | } |
| 25609 | } |
| 25610 | |
| 25611 | /// Emit a compile error if `var_ty` cannot be used for a runtime variable. |
| 25612 | /// Asserts that the layout of `var_ty` is already resolved. |
| 25613 | pub fn validateVarType( |
| 25614 | sema: *Sema, |
| 25615 | block: *Block, |
| 25616 | src: LazySrcLoc, |
| 25617 | var_ty: Type, |
| 25618 | is_extern: bool, |
| 25619 | ) CompileError!void { |
| 25620 | const pt = sema.pt; |
| 25621 | const zcu = pt.zcu; |
| 25622 | var_ty.assertHasLayout(zcu); |
| 25623 | if (is_extern) { |
| 25624 | if (!var_ty.validateExtern(.other, zcu)) { |
| 25625 | const msg = msg: { |
| 25626 | const msg = try sema.errMsg(src, "extern variable cannot have type '{f}'", .{var_ty.fmt(pt)}); |
| 25627 | errdefer msg.destroy(sema.gpa); |
| 25628 | try sema.explainWhyTypeIsNotExtern(msg, src, var_ty, .other); |
| 25629 | break :msg msg; |
| 25630 | }; |
| 25631 | return sema.failWithOwnedErrorMsg(block, msg); |
| 25632 | } |
| 25633 | } else { |
| 25634 | if (var_ty.zigTypeTag(zcu) == .@"opaque") { |
| 25635 | return sema.fail( |
| 25636 | block, |
| 25637 | src, |
| 25638 | "non-extern variable with opaque type '{f}'", |
| 25639 | .{var_ty.fmt(pt)}, |
| 25640 | ); |
| 25641 | } |
| 25642 | } |
| 25643 | |
| 25644 | if (!var_ty.comptimeOnly(zcu)) return; |
| 25645 | |
| 25646 | const msg = msg: { |
| 25647 | const msg = try sema.errMsg(src, "variable of type '{f}' must be const or comptime", .{var_ty.fmt(pt)}); |
| 25648 | errdefer msg.destroy(sema.gpa); |
| 25649 | |
| 25650 | try sema.explainWhyTypeIsComptime(msg, src, var_ty); |
| 25651 | if (var_ty.zigTypeTag(zcu) == .comptime_int or var_ty.zigTypeTag(zcu) == .comptime_float) { |
| 25652 | try sema.errNote(src, msg, "to modify this variable at runtime, it must be given an explicit fixed-size number type", .{}); |
| 25653 | } |
| 25654 | |
| 25655 | break :msg msg; |
| 25656 | }; |
| 25657 | return sema.failWithOwnedErrorMsg(block, msg); |
| 25658 | } |
| 25659 | |
| 25660 | fn explainWhyTypeIsComptime( |
| 25661 | sema: *Sema, |
| 25662 | msg: *Zcu.ErrorMsg, |
| 25663 | src: LazySrcLoc, |
| 25664 | ty: Type, |
| 25665 | ) CompileError!void { |
| 25666 | const pt = sema.pt; |
| 25667 | const zcu = pt.zcu; |
| 25668 | const ip = &zcu.intern_pool; |
| 25669 | assert(ty.comptimeOnly(zcu)); |
| 25670 | switch (ty.zigTypeTag(zcu)) { |
| 25671 | .bool, |
| 25672 | .int, |
| 25673 | .float, |
| 25674 | .error_set, |
| 25675 | .frame, |
| 25676 | .@"anyframe", |
| 25677 | .void, |
| 25678 | .@"enum", |
| 25679 | .@"opaque", |
| 25680 | .spirv, |
| 25681 | .pointer, |
| 25682 | => unreachable, // not comptime-only |
| 25683 | |
| 25684 | .comptime_float, |
| 25685 | .comptime_int, |
| 25686 | .enum_literal, |
| 25687 | .noreturn, |
| 25688 | .undefined, |
| 25689 | .null, |
| 25690 | => return, // no explanation needed |
| 25691 | |
| 25692 | .array, .vector => try sema.explainWhyTypeIsComptime(msg, src, ty.childType(zcu)), |
| 25693 | .optional => try sema.explainWhyTypeIsComptime(msg, src, ty.optionalChild(zcu)), |
| 25694 | .error_union => try sema.explainWhyTypeIsComptime(msg, src, ty.errorUnionPayload(zcu)), |
| 25695 | |
| 25696 | .@"fn" => try sema.errNote(src, msg, "use '*const {f}' for a function pointer type", .{ty.fmt(pt)}), |
| 25697 | .type => try sema.errNote(src, msg, "types are not available at runtime", .{}), |
| 25698 | |
| 25699 | .@"struct" => if (zcu.typeToStruct(ty)) |struct_type| { |
| 25700 | ty.assertHasLayout(zcu); |
| 25701 | for (0..struct_type.field_types.len) |i| { |
| 25702 | const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[i]); |
| 25703 | if (!field_ty.comptimeOnly(zcu)) continue; |
| 25704 | const field_src: LazySrcLoc = .{ |
| 25705 | .base_node_inst = struct_type.zir_index, |
| 25706 | .offset = .{ .container_field_type = @intCast(i) }, |
| 25707 | }; |
| 25708 | try sema.errNote(field_src, msg, "struct requires comptime because of this field", .{}); |
| 25709 | return sema.explainWhyTypeIsComptime(msg, field_src, field_ty); |
| 25710 | } |
| 25711 | unreachable; |
| 25712 | } else { |
| 25713 | const tuple = ip.indexToKey(ty.toIntern()).tuple_type; |
| 25714 | for (tuple.types.get(ip), tuple.values.get(ip)) |field_ty_ip, field_val_ip| { |
| 25715 | if (field_val_ip != .none) continue; |
| 25716 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 25717 | if (!field_ty.comptimeOnly(zcu)) continue; |
| 25718 | try sema.errNote(src, msg, "tuple requires comptime because of field of type '{f}'", .{field_ty.fmt(pt)}); |
| 25719 | return sema.explainWhyTypeIsComptime(msg, src, field_ty); |
| 25720 | } |
| 25721 | unreachable; |
| 25722 | }, |
| 25723 | |
| 25724 | .@"union" => { |
| 25725 | const union_obj = zcu.typeToUnion(ty).?; |
| 25726 | for (0..union_obj.field_types.len) |i| { |
| 25727 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[i]); |
| 25728 | if (!field_ty.comptimeOnly(zcu)) continue; |
| 25729 | const field_src: LazySrcLoc = .{ |
| 25730 | .base_node_inst = union_obj.zir_index, |
| 25731 | .offset = .{ .container_field_type = @intCast(i) }, |
| 25732 | }; |
| 25733 | try sema.errNote(field_src, msg, "union requires comptime because of this field", .{}); |
| 25734 | return sema.explainWhyTypeIsComptime(msg, field_src, field_ty); |
| 25735 | } |
| 25736 | unreachable; |
| 25737 | }, |
| 25738 | } |
| 25739 | } |
| 25740 | |
| 25741 | /// Keep in sync with `Type.validateExtern`. |
| 25742 | pub fn explainWhyTypeIsNotExtern( |
| 25743 | sema: *Sema, |
| 25744 | msg: *Zcu.ErrorMsg, |
| 25745 | src_loc: LazySrcLoc, |
| 25746 | ty: Type, |
| 25747 | position: Type.ExternPosition, |
| 25748 | ) SemaError!void { |
| 25749 | const pt = sema.pt; |
| 25750 | const zcu = pt.zcu; |
| 25751 | switch (ty.zigTypeTag(zcu)) { |
| 25752 | .type, |
| 25753 | .comptime_float, |
| 25754 | .comptime_int, |
| 25755 | .enum_literal, |
| 25756 | .undefined, |
| 25757 | .null, |
| 25758 | .error_union, |
| 25759 | .error_set, |
| 25760 | .frame, |
| 25761 | => return, |
| 25762 | |
| 25763 | .void => try sema.errNote(src_loc, msg, "'void' is a zero bit type", .{}), |
| 25764 | .noreturn => try sema.errNote(src_loc, msg, "'noreturn' is only allowed as a return type", .{}), |
| 25765 | |
| 25766 | .@"opaque", |
| 25767 | .bool, |
| 25768 | .@"anyframe", |
| 25769 | => unreachable, // these *are* allowed |
| 25770 | |
| 25771 | .spirv => { |
| 25772 | assert(ty.isSpirvRuntimeArray(zcu)); |
| 25773 | try sema.errNote(src_loc, msg, "SPIR-V runtime arrays must be the last field of an extern struct", .{}); |
| 25774 | if (position == .other) { |
| 25775 | try sema.errNote(src_loc, msg, "consider enabling the 'runtime_descriptor_array' feature to use the runtime array as the extern pointee", .{}); |
| 25776 | } |
| 25777 | }, |
| 25778 | |
| 25779 | .float => try sema.errNote(src_loc, msg, "'{f}' is not extern compatible on this target", .{ty.fmt(pt)}), |
| 25780 | .pointer => if (ty.isSlice(zcu)) { |
| 25781 | try sema.errNote(src_loc, msg, "slices have no guaranteed in-memory representation", .{}); |
| 25782 | } else { |
| 25783 | assert(ty.childType(zcu).zigTypeTag(zcu) == .@"fn"); |
| 25784 | if (!ty.isConstPtr(zcu)) { |
| 25785 | try sema.errNote(src_loc, msg, "pointer to extern function must be 'const'", .{}); |
| 25786 | } else { |
| 25787 | try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.childType(zcu), .other); |
| 25788 | } |
| 25789 | }, |
| 25790 | .int => if (!std.math.isPowerOfTwo(ty.intInfo(zcu).bits)) { |
| 25791 | try sema.errNote(src_loc, msg, "only integers with 0 or power of two bits are extern compatible", .{}); |
| 25792 | } else { |
| 25793 | try sema.errNote(src_loc, msg, "only integers with 0, 8, 16, 32, 64 and 128 bits are extern compatible", .{}); |
| 25794 | }, |
| 25795 | .@"fn" => if (position != .other) { |
| 25796 | try sema.errNote(src_loc, msg, "type has no guaranteed in-memory representation", .{}); |
| 25797 | try sema.errNote(src_loc, msg, "use '*const ' to make a function pointer type", .{}); |
| 25798 | } else switch (ty.fnCallingConvention(zcu)) { |
| 25799 | .auto => try sema.errNote(src_loc, msg, "extern function must specify calling convention", .{}), |
| 25800 | else => |cc| try sema.errNote(src_loc, msg, "{t} function cannot be extern", .{cc}), |
| 25801 | }, |
| 25802 | .@"enum" => { |
| 25803 | const enum_obj = zcu.intern_pool.loadEnumType(ty.toIntern()); |
| 25804 | switch (enum_obj.int_tag_mode) { |
| 25805 | .auto => { |
| 25806 | try sema.errNote(ty.srcLoc(zcu), msg, "integer tag type of enum is inferred", .{}); |
| 25807 | try sema.errNote(ty.srcLoc(zcu), msg, "consider explicitly specifying the integer tag type", .{}); |
| 25808 | }, |
| 25809 | .explicit => { |
| 25810 | const tag_ty: Type = .fromInterned(enum_obj.int_tag_type); |
| 25811 | try sema.errNote(ty.srcLoc(zcu), msg, "enum tag type '{f}' is not extern compatible", .{tag_ty.fmt(pt)}); |
| 25812 | try sema.explainWhyTypeIsNotExtern(msg, ty.srcLoc(zcu), tag_ty, position); |
| 25813 | }, |
| 25814 | } |
| 25815 | }, |
| 25816 | .@"struct" => { |
| 25817 | if (ty.isTuple(zcu)) { |
| 25818 | return sema.errNote(src_loc, msg, "tuples have no guaranteed in-memory representation", .{}); |
| 25819 | } |
| 25820 | |
| 25821 | const struct_obj = zcu.intern_pool.loadStructType(ty.toIntern()); |
| 25822 | switch (struct_obj.layout) { |
| 25823 | .auto => try sema.errNote(src_loc, msg, "struct with automatic layout has no guaranteed in-memory representation", .{}), |
| 25824 | .@"extern" => unreachable, |
| 25825 | .@"packed" => switch (struct_obj.packed_backing_mode) { |
| 25826 | .auto => try sema.errNote(src_loc, msg, "inferred backing integer of packed struct has unspecified signedness", .{}), |
| 25827 | .explicit => { |
| 25828 | const backing_int_ty: Type = .fromInterned(struct_obj.packed_backing_int_type); |
| 25829 | try sema.errNote(src_loc, msg, "packed struct backing integer type '{f}' is not extern compatible", .{backing_int_ty.fmt(pt)}); |
| 25830 | try sema.explainWhyTypeIsNotExtern(msg, src_loc, backing_int_ty, position); |
| 25831 | }, |
| 25832 | }, |
| 25833 | } |
| 25834 | }, |
| 25835 | .@"union" => { |
| 25836 | const union_obj = zcu.intern_pool.loadUnionType(ty.toIntern()); |
| 25837 | switch (union_obj.layout) { |
| 25838 | .auto => try sema.errNote(src_loc, msg, "union with automatic layout has no guaranteed in-memory representation", .{}), |
| 25839 | .@"extern" => unreachable, |
| 25840 | .@"packed" => switch (union_obj.packed_backing_mode) { |
| 25841 | .auto => try sema.errNote(src_loc, msg, "inferred backing integer of packed union has unspecified signedness", .{}), |
| 25842 | .explicit => { |
| 25843 | const backing_int_ty: Type = .fromInterned(union_obj.packed_backing_int_type); |
| 25844 | try sema.errNote(src_loc, msg, "packed union backing integer type '{f}' is not extern compatible", .{backing_int_ty.fmt(pt)}); |
| 25845 | try sema.explainWhyTypeIsNotExtern(msg, src_loc, backing_int_ty, position); |
| 25846 | }, |
| 25847 | }, |
| 25848 | } |
| 25849 | }, |
| 25850 | .array => switch (position) { |
| 25851 | .ret_ty => try sema.errNote(src_loc, msg, "arrays are not allowed as a return type", .{}), |
| 25852 | .param_ty => try sema.errNote(src_loc, msg, "arrays are not allowed as a parameter type", .{}), |
| 25853 | else => try sema.explainWhyTypeIsNotExtern(msg, src_loc, ty.childType(zcu), .element), |
| 25854 | }, |
| 25855 | .vector => try sema.errNote(src_loc, msg, "vectors have no guaranteed in-memory representation", .{}), |
| 25856 | .optional => try sema.errNote(src_loc, msg, "non-pointer optionals have no guaranteed in-memory representation", .{}), |
| 25857 | } |
| 25858 | } |
| 25859 | |
| 25860 | pub fn explainWhyTypeIsUnpackable( |
| 25861 | sema: *Sema, |
| 25862 | msg: *Zcu.ErrorMsg, |
| 25863 | src: LazySrcLoc, |
| 25864 | reason: Type.UnpackableReason, |
| 25865 | ) CompileError!void { |
| 25866 | const pt = sema.pt; |
| 25867 | const zcu = pt.zcu; |
| 25868 | switch (reason) { |
| 25869 | .comptime_only => try sema.errNote(src, msg, "comptime-only types have no bit-packed representation", .{}), |
| 25870 | .pointer => { |
| 25871 | try sema.errNote(src, msg, "pointers cannot be directly bitpacked", .{}); |
| 25872 | try sema.errNote(src, msg, "consider using 'usize' and '@intFromPtr'", .{}); |
| 25873 | }, |
| 25874 | .enum_inferred_int_tag => |enum_ty| { |
| 25875 | const enum_src = enum_ty.srcLoc(zcu); |
| 25876 | try sema.errNote(enum_src, msg, "integer tag type of enum is inferred", .{}); |
| 25877 | try sema.errNote(enum_src, msg, "consider explicitly specifying the integer tag type", .{}); |
| 25878 | }, |
| 25879 | .non_packed_struct => |struct_ty| { |
| 25880 | try sema.errNote(src, msg, "non-packed structs do not have a bit-packed representation", .{}); |
| 25881 | try sema.addDeclaredHereNote(msg, struct_ty); |
| 25882 | }, |
| 25883 | .non_packed_union => |union_ty| { |
| 25884 | try sema.errNote(src, msg, "non-packed unions do not have a bit-packed representation", .{}); |
| 25885 | try sema.addDeclaredHereNote(msg, union_ty); |
| 25886 | }, |
| 25887 | .slice => try sema.errNote(src, msg, "slices do not have a bit-packed representation", .{}), |
| 25888 | .other => try sema.errNote(src, msg, "type does not have a bit-packed representation", .{}), |
| 25889 | } |
| 25890 | } |
| 25891 | |
| 25892 | /// Backends depend on panic decls being available when lowering safety-checked |
| 25893 | /// instructions. This function ensures the panic function will be available to |
| 25894 | /// be called during that time. |
| 25895 | fn preparePanicId(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !void { |
| 25896 | const zcu = sema.pt.zcu; |
| 25897 | |
| 25898 | // If the backend doesn't support `.panic_fn`, it doesn't want us to lower the panic handlers. |
| 25899 | // The backend will transform panics into traps instead. |
| 25900 | if (!zcu.backendSupportsFeature(.panic_fn)) return; |
| 25901 | |
| 25902 | const fn_index = try sema.getPanicIdFunc(src, panic_id); |
| 25903 | const orig_fn_index = zcu.intern_pool.unwrapCoercedFunc(fn_index); |
| 25904 | try sema.addReferenceEntry(null, src, .wrap(.{ .func = orig_fn_index })); |
| 25905 | try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index); |
| 25906 | } |
| 25907 | |
| 25908 | fn getPanicIdFunc(sema: *Sema, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) !InternPool.Index { |
| 25909 | const zcu = sema.pt.zcu; |
| 25910 | const io = zcu.comp.io; |
| 25911 | try sema.ensureMemoizedStateResolved(src, .panic); |
| 25912 | const panic_fn_index = zcu.std_lang_decl_values.get(panic_id.toStdLangDecl()); |
| 25913 | switch (sema.owner.unwrap()) { |
| 25914 | .@"comptime", |
| 25915 | .nav_ty, |
| 25916 | .nav_val, |
| 25917 | .type_layout, |
| 25918 | .struct_defaults, |
| 25919 | .memoized_state, |
| 25920 | => {}, |
| 25921 | |
| 25922 | .func => |owner_func| zcu.intern_pool.funcSetHasErrorTrace(io, owner_func, true), |
| 25923 | } |
| 25924 | return panic_fn_index; |
| 25925 | } |
| 25926 | |
| 25927 | fn addSafetyCheck( |
| 25928 | sema: *Sema, |
| 25929 | parent_block: *Block, |
| 25930 | src: LazySrcLoc, |
| 25931 | ok: Air.Inst.Ref, |
| 25932 | panic_id: Zcu.SimplePanicId, |
| 25933 | ) !void { |
| 25934 | const gpa = sema.gpa; |
| 25935 | assert(!parent_block.isComptime()); |
| 25936 | |
| 25937 | var fail_block: Block = .{ |
| 25938 | .parent = parent_block, |
| 25939 | .sema = sema, |
| 25940 | .namespace = parent_block.namespace, |
| 25941 | .instructions = .empty, |
| 25942 | .inlining = parent_block.inlining, |
| 25943 | .comptime_reason = null, |
| 25944 | .src_base_inst = parent_block.src_base_inst, |
| 25945 | .type_name_ctx = parent_block.type_name_ctx, |
| 25946 | .type_fqn_ctx = parent_block.type_fqn_ctx, |
| 25947 | }; |
| 25948 | |
| 25949 | defer fail_block.instructions.deinit(gpa); |
| 25950 | |
| 25951 | try sema.safetyPanic(&fail_block, src, panic_id); |
| 25952 | try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); |
| 25953 | } |
| 25954 | |
| 25955 | fn addSafetyCheckExtra( |
| 25956 | sema: *Sema, |
| 25957 | parent_block: *Block, |
| 25958 | ok: Air.Inst.Ref, |
| 25959 | fail_block: *Block, |
| 25960 | ) !void { |
| 25961 | const gpa = sema.gpa; |
| 25962 | |
| 25963 | try parent_block.instructions.ensureUnusedCapacity(gpa, 1); |
| 25964 | |
| 25965 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Block).@"struct".field_names.len + |
| 25966 | 1 + // The main block only needs space for the cond_br. |
| 25967 | @typeInfo(Air.CondBr).@"struct".field_names.len + |
| 25968 | 1 + // The ok branch of the cond_br only needs space for the br. |
| 25969 | fail_block.instructions.items.len); |
| 25970 | |
| 25971 | try sema.air_instructions.ensureUnusedCapacity(gpa, 3); |
| 25972 | const block_inst: Air.Inst.Index = @fromBackingInt(@intCast(sema.air_instructions.len)); |
| 25973 | const cond_br_inst: Air.Inst.Index = @fromBackingInt(@intCast(@backingInt(block_inst) + 1)); |
| 25974 | const br_inst: Air.Inst.Index = @fromBackingInt(@intCast(@backingInt(cond_br_inst) + 1)); |
| 25975 | sema.air_instructions.appendAssumeCapacity(.{ |
| 25976 | .tag = .block, |
| 25977 | .data = .{ .ty_pl = .{ |
| 25978 | .ty = .void, |
| 25979 | .payload = sema.addExtraAssumeCapacity(Air.Block{ |
| 25980 | .body_len = 1, |
| 25981 | }), |
| 25982 | } }, |
| 25983 | }); |
| 25984 | sema.air_extra.appendAssumeCapacity(@backingInt(cond_br_inst)); |
| 25985 | |
| 25986 | sema.air_instructions.appendAssumeCapacity(.{ |
| 25987 | .tag = .cond_br, |
| 25988 | .data = .{ |
| 25989 | .pl_op = .{ |
| 25990 | .operand = ok, |
| 25991 | .payload = sema.addExtraAssumeCapacity(Air.CondBr{ |
| 25992 | .then_body_len = 1, |
| 25993 | .else_body_len = @intCast(fail_block.instructions.items.len), |
| 25994 | .branch_hints = .{ |
| 25995 | // Safety check failure branch is cold. |
| 25996 | .true = .likely, |
| 25997 | .false = .cold, |
| 25998 | // Code coverage not wanted for panic branches. |
| 25999 | .then_cov = .none, |
| 26000 | .else_cov = .none, |
| 26001 | }, |
| 26002 | }), |
| 26003 | }, |
| 26004 | }, |
| 26005 | }); |
| 26006 | sema.air_extra.appendAssumeCapacity(@backingInt(br_inst)); |
| 26007 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(fail_block.instructions.items)); |
| 26008 | |
| 26009 | sema.air_instructions.appendAssumeCapacity(.{ |
| 26010 | .tag = .br, |
| 26011 | .data = .{ .br = .{ |
| 26012 | .block_inst = block_inst, |
| 26013 | .operand = .void_value, |
| 26014 | } }, |
| 26015 | }); |
| 26016 | |
| 26017 | parent_block.instructions.appendAssumeCapacity(block_inst); |
| 26018 | } |
| 26019 | |
| 26020 | fn addSafetyCheckUnwrapError( |
| 26021 | sema: *Sema, |
| 26022 | parent_block: *Block, |
| 26023 | src: LazySrcLoc, |
| 26024 | operand: Air.Inst.Ref, |
| 26025 | unwrap_err_tag: Air.Inst.Tag, |
| 26026 | is_non_err_tag: Air.Inst.Tag, |
| 26027 | ) !void { |
| 26028 | assert(!parent_block.isComptime()); |
| 26029 | const ok = try parent_block.addUnOp(is_non_err_tag, operand); |
| 26030 | const gpa = sema.gpa; |
| 26031 | |
| 26032 | var fail_block: Block = .{ |
| 26033 | .parent = parent_block, |
| 26034 | .sema = sema, |
| 26035 | .namespace = parent_block.namespace, |
| 26036 | .instructions = .empty, |
| 26037 | .inlining = parent_block.inlining, |
| 26038 | .comptime_reason = null, |
| 26039 | .src_base_inst = parent_block.src_base_inst, |
| 26040 | .type_name_ctx = parent_block.type_name_ctx, |
| 26041 | .type_fqn_ctx = parent_block.type_fqn_ctx, |
| 26042 | }; |
| 26043 | |
| 26044 | defer fail_block.instructions.deinit(gpa); |
| 26045 | |
| 26046 | const err = try fail_block.addTyOp(unwrap_err_tag, .anyerror, operand); |
| 26047 | try safetyPanicUnwrapError(sema, &fail_block, src, err); |
| 26048 | |
| 26049 | try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); |
| 26050 | } |
| 26051 | |
| 26052 | fn safetyPanicUnwrapError(sema: *Sema, block: *Block, src: LazySrcLoc, err: Air.Inst.Ref) !void { |
| 26053 | const pt = sema.pt; |
| 26054 | const zcu = pt.zcu; |
| 26055 | if (!zcu.backendSupportsFeature(.panic_fn)) { |
| 26056 | _ = try block.addNoOp(.trap); |
| 26057 | } else { |
| 26058 | const panic_fn = try getStdLangValue(sema, src, .@"panic.unwrapError"); |
| 26059 | try sema.callBuiltin(block, src, Air.internedToRef(panic_fn), .auto, &.{err}, .@"safety check"); |
| 26060 | } |
| 26061 | } |
| 26062 | |
| 26063 | fn addSafetyCheckIndexOob( |
| 26064 | sema: *Sema, |
| 26065 | parent_block: *Block, |
| 26066 | src: LazySrcLoc, |
| 26067 | index: Air.Inst.Ref, |
| 26068 | len: Air.Inst.Ref, |
| 26069 | cmp_op: Air.Inst.Tag, |
| 26070 | ) !void { |
| 26071 | assert(!parent_block.isComptime()); |
| 26072 | const ok = try parent_block.addBinOp(cmp_op, index, len); |
| 26073 | return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.outOfBounds", &.{ index, len }); |
| 26074 | } |
| 26075 | |
| 26076 | fn addSafetyCheckInactiveUnionField( |
| 26077 | sema: *Sema, |
| 26078 | parent_block: *Block, |
| 26079 | src: LazySrcLoc, |
| 26080 | active_tag: Air.Inst.Ref, |
| 26081 | wanted_tag: Air.Inst.Ref, |
| 26082 | ) !void { |
| 26083 | assert(!parent_block.isComptime()); |
| 26084 | const ok = try parent_block.addBinOp(.cmp_eq, active_tag, wanted_tag); |
| 26085 | return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.inactiveUnionField", &.{ active_tag, wanted_tag }); |
| 26086 | } |
| 26087 | |
| 26088 | fn addSafetyCheckSentinelMismatch( |
| 26089 | sema: *Sema, |
| 26090 | parent_block: *Block, |
| 26091 | src: LazySrcLoc, |
| 26092 | maybe_sentinel: ?Value, |
| 26093 | sentinel_ty: Type, |
| 26094 | ptr: Air.Inst.Ref, |
| 26095 | sentinel_index: Air.Inst.Ref, |
| 26096 | ) !void { |
| 26097 | assert(!parent_block.isComptime()); |
| 26098 | const pt = sema.pt; |
| 26099 | const zcu = pt.zcu; |
| 26100 | const expected_sentinel_val = maybe_sentinel orelse return; |
| 26101 | const expected_sentinel = Air.internedToRef(expected_sentinel_val.toIntern()); |
| 26102 | |
| 26103 | const ptr_ty = sema.typeOf(ptr); |
| 26104 | const ptr_info = ptr_ty.ptrInfo(zcu); |
| 26105 | const actual_sentinel: Air.Inst.Ref = switch (ptr_ty.ptrSize(zcu)) { |
| 26106 | .slice => try parent_block.addBinOp(.slice_elem_val, ptr, sentinel_index), |
| 26107 | .one => s: { |
| 26108 | const array_ty: Type = .fromInterned(ptr_info.child); |
| 26109 | assert(array_ty.zigTypeTag(zcu) == .array); |
| 26110 | assert(array_ty.childType(zcu).toIntern() == sentinel_ty.toIntern()); |
| 26111 | const many_ptr_ty = try pt.ptrType(.{ |
| 26112 | .child = sentinel_ty.toIntern(), |
| 26113 | .flags = .{ |
| 26114 | .size = .many, |
| 26115 | .is_const = ptr_info.flags.is_const, |
| 26116 | .is_volatile = ptr_info.flags.is_volatile, |
| 26117 | .is_allowzero = ptr_info.flags.is_allowzero, |
| 26118 | .alignment = switch (ptr_info.flags.alignment) { |
| 26119 | .none => .none, |
| 26120 | else => |ptr_align| .minStrict(ptr_align, sentinel_ty.abiAlignment(zcu)), |
| 26121 | }, |
| 26122 | .address_space = ptr_info.flags.address_space, |
| 26123 | }, |
| 26124 | }); |
| 26125 | const many_ptr = try parent_block.addTyOp(.ptr_cast, many_ptr_ty, ptr); |
| 26126 | break :s try parent_block.addBinOp(.ptr_elem_val, many_ptr, sentinel_index); |
| 26127 | }, |
| 26128 | .many => unreachable, |
| 26129 | .c => unreachable, |
| 26130 | }; |
| 26131 | assert(sema.typeOf(actual_sentinel).toIntern() == sentinel_ty.toIntern()); |
| 26132 | assert(sentinel_ty.isSelfComparable(zcu, true)); |
| 26133 | const ok: Air.Inst.Ref = if (sentinel_ty.zigTypeTag(zcu) == .vector) ok: { |
| 26134 | const elementwise = try parent_block.addCmpVector(expected_sentinel, actual_sentinel, .eq); |
| 26135 | break :ok try parent_block.addReduce(elementwise, .And); |
| 26136 | } else try parent_block.addBinOp(.cmp_eq, expected_sentinel, actual_sentinel); |
| 26137 | |
| 26138 | return addSafetyCheckCall(sema, parent_block, src, ok, .@"panic.sentinelMismatch", &.{ |
| 26139 | expected_sentinel, actual_sentinel, |
| 26140 | }); |
| 26141 | } |
| 26142 | |
| 26143 | fn addSafetyCheckCall( |
| 26144 | sema: *Sema, |
| 26145 | parent_block: *Block, |
| 26146 | src: LazySrcLoc, |
| 26147 | ok: Air.Inst.Ref, |
| 26148 | comptime func_decl: Zcu.StdLangDecl, |
| 26149 | args: []const Air.Inst.Ref, |
| 26150 | ) !void { |
| 26151 | assert(!parent_block.isComptime()); |
| 26152 | const gpa = sema.gpa; |
| 26153 | const pt = sema.pt; |
| 26154 | const zcu = pt.zcu; |
| 26155 | |
| 26156 | var fail_block: Block = .{ |
| 26157 | .parent = parent_block, |
| 26158 | .sema = sema, |
| 26159 | .namespace = parent_block.namespace, |
| 26160 | .instructions = .empty, |
| 26161 | .inlining = parent_block.inlining, |
| 26162 | .comptime_reason = null, |
| 26163 | .src_base_inst = parent_block.src_base_inst, |
| 26164 | .type_name_ctx = parent_block.type_name_ctx, |
| 26165 | .type_fqn_ctx = parent_block.type_fqn_ctx, |
| 26166 | }; |
| 26167 | |
| 26168 | defer fail_block.instructions.deinit(gpa); |
| 26169 | |
| 26170 | if (!zcu.backendSupportsFeature(.panic_fn)) { |
| 26171 | _ = try fail_block.addNoOp(.trap); |
| 26172 | } else { |
| 26173 | const panic_fn = try getStdLangValue(sema, src, func_decl); |
| 26174 | try sema.callBuiltin(&fail_block, src, Air.internedToRef(panic_fn), .auto, args, .@"safety check"); |
| 26175 | } |
| 26176 | |
| 26177 | try sema.addSafetyCheckExtra(parent_block, ok, &fail_block); |
| 26178 | } |
| 26179 | |
| 26180 | /// This does not set `sema.branch_hint`. |
| 26181 | fn safetyPanic(sema: *Sema, block: *Block, src: LazySrcLoc, panic_id: Zcu.SimplePanicId) CompileError!void { |
| 26182 | if (!sema.pt.zcu.backendSupportsFeature(.panic_fn)) { |
| 26183 | _ = try block.addNoOp(.trap); |
| 26184 | } else { |
| 26185 | const panic_fn = try sema.getPanicIdFunc(src, panic_id); |
| 26186 | try sema.callBuiltin(block, src, Air.internedToRef(panic_fn), .auto, &.{}, .@"safety check"); |
| 26187 | } |
| 26188 | } |
| 26189 | |
| 26190 | fn emitBackwardBranch(sema: *Sema, block: *Block, src: LazySrcLoc) !void { |
| 26191 | sema.branch_count += 1; |
| 26192 | if (sema.branch_count > sema.branch_quota) { |
| 26193 | const msg = msg: { |
| 26194 | const msg = try sema.errMsg( |
| 26195 | src, |
| 26196 | "evaluation exceeded {d} backwards branches", |
| 26197 | .{sema.branch_quota}, |
| 26198 | ); |
| 26199 | errdefer msg.destroy(sema.gpa); |
| 26200 | try sema.errNote( |
| 26201 | src, |
| 26202 | msg, |
| 26203 | "use @setEvalBranchQuota() to raise the branch limit from {d}", |
| 26204 | .{sema.branch_quota}, |
| 26205 | ); |
| 26206 | break :msg msg; |
| 26207 | }; |
| 26208 | return sema.failWithOwnedErrorMsg(block, msg); |
| 26209 | } |
| 26210 | } |
| 26211 | |
| 26212 | fn fieldPtrLoad( |
| 26213 | sema: *Sema, |
| 26214 | block: *Block, |
| 26215 | src: LazySrcLoc, |
| 26216 | object_ptr: Air.Inst.Ref, |
| 26217 | field_name: InternPool.NullTerminatedString, |
| 26218 | field_name_src: LazySrcLoc, |
| 26219 | ) CompileError!Air.Inst.Ref { |
| 26220 | const pt = sema.pt; |
| 26221 | const zcu = pt.zcu; |
| 26222 | const ip = &zcu.intern_pool; |
| 26223 | const object_ptr_ty = sema.typeOf(object_ptr); |
| 26224 | assert(object_ptr_ty.zigTypeTag(zcu) == .pointer); |
| 26225 | const pointee_ty = object_ptr_ty.childType(zcu); |
| 26226 | if (pointee_ty.isSpirvRuntimeArray(zcu) and field_name.eqlSlice("len", ip)) { |
| 26227 | return sema.analyzeSpirvRuntimeArrayLen(block, src, object_ptr, field_name_src); |
| 26228 | } |
| 26229 | try sema.ensureLayoutResolved(pointee_ty, src, .ptr_access); |
| 26230 | if (try pointee_ty.onePossibleValue(pt)) |opv| { |
| 26231 | const object: Air.Inst.Ref = .fromValue(opv); |
| 26232 | return fieldVal(sema, block, src, object, field_name, field_name_src); |
| 26233 | } |
| 26234 | |
| 26235 | if (try sema.resolveDefinedValue(block, src, object_ptr)) |object_ptr_val| { |
| 26236 | if (try sema.pointerDeref(block, src, object_ptr_val, object_ptr_ty)) |object_val| { |
| 26237 | const object: Air.Inst.Ref = .fromValue(object_val); |
| 26238 | return fieldVal(sema, block, src, object, field_name, field_name_src); |
| 26239 | } |
| 26240 | } |
| 26241 | const field_ptr = try sema.fieldPtr(block, src, object_ptr, field_name, field_name_src, false); |
| 26242 | return analyzeLoad(sema, block, src, field_ptr, field_name_src); |
| 26243 | } |
| 26244 | |
| 26245 | fn fieldVal( |
| 26246 | sema: *Sema, |
| 26247 | block: *Block, |
| 26248 | src: LazySrcLoc, |
| 26249 | object: Air.Inst.Ref, |
| 26250 | field_name: InternPool.NullTerminatedString, |
| 26251 | field_name_src: LazySrcLoc, |
| 26252 | ) CompileError!Air.Inst.Ref { |
| 26253 | // When editing this function, note that there is corresponding logic to be edited |
| 26254 | // in `fieldPtr`. This function takes a value and returns a value. |
| 26255 | |
| 26256 | const pt = sema.pt; |
| 26257 | const zcu = pt.zcu; |
| 26258 | const ip = &zcu.intern_pool; |
| 26259 | const object_src = src; // TODO better source location |
| 26260 | const object_ty = sema.typeOf(object); |
| 26261 | |
| 26262 | // Zig allows dereferencing a single pointer during field lookup. Note that |
| 26263 | // we don't actually need to generate the dereference some field lookups, like the |
| 26264 | // length of arrays and other comptime operations. |
| 26265 | const is_pointer_to = object_ty.isSinglePointer(zcu); |
| 26266 | |
| 26267 | const inner_ty = if (is_pointer_to) |
| 26268 | object_ty.childType(zcu) |
| 26269 | else |
| 26270 | object_ty; |
| 26271 | |
| 26272 | switch (inner_ty.zigTypeTag(zcu)) { |
| 26273 | .array => { |
| 26274 | if (field_name.eqlSlice("len", ip)) { |
| 26275 | return Air.internedToRef((try pt.intValue(.usize, inner_ty.arrayLen(zcu))).toIntern()); |
| 26276 | } else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) { |
| 26277 | const ptr_info = object_ty.ptrInfo(zcu); |
| 26278 | const result_ty = try pt.ptrType(.{ |
| 26279 | .child = Type.fromInterned(ptr_info.child).childType(zcu).toIntern(), |
| 26280 | .sentinel = if (inner_ty.sentinel(zcu)) |s| s.toIntern() else .none, |
| 26281 | .flags = .{ |
| 26282 | .size = .many, |
| 26283 | .alignment = ptr_info.flags.alignment, |
| 26284 | .is_const = ptr_info.flags.is_const, |
| 26285 | .is_volatile = ptr_info.flags.is_volatile, |
| 26286 | .is_allowzero = ptr_info.flags.is_allowzero, |
| 26287 | .address_space = ptr_info.flags.address_space, |
| 26288 | .vector_index = ptr_info.flags.vector_index, |
| 26289 | }, |
| 26290 | .packed_offset = ptr_info.packed_offset, |
| 26291 | }); |
| 26292 | return sema.coerce(block, result_ty, object, src); |
| 26293 | } else { |
| 26294 | return sema.fail( |
| 26295 | block, |
| 26296 | field_name_src, |
| 26297 | "no member named '{f}' in '{f}'", |
| 26298 | .{ field_name.fmt(ip), object_ty.fmt(pt) }, |
| 26299 | ); |
| 26300 | } |
| 26301 | }, |
| 26302 | .pointer => { |
| 26303 | const ptr_info = inner_ty.ptrInfo(zcu); |
| 26304 | if (ptr_info.flags.size == .slice) { |
| 26305 | if (field_name.eqlSlice("ptr", ip)) { |
| 26306 | const slice = if (is_pointer_to) |
| 26307 | try sema.analyzeLoad(block, src, object, object_src) |
| 26308 | else |
| 26309 | object; |
| 26310 | return sema.analyzeSlicePtr(block, object_src, slice, inner_ty); |
| 26311 | } else if (field_name.eqlSlice("len", ip)) { |
| 26312 | const slice = if (is_pointer_to) |
| 26313 | try sema.analyzeLoad(block, src, object, object_src) |
| 26314 | else |
| 26315 | object; |
| 26316 | return sema.analyzeSliceLen(block, src, slice); |
| 26317 | } else { |
| 26318 | return sema.fail( |
| 26319 | block, |
| 26320 | field_name_src, |
| 26321 | "no member named '{f}' in '{f}'", |
| 26322 | .{ field_name.fmt(ip), object_ty.fmt(pt) }, |
| 26323 | ); |
| 26324 | } |
| 26325 | } |
| 26326 | }, |
| 26327 | .type => { |
| 26328 | const dereffed_type = if (is_pointer_to) |
| 26329 | try sema.analyzeLoad(block, src, object, object_src) |
| 26330 | else |
| 26331 | object; |
| 26332 | |
| 26333 | const val = (try sema.resolveDefinedValue(block, object_src, dereffed_type)).?; |
| 26334 | const child_type = val.toType(); |
| 26335 | |
| 26336 | switch (child_type.zigTypeTag(zcu)) { |
| 26337 | .error_set => { |
| 26338 | const err_set_ty: Type = err_set: switch (ip.indexToKey(child_type.toIntern())) { |
| 26339 | .inferred_error_set_type => |func_index| { |
| 26340 | try sema.ensureFuncIesResolved(block, src, func_index); |
| 26341 | const resolved_ies = ip.funcIesResolvedUnordered(func_index); |
| 26342 | continue :err_set ip.indexToKey(resolved_ies); |
| 26343 | }, |
| 26344 | .error_set_type => |err_set| if (err_set.nameIndex(ip, field_name) == null) { |
| 26345 | return sema.fail(block, src, "no error named '{f}' in '{f}'", .{ |
| 26346 | field_name.fmt(ip), child_type.fmt(pt), |
| 26347 | }); |
| 26348 | } else child_type, |
| 26349 | .simple_type => |t| { |
| 26350 | assert(t == .anyerror); |
| 26351 | _ = try pt.getErrorValue(field_name); |
| 26352 | break :err_set try pt.singleErrorSetType(field_name); |
| 26353 | }, |
| 26354 | else => unreachable, |
| 26355 | }; |
| 26356 | return .fromIntern(try pt.intern(.{ .err = .{ |
| 26357 | .ty = err_set_ty.toIntern(), |
| 26358 | .name = field_name, |
| 26359 | } })); |
| 26360 | }, |
| 26361 | .@"union" => { |
| 26362 | if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { |
| 26363 | return inst; |
| 26364 | } |
| 26365 | try sema.ensureLayoutResolved(child_type, src, .field_used); |
| 26366 | if (child_type.unionTagType(zcu)) |enum_ty| { |
| 26367 | if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index_usize| { |
| 26368 | const field_index: u32 = @intCast(field_index_usize); |
| 26369 | return Air.internedToRef((try pt.enumValueFieldIndex(enum_ty, field_index)).toIntern()); |
| 26370 | } |
| 26371 | } |
| 26372 | return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); |
| 26373 | }, |
| 26374 | .@"enum" => { |
| 26375 | if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { |
| 26376 | return inst; |
| 26377 | } |
| 26378 | try sema.ensureLayoutResolved(child_type, src, .field_used); |
| 26379 | const field_index_usize = child_type.enumFieldIndex(field_name, zcu) orelse |
| 26380 | return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); |
| 26381 | const field_index: u32 = @intCast(field_index_usize); |
| 26382 | const enum_val = try pt.enumValueFieldIndex(child_type, field_index); |
| 26383 | return Air.internedToRef(enum_val.toIntern()); |
| 26384 | }, |
| 26385 | .@"struct", .@"opaque" => { |
| 26386 | if (!child_type.isTuple(zcu) and child_type.toIntern() != .anyopaque_type) { |
| 26387 | if (try sema.namespaceLookupVal(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { |
| 26388 | return inst; |
| 26389 | } |
| 26390 | } |
| 26391 | return sema.failWithBadMemberAccess(block, child_type, src, field_name); |
| 26392 | }, |
| 26393 | else => return sema.failWithOwnedErrorMsg(block, msg: { |
| 26394 | const msg = try sema.errMsg(src, "type '{f}' has no members", .{child_type.fmt(pt)}); |
| 26395 | errdefer msg.destroy(sema.gpa); |
| 26396 | if (child_type.isSlice(zcu)) try sema.errNote(src, msg, "slice values have 'len' and 'ptr' members", .{}); |
| 26397 | if (child_type.zigTypeTag(zcu) == .array) try sema.errNote(src, msg, "array values have 'len' member", .{}); |
| 26398 | break :msg msg; |
| 26399 | }), |
| 26400 | } |
| 26401 | }, |
| 26402 | .@"struct" => if (is_pointer_to) { |
| 26403 | // Avoid loading the entire struct by fetching a pointer and loading that |
| 26404 | try sema.ensureLayoutResolved(inner_ty, src, .ptr_access); |
| 26405 | const field_ptr = try sema.structFieldPtr(block, src, object, field_name, field_name_src, inner_ty); |
| 26406 | return sema.analyzeLoad(block, src, field_ptr, object_src); |
| 26407 | } else { |
| 26408 | return sema.structFieldVal(block, object, field_name, field_name_src, inner_ty); |
| 26409 | }, |
| 26410 | .@"union" => if (is_pointer_to) { |
| 26411 | // Avoid loading the entire union by fetching a pointer and loading that |
| 26412 | try sema.ensureLayoutResolved(inner_ty, src, .ptr_access); |
| 26413 | const field_ptr = try sema.unionFieldPtr(block, src, object, field_name, field_name_src, inner_ty, false); |
| 26414 | return sema.analyzeLoad(block, src, field_ptr, object_src); |
| 26415 | } else { |
| 26416 | return sema.unionFieldVal(block, src, object, field_name, field_name_src, inner_ty); |
| 26417 | }, |
| 26418 | .spirv => if (inner_ty.isSpirvRuntimeArray(zcu) and field_name.eqlSlice("len", ip)) { |
| 26419 | if (!is_pointer_to) { |
| 26420 | return sema.fail( |
| 26421 | block, |
| 26422 | src, |
| 26423 | "accessing 'len' field on a SPIR-V runtime_array requires a pointer to the array field", |
| 26424 | .{}, |
| 26425 | ); |
| 26426 | } |
| 26427 | return sema.analyzeSpirvRuntimeArrayLen(block, src, object, field_name_src); |
| 26428 | }, |
| 26429 | else => {}, |
| 26430 | } |
| 26431 | return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name); |
| 26432 | } |
| 26433 | |
| 26434 | fn analyzeSpirvRuntimeArrayLen( |
| 26435 | sema: *Sema, |
| 26436 | block: *Block, |
| 26437 | src: LazySrcLoc, |
| 26438 | runtime_array_ptr: Air.Inst.Ref, |
| 26439 | src_for_err: LazySrcLoc, |
| 26440 | ) CompileError!Air.Inst.Ref { |
| 26441 | const pt = sema.pt; |
| 26442 | const zcu = pt.zcu; |
| 26443 | const ip = &zcu.intern_pool; |
| 26444 | |
| 26445 | const struct_operand: Air.Inst.Ref, const field_index: u32 = sf: { |
| 26446 | if (runtime_array_ptr.toIndex()) |inst| { |
| 26447 | const tag = sema.air_instructions.items(.tag)[@backingInt(inst)]; |
| 26448 | const data = sema.air_instructions.items(.data)[@backingInt(inst)]; |
| 26449 | switch (tag) { |
| 26450 | .struct_field_ptr => { |
| 26451 | const extra = sema.getTmpAir().extraData(Air.StructField, data.ty_pl.payload).data; |
| 26452 | break :sf .{ extra.struct_operand, extra.field_index }; |
| 26453 | }, |
| 26454 | .struct_field_ptr_index_0 => break :sf .{ data.ty_op.operand, 0 }, |
| 26455 | .struct_field_ptr_index_1 => break :sf .{ data.ty_op.operand, 1 }, |
| 26456 | .struct_field_ptr_index_2 => break :sf .{ data.ty_op.operand, 2 }, |
| 26457 | .struct_field_ptr_index_3 => break :sf .{ data.ty_op.operand, 3 }, |
| 26458 | else => {}, |
| 26459 | } |
| 26460 | } |
| 26461 | |
| 26462 | const ptr_val = sema.resolveValue(runtime_array_ptr) orelse return sema.fail( |
| 26463 | block, |
| 26464 | src_for_err, |
| 26465 | "'len' field on a SPIR-V runtime_array requires direct struct field access", |
| 26466 | .{}, |
| 26467 | ); |
| 26468 | const ptr_key = ip.indexToKey(ptr_val.toIntern()).ptr; |
| 26469 | if (ptr_key.base_addr == .field and ptr_key.byte_offset == 0) { |
| 26470 | const field = ptr_key.base_addr.field; |
| 26471 | break :sf .{ .fromIntern(field.base), @intCast(field.index) }; |
| 26472 | } |
| 26473 | |
| 26474 | const parent_ty: Type = switch (ptr_key.base_addr) { |
| 26475 | .nav => |nav| .fromInterned(ip.getNav(nav).resolved.?.type), |
| 26476 | .uav => |uav| .fromInterned(ip.typeOf(uav.val)), |
| 26477 | .comptime_alloc, |
| 26478 | .comptime_field, |
| 26479 | .eu_payload, |
| 26480 | .opt_payload, |
| 26481 | .arr_elem, |
| 26482 | .field, |
| 26483 | .int, |
| 26484 | => return sema.fail( |
| 26485 | block, |
| 26486 | src_for_err, |
| 26487 | "'len' field on a SPIR-V runtime_array requires direct struct field access", |
| 26488 | .{}, |
| 26489 | ), |
| 26490 | }; |
| 26491 | if (parent_ty.zigTypeTag(zcu) != .@"struct") return sema.fail( |
| 26492 | block, |
| 26493 | src_for_err, |
| 26494 | "'len' field on a SPIR-V runtime_array requires the array to be a struct field", |
| 26495 | .{}, |
| 26496 | ); |
| 26497 | |
| 26498 | const field_ptr_info = ip.indexToKey(ptr_key.ty).ptr_type; |
| 26499 | const rtarr_ty_ip = field_ptr_info.child; |
| 26500 | const struct_obj = ip.loadStructType(parent_ty.toIntern()); |
| 26501 | const field_idx: u32 = for (struct_obj.field_types.get(ip), 0..) |field_ty_ip, i| { |
| 26502 | if (field_ty_ip == rtarr_ty_ip and |
| 26503 | struct_obj.field_offsets.get(ip)[i] == ptr_key.byte_offset) |
| 26504 | { |
| 26505 | break @intCast(i); |
| 26506 | } |
| 26507 | } else unreachable; |
| 26508 | const struct_ptr_ty = try pt.ptrType(.{ |
| 26509 | .child = parent_ty.toIntern(), |
| 26510 | .flags = field_ptr_info.flags, |
| 26511 | }); |
| 26512 | const struct_ptr_val = try sema.ptrSubtract( |
| 26513 | block, |
| 26514 | src_for_err, |
| 26515 | ptr_val, |
| 26516 | ptr_key.byte_offset, |
| 26517 | struct_ptr_ty, |
| 26518 | ); |
| 26519 | break :sf .{ .fromIntern(struct_ptr_val.toIntern()), field_idx }; |
| 26520 | }; |
| 26521 | |
| 26522 | try sema.requireRuntimeBlock(block, src, null); |
| 26523 | return block.addInst(.{ |
| 26524 | .tag = .spirv_runtime_array_len, |
| 26525 | .data = .{ .ty_pl = .{ |
| 26526 | .ty = .u32, |
| 26527 | .payload = try sema.addExtra(Air.StructField{ |
| 26528 | .struct_operand = struct_operand, |
| 26529 | .field_index = field_index, |
| 26530 | }), |
| 26531 | } }, |
| 26532 | }); |
| 26533 | } |
| 26534 | |
| 26535 | fn fieldPtr( |
| 26536 | sema: *Sema, |
| 26537 | block: *Block, |
| 26538 | src: LazySrcLoc, |
| 26539 | object_ptr: Air.Inst.Ref, |
| 26540 | field_name: InternPool.NullTerminatedString, |
| 26541 | field_name_src: LazySrcLoc, |
| 26542 | initializing: bool, |
| 26543 | ) CompileError!Air.Inst.Ref { |
| 26544 | // When editing this function, note that there is corresponding logic to be edited |
| 26545 | // in `fieldVal`. This function takes a pointer and returns a pointer. |
| 26546 | |
| 26547 | const pt = sema.pt; |
| 26548 | const zcu = pt.zcu; |
| 26549 | const ip = &zcu.intern_pool; |
| 26550 | const object_ptr_src = src; // TODO better source location |
| 26551 | const object_ptr_ty = sema.typeOf(object_ptr); |
| 26552 | const object_ty = switch (object_ptr_ty.zigTypeTag(zcu)) { |
| 26553 | .pointer => object_ptr_ty.childType(zcu), |
| 26554 | else => return sema.fail(block, object_ptr_src, "expected pointer, found '{f}'", .{object_ptr_ty.fmt(pt)}), |
| 26555 | }; |
| 26556 | |
| 26557 | // Zig allows dereferencing a single pointer during field lookup. Note that |
| 26558 | // we don't actually need to generate the dereference some field lookups, like the |
| 26559 | // length of arrays and other comptime operations. |
| 26560 | const is_pointer_to = object_ty.isSinglePointer(zcu); |
| 26561 | |
| 26562 | const inner_ty = if (is_pointer_to) |
| 26563 | object_ty.childType(zcu) |
| 26564 | else |
| 26565 | object_ty; |
| 26566 | |
| 26567 | switch (inner_ty.zigTypeTag(zcu)) { |
| 26568 | .array => { |
| 26569 | if (field_name.eqlSlice("len", ip)) { |
| 26570 | const int_val = try pt.intValue(.usize, inner_ty.arrayLen(zcu)); |
| 26571 | return uavRef(sema, int_val); |
| 26572 | } else if (field_name.eqlSlice("ptr", ip) and is_pointer_to) { |
| 26573 | const ptr_info = object_ty.ptrInfo(zcu); |
| 26574 | const new_ptr_ty = try pt.ptrType(.{ |
| 26575 | .child = Type.fromInterned(ptr_info.child).childType(zcu).toIntern(), |
| 26576 | .sentinel = if (inner_ty.sentinel(zcu)) |s| s.toIntern() else .none, |
| 26577 | .flags = .{ |
| 26578 | .size = .many, |
| 26579 | .alignment = ptr_info.flags.alignment, |
| 26580 | .is_const = ptr_info.flags.is_const, |
| 26581 | .is_volatile = ptr_info.flags.is_volatile, |
| 26582 | .is_allowzero = ptr_info.flags.is_allowzero, |
| 26583 | .address_space = ptr_info.flags.address_space, |
| 26584 | .vector_index = ptr_info.flags.vector_index, |
| 26585 | }, |
| 26586 | .packed_offset = ptr_info.packed_offset, |
| 26587 | }); |
| 26588 | const ptr_ptr_info = object_ptr_ty.ptrInfo(zcu); |
| 26589 | const result_ty = try pt.ptrType(.{ |
| 26590 | .child = new_ptr_ty.toIntern(), |
| 26591 | .sentinel = if (object_ptr_ty.sentinel(zcu)) |s| s.toIntern() else .none, |
| 26592 | .flags = .{ |
| 26593 | .size = .one, |
| 26594 | .alignment = ptr_ptr_info.flags.alignment, |
| 26595 | .is_const = ptr_ptr_info.flags.is_const, |
| 26596 | .is_volatile = ptr_ptr_info.flags.is_volatile, |
| 26597 | .is_allowzero = ptr_ptr_info.flags.is_allowzero, |
| 26598 | .address_space = ptr_ptr_info.flags.address_space, |
| 26599 | .vector_index = ptr_ptr_info.flags.vector_index, |
| 26600 | }, |
| 26601 | .packed_offset = ptr_ptr_info.packed_offset, |
| 26602 | }); |
| 26603 | return block.addTyOp(.ptr_cast, result_ty, object_ptr); |
| 26604 | } else { |
| 26605 | return sema.fail( |
| 26606 | block, |
| 26607 | field_name_src, |
| 26608 | "no member named '{f}' in '{f}'", |
| 26609 | .{ field_name.fmt(ip), object_ty.fmt(pt) }, |
| 26610 | ); |
| 26611 | } |
| 26612 | }, |
| 26613 | .pointer => if (inner_ty.isSlice(zcu)) { |
| 26614 | const inner_ptr = if (is_pointer_to) |
| 26615 | try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) |
| 26616 | else |
| 26617 | object_ptr; |
| 26618 | |
| 26619 | const attr_ptr_ty = if (is_pointer_to) object_ty else object_ptr_ty; |
| 26620 | |
| 26621 | if (field_name.eqlSlice("ptr", ip)) { |
| 26622 | const result_ty = try attr_ptr_ty.fieldPtrType(Value.slice_ptr_index, pt); |
| 26623 | if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| { |
| 26624 | return Air.internedToRef((try val.ptrField(Value.slice_ptr_index, pt)).toIntern()); |
| 26625 | } |
| 26626 | try sema.requireRuntimeBlock(block, src, null); |
| 26627 | |
| 26628 | const field_ptr = try block.addTyOp(.ptr_slice_ptr_ptr, result_ty, inner_ptr); |
| 26629 | try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr); |
| 26630 | return field_ptr; |
| 26631 | } else if (field_name.eqlSlice("len", ip)) { |
| 26632 | const result_ty = try attr_ptr_ty.fieldPtrType(Value.slice_len_index, pt); |
| 26633 | if (try sema.resolveDefinedValue(block, object_ptr_src, inner_ptr)) |val| { |
| 26634 | return Air.internedToRef((try val.ptrField(Value.slice_len_index, pt)).toIntern()); |
| 26635 | } |
| 26636 | try sema.requireRuntimeBlock(block, src, null); |
| 26637 | |
| 26638 | const field_ptr = try block.addTyOp(.ptr_slice_len_ptr, result_ty, inner_ptr); |
| 26639 | try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr); |
| 26640 | return field_ptr; |
| 26641 | } else { |
| 26642 | return sema.fail( |
| 26643 | block, |
| 26644 | field_name_src, |
| 26645 | "no member named '{f}' in '{f}'", |
| 26646 | .{ field_name.fmt(ip), object_ty.fmt(pt) }, |
| 26647 | ); |
| 26648 | } |
| 26649 | }, |
| 26650 | .type => { |
| 26651 | const result = try sema.analyzeLoad(block, src, object_ptr, object_ptr_src); |
| 26652 | const inner = if (is_pointer_to) |
| 26653 | try sema.analyzeLoad(block, src, result, object_ptr_src) |
| 26654 | else |
| 26655 | result; |
| 26656 | |
| 26657 | const val = (sema.resolveDefinedValue(block, src, inner) catch unreachable).?; |
| 26658 | const child_type = val.toType(); |
| 26659 | |
| 26660 | switch (child_type.zigTypeTag(zcu)) { |
| 26661 | .error_set => { |
| 26662 | const err_set_ty: Type = err_set: switch (ip.indexToKey(child_type.toIntern())) { |
| 26663 | .inferred_error_set_type => |func_index| { |
| 26664 | try sema.ensureFuncIesResolved(block, src, func_index); |
| 26665 | const resolved_ies = ip.funcIesResolvedUnordered(func_index); |
| 26666 | continue :err_set ip.indexToKey(resolved_ies); |
| 26667 | }, |
| 26668 | .error_set_type => |err_set| if (err_set.nameIndex(ip, field_name) == null) { |
| 26669 | return sema.fail(block, src, "no error named '{f}' in '{f}'", .{ |
| 26670 | field_name.fmt(ip), child_type.fmt(pt), |
| 26671 | }); |
| 26672 | } else child_type, |
| 26673 | .simple_type => |t| { |
| 26674 | assert(t == .anyerror); |
| 26675 | _ = try pt.getErrorValue(field_name); |
| 26676 | break :err_set try pt.singleErrorSetType(field_name); |
| 26677 | }, |
| 26678 | else => unreachable, |
| 26679 | }; |
| 26680 | return uavRef(sema, .fromInterned(try pt.intern(.{ .err = .{ |
| 26681 | .ty = err_set_ty.toIntern(), |
| 26682 | .name = field_name, |
| 26683 | } }))); |
| 26684 | }, |
| 26685 | .@"union" => { |
| 26686 | if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { |
| 26687 | return inst; |
| 26688 | } |
| 26689 | try sema.ensureLayoutResolved(child_type, src, .field_used); |
| 26690 | if (child_type.unionTagType(zcu)) |enum_ty| { |
| 26691 | if (enum_ty.enumFieldIndex(field_name, zcu)) |field_index| { |
| 26692 | const field_index_u32: u32 = @intCast(field_index); |
| 26693 | const idx_val = try pt.enumValueFieldIndex(enum_ty, field_index_u32); |
| 26694 | return uavRef(sema, idx_val); |
| 26695 | } |
| 26696 | } |
| 26697 | return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); |
| 26698 | }, |
| 26699 | .@"enum" => { |
| 26700 | if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { |
| 26701 | return inst; |
| 26702 | } |
| 26703 | try sema.ensureLayoutResolved(child_type, src, .field_used); |
| 26704 | const field_index = child_type.enumFieldIndex(field_name, zcu) orelse { |
| 26705 | return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); |
| 26706 | }; |
| 26707 | const field_index_u32: u32 = @intCast(field_index); |
| 26708 | const idx_val = try pt.enumValueFieldIndex(child_type, field_index_u32); |
| 26709 | return uavRef(sema, idx_val); |
| 26710 | }, |
| 26711 | .@"struct", .@"opaque" => { |
| 26712 | if (try sema.namespaceLookupRef(block, src, child_type.getNamespaceIndex(zcu), field_name)) |inst| { |
| 26713 | return inst; |
| 26714 | } |
| 26715 | return sema.failWithBadMemberAccess(block, child_type, field_name_src, field_name); |
| 26716 | }, |
| 26717 | else => return sema.fail(block, src, "type '{f}' has no members", .{child_type.fmt(pt)}), |
| 26718 | } |
| 26719 | }, |
| 26720 | .@"struct" => { |
| 26721 | const inner_ptr = if (is_pointer_to) |
| 26722 | try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) |
| 26723 | else |
| 26724 | object_ptr; |
| 26725 | try sema.ensureLayoutResolved(inner_ty, src, .ptr_access); |
| 26726 | const field_ptr = try sema.structFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty); |
| 26727 | try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr); |
| 26728 | return field_ptr; |
| 26729 | }, |
| 26730 | .@"union" => { |
| 26731 | const inner_ptr = if (is_pointer_to) |
| 26732 | try sema.analyzeLoad(block, src, object_ptr, object_ptr_src) |
| 26733 | else |
| 26734 | object_ptr; |
| 26735 | try sema.ensureLayoutResolved(inner_ty, src, .ptr_access); |
| 26736 | const field_ptr = try sema.unionFieldPtr(block, src, inner_ptr, field_name, field_name_src, inner_ty, initializing); |
| 26737 | try sema.checkKnownAllocPtr(block, inner_ptr, field_ptr); |
| 26738 | return field_ptr; |
| 26739 | }, |
| 26740 | else => {}, |
| 26741 | } |
| 26742 | return sema.failWithInvalidFieldAccess(block, src, object_ty, field_name); |
| 26743 | } |
| 26744 | |
| 26745 | const ResolvedFieldCallee = union(enum) { |
| 26746 | /// The LHS of the call was an actual field with this value. |
| 26747 | direct: Air.Inst.Ref, |
| 26748 | /// This is a method call, with the function and first argument given. |
| 26749 | method: struct { |
| 26750 | func_inst: Air.Inst.Ref, |
| 26751 | arg0_inst: Air.Inst.Ref, |
| 26752 | }, |
| 26753 | }; |
| 26754 | |
| 26755 | fn fieldCallBind( |
| 26756 | sema: *Sema, |
| 26757 | block: *Block, |
| 26758 | src: LazySrcLoc, |
| 26759 | raw_ptr: Air.Inst.Ref, |
| 26760 | field_name: InternPool.NullTerminatedString, |
| 26761 | field_name_src: LazySrcLoc, |
| 26762 | ) CompileError!ResolvedFieldCallee { |
| 26763 | // When editing this function, note that there is corresponding logic to be edited |
| 26764 | // in `fieldVal`. This function takes a pointer and returns a pointer. |
| 26765 | |
| 26766 | const pt = sema.pt; |
| 26767 | const zcu = pt.zcu; |
| 26768 | const ip = &zcu.intern_pool; |
| 26769 | const raw_ptr_src = src; // TODO better source location |
| 26770 | const raw_ptr_ty = sema.typeOf(raw_ptr); |
| 26771 | const inner_ty = if (raw_ptr_ty.zigTypeTag(zcu) == .pointer and (raw_ptr_ty.ptrSize(zcu) == .one or raw_ptr_ty.ptrSize(zcu) == .c)) |
| 26772 | raw_ptr_ty.childType(zcu) |
| 26773 | else |
| 26774 | return sema.fail(block, raw_ptr_src, "expected single pointer, found '{f}'", .{raw_ptr_ty.fmt(pt)}); |
| 26775 | |
| 26776 | // Optionally dereference a second pointer to get the concrete type. |
| 26777 | const is_double_ptr = inner_ty.zigTypeTag(zcu) == .pointer and inner_ty.ptrSize(zcu) == .one; |
| 26778 | const concrete_ty = if (is_double_ptr) inner_ty.childType(zcu) else inner_ty; |
| 26779 | try sema.ensureLayoutResolved(concrete_ty, src, .ptr_access); |
| 26780 | const ptr_ty = if (is_double_ptr) inner_ty else raw_ptr_ty; |
| 26781 | const object_ptr = if (is_double_ptr) |
| 26782 | try sema.analyzeLoad(block, src, raw_ptr, src) |
| 26783 | else |
| 26784 | raw_ptr; |
| 26785 | |
| 26786 | find_field: { |
| 26787 | switch (concrete_ty.zigTypeTag(zcu)) { |
| 26788 | .@"struct" => { |
| 26789 | if (zcu.typeToStruct(concrete_ty)) |struct_type| { |
| 26790 | const field_index = struct_type.nameIndex(ip, field_name) orelse break :find_field; |
| 26791 | return sema.finishFieldCallBind(block, src, ptr_ty, field_index, object_ptr); |
| 26792 | } else if (concrete_ty.isTuple(zcu)) { |
| 26793 | if (field_name.eqlSlice("len", ip)) { |
| 26794 | return .{ .direct = try pt.intRef(.usize, concrete_ty.structFieldCount(zcu)) }; |
| 26795 | } |
| 26796 | if (field_name.toUnsigned(ip)) |field_index| { |
| 26797 | if (field_index >= concrete_ty.structFieldCount(zcu)) break :find_field; |
| 26798 | return sema.finishFieldCallBind(block, src, ptr_ty, field_index, object_ptr); |
| 26799 | } |
| 26800 | } else { |
| 26801 | const max = concrete_ty.structFieldCount(zcu); |
| 26802 | for (0..max) |i_usize| { |
| 26803 | const i: u32 = @intCast(i_usize); |
| 26804 | if (field_name == concrete_ty.structFieldName(i, zcu).unwrap().?) { |
| 26805 | return sema.finishFieldCallBind(block, src, ptr_ty, i, object_ptr); |
| 26806 | } |
| 26807 | } |
| 26808 | } |
| 26809 | }, |
| 26810 | .@"union" => { |
| 26811 | const union_obj = zcu.typeToUnion(concrete_ty).?; |
| 26812 | const enum_obj = ip.loadEnumType(union_obj.enum_tag_type); |
| 26813 | if (enum_obj.nameIndex(ip, field_name) == null) break :find_field; |
| 26814 | const field_ptr = try unionFieldPtr(sema, block, src, object_ptr, field_name, field_name_src, concrete_ty, false); |
| 26815 | return .{ .direct = try sema.analyzeLoad(block, src, field_ptr, src) }; |
| 26816 | }, |
| 26817 | .type => { |
| 26818 | const namespace = try sema.analyzeLoad(block, src, object_ptr, src); |
| 26819 | return .{ .direct = try sema.fieldVal(block, src, namespace, field_name, field_name_src) }; |
| 26820 | }, |
| 26821 | else => {}, |
| 26822 | } |
| 26823 | } |
| 26824 | |
| 26825 | // If we get here, we need to look for a decl in the struct type instead. |
| 26826 | const found_nav = found_nav: { |
| 26827 | const namespace = concrete_ty.getNamespace(zcu).unwrap() orelse |
| 26828 | break :found_nav null; |
| 26829 | const nav_index = try sema.namespaceLookup(block, src, namespace, field_name) orelse |
| 26830 | break :found_nav null; |
| 26831 | |
| 26832 | const decl_val = try sema.analyzeNavVal(block, src, nav_index); |
| 26833 | const decl_type = sema.typeOf(decl_val); |
| 26834 | if (zcu.typeToFunc(decl_type)) |func_type| f: { |
| 26835 | if (func_type.param_types.len == 0) break :f; |
| 26836 | |
| 26837 | const first_param_type: Type = .fromInterned(func_type.param_types.get(ip)[0]); |
| 26838 | if (first_param_type.isGenericPoison() or |
| 26839 | (first_param_type.zigTypeTag(zcu) == .pointer and |
| 26840 | (first_param_type.ptrSize(zcu) == .one or |
| 26841 | first_param_type.ptrSize(zcu) == .c) and |
| 26842 | first_param_type.childType(zcu).eql(concrete_ty))) |
| 26843 | { |
| 26844 | // Note that if the param type is generic poison, we know that it must |
| 26845 | // specifically be `anytype` since it's the first parameter, meaning we |
| 26846 | // can safely assume it can be a pointer. |
| 26847 | // TODO: bound fn calls on rvalues should probably |
| 26848 | // generate a by-value argument somehow. |
| 26849 | return .{ .method = .{ |
| 26850 | .func_inst = decl_val, |
| 26851 | .arg0_inst = object_ptr, |
| 26852 | } }; |
| 26853 | } else if (first_param_type.eql(concrete_ty)) { |
| 26854 | const deref = try sema.analyzeLoad(block, src, object_ptr, src); |
| 26855 | return .{ .method = .{ |
| 26856 | .func_inst = decl_val, |
| 26857 | .arg0_inst = deref, |
| 26858 | } }; |
| 26859 | } else if (first_param_type.zigTypeTag(zcu) == .optional) { |
| 26860 | const child = first_param_type.optionalChild(zcu); |
| 26861 | if (child.eql(concrete_ty)) { |
| 26862 | const deref = try sema.analyzeLoad(block, src, object_ptr, src); |
| 26863 | return .{ .method = .{ |
| 26864 | .func_inst = decl_val, |
| 26865 | .arg0_inst = deref, |
| 26866 | } }; |
| 26867 | } else if (child.zigTypeTag(zcu) == .pointer and |
| 26868 | child.ptrSize(zcu) == .one and |
| 26869 | child.childType(zcu).eql(concrete_ty)) |
| 26870 | { |
| 26871 | return .{ .method = .{ |
| 26872 | .func_inst = decl_val, |
| 26873 | .arg0_inst = object_ptr, |
| 26874 | } }; |
| 26875 | } |
| 26876 | } else if (first_param_type.zigTypeTag(zcu) == .error_union and |
| 26877 | first_param_type.errorUnionPayload(zcu).eql(concrete_ty)) |
| 26878 | { |
| 26879 | const deref = try sema.analyzeLoad(block, src, object_ptr, src); |
| 26880 | return .{ .method = .{ |
| 26881 | .func_inst = decl_val, |
| 26882 | .arg0_inst = deref, |
| 26883 | } }; |
| 26884 | } |
| 26885 | } |
| 26886 | break :found_nav nav_index; |
| 26887 | }; |
| 26888 | |
| 26889 | const msg = msg: { |
| 26890 | const msg = try sema.errMsg(src, "no field or member function named '{f}' in '{f}'", .{ |
| 26891 | field_name.fmt(ip), |
| 26892 | concrete_ty.fmt(pt), |
| 26893 | }); |
| 26894 | errdefer msg.destroy(sema.gpa); |
| 26895 | try sema.addDeclaredHereNote(msg, concrete_ty); |
| 26896 | if (found_nav) |nav_index| { |
| 26897 | try sema.errNote( |
| 26898 | zcu.navSrcLoc(nav_index), |
| 26899 | msg, |
| 26900 | "'{f}' is not a member function", |
| 26901 | .{field_name.fmt(ip)}, |
| 26902 | ); |
| 26903 | } |
| 26904 | if (concrete_ty.zigTypeTag(zcu) == .error_union) { |
| 26905 | try sema.errNote(src, msg, "consider using 'try', 'catch', or 'if'", .{}); |
| 26906 | } |
| 26907 | if (is_double_ptr) { |
| 26908 | try sema.errNote(src, msg, "method invocation only supports up to one level of implicit pointer dereferencing", .{}); |
| 26909 | try sema.errNote(src, msg, "use '.*' to dereference pointer", .{}); |
| 26910 | } |
| 26911 | break :msg msg; |
| 26912 | }; |
| 26913 | return sema.failWithOwnedErrorMsg(block, msg); |
| 26914 | } |
| 26915 | |
| 26916 | fn finishFieldCallBind( |
| 26917 | sema: *Sema, |
| 26918 | block: *Block, |
| 26919 | src: LazySrcLoc, |
| 26920 | ptr_ty: Type, |
| 26921 | field_index: u32, |
| 26922 | object_ptr: Air.Inst.Ref, |
| 26923 | ) CompileError!ResolvedFieldCallee { |
| 26924 | const pt = sema.pt; |
| 26925 | const zcu = pt.zcu; |
| 26926 | const ptr_field_ty = try ptr_ty.fieldPtrType(field_index, pt); |
| 26927 | |
| 26928 | const container_ty = ptr_ty.childType(zcu); |
| 26929 | if (container_ty.zigTypeTag(zcu) == .@"struct") { |
| 26930 | if (container_ty.structFieldIsComptime(field_index, zcu)) { |
| 26931 | const default_val = (try container_ty.structFieldValueComptime(pt, field_index)).?; |
| 26932 | return .{ .direct = Air.internedToRef(default_val.toIntern()) }; |
| 26933 | } |
| 26934 | } |
| 26935 | |
| 26936 | if (try sema.resolveDefinedValue(block, src, object_ptr)) |struct_ptr_val| { |
| 26937 | const ptr_val = try struct_ptr_val.ptrField(field_index, pt); |
| 26938 | const pointer = Air.internedToRef(ptr_val.toIntern()); |
| 26939 | return .{ .direct = try sema.analyzeLoad(block, src, pointer, src) }; |
| 26940 | } |
| 26941 | |
| 26942 | try sema.requireRuntimeBlock(block, src, null); |
| 26943 | const ptr_inst = try block.addStructFieldPtr(object_ptr, field_index, ptr_field_ty); |
| 26944 | return .{ .direct = try sema.analyzeLoad(block, src, ptr_inst, src) }; |
| 26945 | } |
| 26946 | |
| 26947 | fn namespaceLookup( |
| 26948 | sema: *Sema, |
| 26949 | block: *Block, |
| 26950 | src: LazySrcLoc, |
| 26951 | namespace: InternPool.NamespaceIndex, |
| 26952 | decl_name: InternPool.NullTerminatedString, |
| 26953 | ) CompileError!?InternPool.Nav.Index { |
| 26954 | const pt = sema.pt; |
| 26955 | const zcu = pt.zcu; |
| 26956 | const gpa = sema.gpa; |
| 26957 | if (try sema.lookupInNamespace(block, namespace, decl_name)) |lookup| { |
| 26958 | if (lookup.accessible == .private) { |
| 26959 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 26960 | const msg = try sema.errMsg(src, "'{f}' is not marked 'pub'", .{ |
| 26961 | decl_name.fmt(&zcu.intern_pool), |
| 26962 | }); |
| 26963 | errdefer msg.destroy(gpa); |
| 26964 | try sema.errNote(zcu.navSrcLoc(lookup.nav), msg, "declared here", .{}); |
| 26965 | break :msg msg; |
| 26966 | }); |
| 26967 | } |
| 26968 | return lookup.nav; |
| 26969 | } |
| 26970 | return null; |
| 26971 | } |
| 26972 | |
| 26973 | fn namespaceLookupRef( |
| 26974 | sema: *Sema, |
| 26975 | block: *Block, |
| 26976 | src: LazySrcLoc, |
| 26977 | namespace: InternPool.NamespaceIndex, |
| 26978 | decl_name: InternPool.NullTerminatedString, |
| 26979 | ) CompileError!?Air.Inst.Ref { |
| 26980 | const nav = try sema.namespaceLookup(block, src, namespace, decl_name) orelse return null; |
| 26981 | return try sema.analyzeNavRef(block, src, nav); |
| 26982 | } |
| 26983 | |
| 26984 | fn namespaceLookupVal( |
| 26985 | sema: *Sema, |
| 26986 | block: *Block, |
| 26987 | src: LazySrcLoc, |
| 26988 | namespace: InternPool.NamespaceIndex, |
| 26989 | decl_name: InternPool.NullTerminatedString, |
| 26990 | ) CompileError!?Air.Inst.Ref { |
| 26991 | const nav = try sema.namespaceLookup(block, src, namespace, decl_name) orelse return null; |
| 26992 | return try sema.analyzeNavVal(block, src, nav); |
| 26993 | } |
| 26994 | |
| 26995 | /// Asserts that the layout of `struct_ty` is already resolved. |
| 26996 | fn structFieldPtr( |
| 26997 | sema: *Sema, |
| 26998 | block: *Block, |
| 26999 | src: LazySrcLoc, |
| 27000 | struct_ptr: Air.Inst.Ref, |
| 27001 | field_name: InternPool.NullTerminatedString, |
| 27002 | field_name_src: LazySrcLoc, |
| 27003 | struct_ty: Type, |
| 27004 | ) CompileError!Air.Inst.Ref { |
| 27005 | const pt = sema.pt; |
| 27006 | const zcu = pt.zcu; |
| 27007 | const ip = &zcu.intern_pool; |
| 27008 | |
| 27009 | assert(struct_ty.zigTypeTag(zcu) == .@"struct"); |
| 27010 | struct_ty.assertHasLayout(zcu); |
| 27011 | |
| 27012 | const field_index: u32 = if (struct_ty.isTuple(zcu)) field_index: { |
| 27013 | if (field_name.eqlSlice("len", ip)) { |
| 27014 | const len_inst = try pt.intRef(.usize, struct_ty.structFieldCount(zcu)); |
| 27015 | return sema.analyzeRef(block, src, len_inst, .none); |
| 27016 | } |
| 27017 | break :field_index try sema.tupleFieldIndex(block, struct_ty, field_name, field_name_src); |
| 27018 | } else field_index: { |
| 27019 | const struct_type = zcu.typeToStruct(struct_ty).?; |
| 27020 | break :field_index struct_type.nameIndex(ip, field_name) orelse { |
| 27021 | return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name); |
| 27022 | }; |
| 27023 | }; |
| 27024 | |
| 27025 | return sema.structFieldPtrByIndex(block, src, struct_ptr, field_index, struct_ty); |
| 27026 | } |
| 27027 | |
| 27028 | /// Supports both structs and unions. |
| 27029 | /// |
| 27030 | /// Asserts that the layout of `struct_ty` is already resolved. |
| 27031 | fn structFieldPtrByIndex( |
| 27032 | sema: *Sema, |
| 27033 | block: *Block, |
| 27034 | src: LazySrcLoc, |
| 27035 | struct_ptr: Air.Inst.Ref, |
| 27036 | field_index: u32, |
| 27037 | struct_ty: Type, |
| 27038 | ) CompileError!Air.Inst.Ref { |
| 27039 | const pt = sema.pt; |
| 27040 | const zcu = pt.zcu; |
| 27041 | |
| 27042 | struct_ty.assertHasLayout(zcu); |
| 27043 | const struct_ptr_ty = sema.typeOf(struct_ptr); |
| 27044 | |
| 27045 | if (struct_ty.structFieldIsComptime(field_index, zcu)) { |
| 27046 | const field_ptr_ty = try struct_ptr_ty.fieldPtrType(field_index, pt); |
| 27047 | return .fromIntern(try pt.intern(.{ .ptr = .{ |
| 27048 | .ty = field_ptr_ty.toIntern(), |
| 27049 | .base_addr = .{ .comptime_field = struct_ty.structFieldDefaultValue(field_index, zcu).?.toIntern() }, |
| 27050 | .byte_offset = 0, |
| 27051 | } })); |
| 27052 | } else if (try sema.resolveDefinedValue(block, src, struct_ptr)) |struct_ptr_val| { |
| 27053 | return .fromValue(try struct_ptr_val.ptrField(field_index, pt)); |
| 27054 | } else { |
| 27055 | const field_ptr_ty = try struct_ptr_ty.fieldPtrType(field_index, pt); |
| 27056 | return block.addStructFieldPtr(struct_ptr, field_index, field_ptr_ty); |
| 27057 | } |
| 27058 | } |
| 27059 | |
| 27060 | fn structFieldVal( |
| 27061 | sema: *Sema, |
| 27062 | block: *Block, |
| 27063 | struct_byval: Air.Inst.Ref, |
| 27064 | field_name: InternPool.NullTerminatedString, |
| 27065 | field_name_src: LazySrcLoc, |
| 27066 | struct_ty: Type, |
| 27067 | ) CompileError!Air.Inst.Ref { |
| 27068 | const pt = sema.pt; |
| 27069 | const zcu = pt.zcu; |
| 27070 | const ip = &zcu.intern_pool; |
| 27071 | assert(struct_ty.zigTypeTag(zcu) == .@"struct"); |
| 27072 | assert(sema.typeOf(struct_byval).toIntern() == struct_ty.toIntern()); |
| 27073 | struct_ty.assertHasLayout(zcu); |
| 27074 | |
| 27075 | switch (ip.indexToKey(struct_ty.toIntern())) { |
| 27076 | .struct_type => { |
| 27077 | const struct_type = ip.loadStructType(struct_ty.toIntern()); |
| 27078 | |
| 27079 | const field_index = struct_type.nameIndex(ip, field_name) orelse |
| 27080 | return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_name_src, field_name); |
| 27081 | if (struct_type.field_is_comptime_bits.get(ip, field_index)) { |
| 27082 | return .fromIntern(struct_type.field_defaults.get(ip)[field_index]); |
| 27083 | } |
| 27084 | |
| 27085 | const field_ty: Type = .fromInterned(struct_type.field_types.get(ip)[field_index]); |
| 27086 | if (try field_ty.onePossibleValue(pt)) |field_val| |
| 27087 | return .fromValue(field_val); |
| 27088 | |
| 27089 | if (sema.resolveValue(struct_byval)) |struct_val| { |
| 27090 | if (struct_val.isUndef(zcu)) return pt.undefRef(field_ty); |
| 27091 | return .fromValue(try struct_val.fieldValue(pt, field_index)); |
| 27092 | } |
| 27093 | |
| 27094 | return block.addStructFieldVal(struct_byval, field_index, field_ty); |
| 27095 | }, |
| 27096 | .tuple_type => { |
| 27097 | return sema.tupleFieldVal(block, struct_byval, field_name, field_name_src, struct_ty); |
| 27098 | }, |
| 27099 | else => unreachable, |
| 27100 | } |
| 27101 | } |
| 27102 | |
| 27103 | fn tupleFieldVal( |
| 27104 | sema: *Sema, |
| 27105 | block: *Block, |
| 27106 | tuple_byval: Air.Inst.Ref, |
| 27107 | field_name: InternPool.NullTerminatedString, |
| 27108 | field_name_src: LazySrcLoc, |
| 27109 | tuple_ty: Type, |
| 27110 | ) CompileError!Air.Inst.Ref { |
| 27111 | const pt = sema.pt; |
| 27112 | const zcu = pt.zcu; |
| 27113 | if (field_name.eqlSlice("len", &zcu.intern_pool)) { |
| 27114 | return pt.intRef(.usize, tuple_ty.structFieldCount(zcu)); |
| 27115 | } |
| 27116 | const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_name_src); |
| 27117 | return sema.tupleFieldValByIndex(block, tuple_byval, field_index, tuple_ty); |
| 27118 | } |
| 27119 | |
| 27120 | /// Asserts that `field_name` is not "len". |
| 27121 | fn tupleFieldIndex( |
| 27122 | sema: *Sema, |
| 27123 | block: *Block, |
| 27124 | tuple_ty: Type, |
| 27125 | field_name: InternPool.NullTerminatedString, |
| 27126 | field_name_src: LazySrcLoc, |
| 27127 | ) CompileError!u32 { |
| 27128 | const pt = sema.pt; |
| 27129 | const ip = &pt.zcu.intern_pool; |
| 27130 | assert(!field_name.eqlSlice("len", ip)); |
| 27131 | if (field_name.toUnsigned(ip)) |field_index| { |
| 27132 | if (field_index < tuple_ty.structFieldCount(pt.zcu)) return field_index; |
| 27133 | return sema.fail(block, field_name_src, "index '{f}' out of bounds of tuple '{f}'", .{ |
| 27134 | field_name.fmt(ip), tuple_ty.fmt(pt), |
| 27135 | }); |
| 27136 | } |
| 27137 | |
| 27138 | return sema.fail(block, field_name_src, "no field named '{f}' in tuple '{f}'", .{ |
| 27139 | field_name.fmt(ip), tuple_ty.fmt(pt), |
| 27140 | }); |
| 27141 | } |
| 27142 | |
| 27143 | fn tupleFieldValByIndex( |
| 27144 | sema: *Sema, |
| 27145 | block: *Block, |
| 27146 | tuple_byval: Air.Inst.Ref, |
| 27147 | field_index: u32, |
| 27148 | tuple_ty: Type, |
| 27149 | ) CompileError!Air.Inst.Ref { |
| 27150 | const pt = sema.pt; |
| 27151 | const zcu = pt.zcu; |
| 27152 | const field_ty = tuple_ty.fieldType(field_index, zcu); |
| 27153 | |
| 27154 | if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_value| { |
| 27155 | return Air.internedToRef(default_value.toIntern()); |
| 27156 | } |
| 27157 | |
| 27158 | if (try field_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 27159 | |
| 27160 | if (sema.resolveValue(tuple_byval)) |tuple_val| { |
| 27161 | return switch (zcu.intern_pool.indexToKey(tuple_val.toIntern())) { |
| 27162 | .undef => pt.undefRef(field_ty), |
| 27163 | .aggregate => |aggregate| Air.internedToRef(switch (aggregate.storage) { |
| 27164 | .bytes => |bytes| try pt.intValue(.u8, bytes.at(field_index, &zcu.intern_pool)), |
| 27165 | .elems => |elems| Value.fromInterned(elems[field_index]), |
| 27166 | .repeated_elem => |elem| Value.fromInterned(elem), |
| 27167 | }.toIntern()), |
| 27168 | else => unreachable, |
| 27169 | }; |
| 27170 | } |
| 27171 | |
| 27172 | return block.addStructFieldVal(tuple_byval, field_index, field_ty); |
| 27173 | } |
| 27174 | |
| 27175 | /// Asserts that the layout of `union_ty` is already resolved. |
| 27176 | fn unionFieldPtr( |
| 27177 | sema: *Sema, |
| 27178 | block: *Block, |
| 27179 | src: LazySrcLoc, |
| 27180 | union_ptr: Air.Inst.Ref, |
| 27181 | field_name: InternPool.NullTerminatedString, |
| 27182 | field_name_src: LazySrcLoc, |
| 27183 | union_ty: Type, |
| 27184 | initializing: bool, |
| 27185 | ) CompileError!Air.Inst.Ref { |
| 27186 | const pt = sema.pt; |
| 27187 | const zcu = pt.zcu; |
| 27188 | const ip = &zcu.intern_pool; |
| 27189 | |
| 27190 | assert(union_ty.zigTypeTag(zcu) == .@"union"); |
| 27191 | union_ty.assertHasLayout(zcu); |
| 27192 | |
| 27193 | const union_obj = zcu.typeToUnion(union_ty).?; |
| 27194 | const tag_ty: Type = .fromInterned(union_obj.enum_tag_type); |
| 27195 | |
| 27196 | const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); |
| 27197 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); |
| 27198 | |
| 27199 | if (initializing and field_ty.classify(zcu) == .no_possible_value) { |
| 27200 | const msg = msg: { |
| 27201 | const msg = try sema.errMsg(src, "cannot initialize union field with uninstantiable type '{f}'", .{field_ty.fmt(pt)}); |
| 27202 | errdefer msg.destroy(sema.gpa); |
| 27203 | |
| 27204 | try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{ |
| 27205 | field_name.fmt(ip), |
| 27206 | }); |
| 27207 | try sema.addDeclaredHereNote(msg, union_ty); |
| 27208 | break :msg msg; |
| 27209 | }; |
| 27210 | return sema.failWithOwnedErrorMsg(block, msg); |
| 27211 | } |
| 27212 | |
| 27213 | if (try sema.resolveDefinedValue(block, src, union_ptr)) |union_ptr_val| ct: { |
| 27214 | switch (union_obj.layout) { |
| 27215 | .auto => if (initializing) { |
| 27216 | if (!sema.isComptimeMutablePtr(union_ptr_val)) { |
| 27217 | // The initialization is a runtime operation. |
| 27218 | break :ct; |
| 27219 | } |
| 27220 | // Store to the union to initialize the tag. |
| 27221 | const field_tag = try pt.enumValueFieldIndex(tag_ty, field_index); |
| 27222 | const payload_val = try field_ty.onePossibleValue(pt) orelse try pt.undefValue(field_ty); |
| 27223 | const new_union_val = try pt.unionValue(union_ty, field_tag, payload_val); |
| 27224 | try sema.storePtrVal(block, src, union_ptr_val, new_union_val, union_ty); |
| 27225 | } else { |
| 27226 | const union_val = try sema.pointerDeref(block, src, union_ptr_val, union_ptr_val.typeOf(zcu)) orelse break :ct; |
| 27227 | if (union_val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, null); |
| 27228 | const active_index = tag_ty.enumTagFieldIndex(union_val.unionTag(zcu).?, zcu).?; |
| 27229 | if (active_index != field_index) { |
| 27230 | const msg = msg: { |
| 27231 | const active_field_name = tag_ty.enumFieldName(active_index, zcu); |
| 27232 | const msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{ |
| 27233 | field_name.fmt(ip), |
| 27234 | active_field_name.fmt(ip), |
| 27235 | }); |
| 27236 | errdefer msg.destroy(sema.gpa); |
| 27237 | try sema.addDeclaredHereNote(msg, union_ty); |
| 27238 | break :msg msg; |
| 27239 | }; |
| 27240 | return sema.failWithOwnedErrorMsg(block, msg); |
| 27241 | } |
| 27242 | }, |
| 27243 | .@"packed", .@"extern" => {}, |
| 27244 | } |
| 27245 | return .fromValue(try union_ptr_val.ptrField(field_index, pt)); |
| 27246 | } |
| 27247 | |
| 27248 | // If the union has a tag, we must either set or or safety check it depending on `initializing`. |
| 27249 | tag: { |
| 27250 | if (union_ty.containerLayout(zcu) != .auto) break :tag; |
| 27251 | if (tag_ty.classify(zcu) == .one_possible_value) break :tag; |
| 27252 | // There is a hypothetical non-trivial tag. We must set it even if not there at runtime, but |
| 27253 | // only emit a safety check if it's available at runtime (i.e. it's safety-tagged). |
| 27254 | const want_tag = try pt.enumValueFieldIndex(tag_ty, field_index); |
| 27255 | if (initializing) { |
| 27256 | const set_tag_inst = try block.addBinOp(.set_union_tag, union_ptr, .fromValue(want_tag)); |
| 27257 | try sema.checkComptimeKnownStore(block, set_tag_inst, .unneeded); // `unneeded` since this isn't a "proper" store |
| 27258 | } else if (block.wantSafety() and union_obj.has_runtime_tag) { |
| 27259 | // The tag exists at runtime (actual or safety tag), so emit a safety check. |
| 27260 | // TODO would it be better if get_union_tag supported pointers to unions? |
| 27261 | const union_val = try block.addTyOp(.load, union_ty, union_ptr); |
| 27262 | const active_tag = try block.addTyOp(.get_union_tag, tag_ty, union_val); |
| 27263 | try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, .fromValue(want_tag)); |
| 27264 | } |
| 27265 | } |
| 27266 | if (field_ty.classify(zcu) == .no_possible_value) { |
| 27267 | _ = try block.addNoOp(.unreach); |
| 27268 | return .unreachable_value; |
| 27269 | } |
| 27270 | |
| 27271 | const field_ptr_ty = try sema.typeOf(union_ptr).fieldPtrType(field_index, pt); |
| 27272 | return block.addStructFieldPtr(union_ptr, field_index, field_ptr_ty); |
| 27273 | } |
| 27274 | |
| 27275 | fn unionFieldVal( |
| 27276 | sema: *Sema, |
| 27277 | block: *Block, |
| 27278 | src: LazySrcLoc, |
| 27279 | union_byval: Air.Inst.Ref, |
| 27280 | field_name: InternPool.NullTerminatedString, |
| 27281 | field_name_src: LazySrcLoc, |
| 27282 | union_ty: Type, |
| 27283 | ) CompileError!Air.Inst.Ref { |
| 27284 | const pt = sema.pt; |
| 27285 | const zcu = pt.zcu; |
| 27286 | const ip = &zcu.intern_pool; |
| 27287 | assert(union_ty.zigTypeTag(zcu) == .@"union"); |
| 27288 | assert(sema.typeOf(union_byval).toIntern() == union_ty.toIntern()); |
| 27289 | union_ty.assertHasLayout(zcu); |
| 27290 | |
| 27291 | const union_obj = zcu.typeToUnion(union_ty).?; |
| 27292 | const field_index = try sema.unionFieldIndex(block, union_ty, field_name, field_name_src); |
| 27293 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); |
| 27294 | const enum_tag_ty: Type = .fromInterned(union_obj.enum_tag_type); |
| 27295 | |
| 27296 | if (sema.resolveValue(union_byval)) |union_val| { |
| 27297 | if (union_val.isUndef(zcu)) return pt.undefRef(field_ty); |
| 27298 | switch (union_obj.layout) { |
| 27299 | .auto => { |
| 27300 | const active_tag_val = union_val.unionTag(zcu).?; |
| 27301 | const active_index = enum_tag_ty.enumTagFieldIndex(active_tag_val, zcu).?; |
| 27302 | if (active_index == field_index) return .fromValue(union_val.unionPayload(zcu)); |
| 27303 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 27304 | const msg = try sema.errMsg(src, "access of union field '{f}' while field '{f}' is active", .{ |
| 27305 | field_name.fmt(ip), enum_tag_ty.enumFieldName(active_index, zcu).fmt(ip), |
| 27306 | }); |
| 27307 | errdefer msg.destroy(zcu.comp.gpa); |
| 27308 | try sema.addDeclaredHereNote(msg, union_ty); |
| 27309 | break :msg msg; |
| 27310 | }); |
| 27311 | }, |
| 27312 | .@"extern" => if (try sema.castMemory(union_val, field_ty, 0)) |field_val| { |
| 27313 | return .fromValue(field_val); |
| 27314 | } else { |
| 27315 | // Runtime-known due to a pointer-to-integer conversion. |
| 27316 | }, |
| 27317 | .@"packed" => { |
| 27318 | return .fromValue(try sema.bitCastVal(union_val, field_ty)); |
| 27319 | }, |
| 27320 | } |
| 27321 | } |
| 27322 | |
| 27323 | if (union_obj.layout == .auto and block.wantSafety() and union_obj.has_runtime_tag) { |
| 27324 | const wanted_tag_val = try pt.enumValueFieldIndex(enum_tag_ty, field_index); |
| 27325 | const active_tag = try block.addTyOp(.get_union_tag, enum_tag_ty, union_byval); |
| 27326 | try sema.addSafetyCheckInactiveUnionField(block, src, active_tag, .fromValue(wanted_tag_val)); |
| 27327 | } |
| 27328 | |
| 27329 | if (field_ty.classify(zcu) == .no_possible_value) { |
| 27330 | _ = try block.addNoOp(.unreach); |
| 27331 | return .unreachable_value; |
| 27332 | } |
| 27333 | |
| 27334 | if (try field_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 27335 | |
| 27336 | return block.addStructFieldVal(union_byval, field_index, field_ty); |
| 27337 | } |
| 27338 | |
| 27339 | fn elemPtr( |
| 27340 | sema: *Sema, |
| 27341 | block: *Block, |
| 27342 | src: LazySrcLoc, |
| 27343 | indexable_ptr: Air.Inst.Ref, |
| 27344 | elem_index: Air.Inst.Ref, |
| 27345 | elem_index_src: LazySrcLoc, |
| 27346 | init: bool, |
| 27347 | oob_safety: bool, |
| 27348 | ) CompileError!Air.Inst.Ref { |
| 27349 | const pt = sema.pt; |
| 27350 | const zcu = pt.zcu; |
| 27351 | const indexable_ptr_src = src; // TODO better source location |
| 27352 | const indexable_ptr_ty = sema.typeOf(indexable_ptr); |
| 27353 | |
| 27354 | const indexable_ty = switch (indexable_ptr_ty.zigTypeTag(zcu)) { |
| 27355 | .pointer => indexable_ptr_ty.childType(zcu), |
| 27356 | else => return sema.fail(block, indexable_ptr_src, "expected pointer, found '{f}'", .{indexable_ptr_ty.fmt(pt)}), |
| 27357 | }; |
| 27358 | try sema.checkIndexable(block, src, indexable_ty); |
| 27359 | try sema.ensureLayoutResolved(indexable_ty, src, .ptr_access); |
| 27360 | |
| 27361 | const elem_ptr = switch (indexable_ty.zigTypeTag(zcu)) { |
| 27362 | .vector => try sema.elemPtrVector(block, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init), |
| 27363 | .array => try sema.elemPtrArray(block, src, indexable_ptr_src, indexable_ptr, elem_index_src, elem_index, init, oob_safety), |
| 27364 | .@"struct" => try sema.tupleElemPtr(block, src, indexable_ptr, elem_index, elem_index_src), |
| 27365 | .spirv => try sema.elemPtrSpirvRuntimeArray(block, indexable_ptr, elem_index), |
| 27366 | else => { |
| 27367 | const indexable = try sema.analyzeLoad(block, indexable_ptr_src, indexable_ptr, indexable_ptr_src); |
| 27368 | try sema.ensureLayoutResolved(sema.typeOf(indexable).childType(zcu), src, .ptr_access); |
| 27369 | return elemPtrOneLayerOnly(sema, block, src, indexable, elem_index, elem_index_src, init, oob_safety); |
| 27370 | }, |
| 27371 | }; |
| 27372 | |
| 27373 | try sema.checkKnownAllocPtr(block, indexable_ptr, elem_ptr); |
| 27374 | return elem_ptr; |
| 27375 | } |
| 27376 | |
| 27377 | /// Asserts that `indexable` is an indexable pointer whose child type has its layout already resolved. |
| 27378 | fn elemPtrOneLayerOnly( |
| 27379 | sema: *Sema, |
| 27380 | block: *Block, |
| 27381 | src: LazySrcLoc, |
| 27382 | indexable: Air.Inst.Ref, |
| 27383 | elem_index: Air.Inst.Ref, |
| 27384 | elem_index_src: LazySrcLoc, |
| 27385 | init: bool, |
| 27386 | oob_safety: bool, |
| 27387 | ) CompileError!Air.Inst.Ref { |
| 27388 | const indexable_src = src; // TODO better source location |
| 27389 | const indexable_ty = sema.typeOf(indexable); |
| 27390 | const pt = sema.pt; |
| 27391 | const zcu = pt.zcu; |
| 27392 | |
| 27393 | assert(indexable_ty.isIndexable(zcu)); |
| 27394 | assert(indexable_ty.zigTypeTag(zcu) == .pointer); |
| 27395 | const child_ty = indexable_ty.childType(zcu); |
| 27396 | child_ty.assertHasLayout(zcu); |
| 27397 | |
| 27398 | switch (indexable_ty.ptrSize(zcu)) { |
| 27399 | .slice => return sema.elemPtrSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), |
| 27400 | .many, .c => { |
| 27401 | const maybe_ptr_val = try sema.resolveDefinedValue(block, indexable_src, indexable); |
| 27402 | const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); |
| 27403 | const maybe_index: ?u64 = if (maybe_index_val) |val| val.toUnsignedInt(zcu) else null; |
| 27404 | ct: { |
| 27405 | const ptr_val = maybe_ptr_val orelse break :ct; |
| 27406 | const index: usize = @intCast(maybe_index orelse break :ct); |
| 27407 | return .fromValue(try ptr_val.ptrElem(index, pt)); |
| 27408 | } |
| 27409 | |
| 27410 | const result_ty = try indexable_ty.elemPtrType(maybe_index, pt); |
| 27411 | |
| 27412 | try sema.validateRuntimeElemAccess(block, elem_index_src, result_ty, indexable_src); |
| 27413 | try sema.validateRuntimeValue(block, indexable_src, indexable); |
| 27414 | try sema.checkLogicalPtrOperation(block, src, indexable_ty); |
| 27415 | |
| 27416 | if (child_ty.abiSize(zcu) == 0) { |
| 27417 | // zero-bit child type; just bitcast the pointer |
| 27418 | return block.addTyOp(.ptr_cast, result_ty, indexable); |
| 27419 | } |
| 27420 | |
| 27421 | return block.addPtrElemPtr(indexable, elem_index, result_ty); |
| 27422 | }, |
| 27423 | .one => { |
| 27424 | const elem_ptr = switch (child_ty.zigTypeTag(zcu)) { |
| 27425 | .vector => try sema.elemPtrVector(block, indexable_src, indexable, elem_index_src, elem_index, init), |
| 27426 | .array => try sema.elemPtrArray(block, src, indexable_src, indexable, elem_index_src, elem_index, init, oob_safety), |
| 27427 | .@"struct" => try sema.tupleElemPtr(block, indexable_src, indexable, elem_index, elem_index_src), |
| 27428 | .spirv => try sema.elemPtrSpirvRuntimeArray(block, indexable, elem_index), |
| 27429 | else => unreachable, // Guaranteed by checkIndexable |
| 27430 | }; |
| 27431 | try sema.checkKnownAllocPtr(block, indexable, elem_ptr); |
| 27432 | return elem_ptr; |
| 27433 | }, |
| 27434 | } |
| 27435 | } |
| 27436 | |
| 27437 | fn elemVal( |
| 27438 | sema: *Sema, |
| 27439 | block: *Block, |
| 27440 | src: LazySrcLoc, |
| 27441 | indexable: Air.Inst.Ref, |
| 27442 | elem_index_uncasted: Air.Inst.Ref, |
| 27443 | elem_index_src: LazySrcLoc, |
| 27444 | oob_safety: bool, |
| 27445 | ) CompileError!Air.Inst.Ref { |
| 27446 | const indexable_src = src; // TODO better source location |
| 27447 | const indexable_ty = sema.typeOf(indexable); |
| 27448 | const pt = sema.pt; |
| 27449 | const zcu = pt.zcu; |
| 27450 | |
| 27451 | try sema.checkIndexable(block, src, indexable_ty); |
| 27452 | |
| 27453 | // TODO in case of a vector of pointers, we need to detect whether the element |
| 27454 | // index is a scalar or vector instead of unconditionally casting to usize. |
| 27455 | const elem_index = try sema.coerce(block, .usize, elem_index_uncasted, elem_index_src); |
| 27456 | |
| 27457 | switch (indexable_ty.zigTypeTag(zcu)) { |
| 27458 | .pointer => { |
| 27459 | const child_ty = indexable_ty.childType(zcu); |
| 27460 | try sema.ensureLayoutResolved(child_ty, src, .ptr_access); |
| 27461 | switch (indexable_ty.ptrSize(zcu)) { |
| 27462 | .slice => return sema.elemValSlice(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), |
| 27463 | .many, .c => { |
| 27464 | const maybe_indexable_val = try sema.resolveDefinedValue(block, indexable_src, indexable); |
| 27465 | const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); |
| 27466 | |
| 27467 | ct: { |
| 27468 | const indexable_val = maybe_indexable_val orelse break :ct; |
| 27469 | const index_val = maybe_index_val orelse break :ct; |
| 27470 | const index: usize = @intCast(index_val.toUnsignedInt(zcu)); |
| 27471 | const many_ptr_ty = try pt.manyConstPtrType(child_ty); |
| 27472 | const many_ptr_val = try pt.getCoerced(indexable_val, many_ptr_ty); |
| 27473 | const elem_ptr_val = try many_ptr_val.ptrElem(index, pt); |
| 27474 | return sema.analyzeLoad(block, src, .fromValue(elem_ptr_val), indexable_src); |
| 27475 | } |
| 27476 | |
| 27477 | try sema.validateRuntimeElemAccess(block, elem_index_src, child_ty, src); |
| 27478 | switch (child_ty.classify(zcu)) { |
| 27479 | .runtime => {}, |
| 27480 | .one_possible_value => return .fromValue((try child_ty.onePossibleValue(pt)).?), |
| 27481 | .no_possible_value => switch (child_ty.zigTypeTag(zcu)) { |
| 27482 | .@"opaque" => return sema.fail(block, src, "cannot load opaque type '{f}'", .{child_ty.fmt(pt)}), |
| 27483 | else => return sema.fail(block, src, "cannot load uninstantiable type '{f}'", .{child_ty.fmt(pt)}), |
| 27484 | }, |
| 27485 | .partially_comptime, .fully_comptime => unreachable, // caught by `validateRuntimeElemAccess` |
| 27486 | } |
| 27487 | try sema.checkLogicalPtrOperation(block, src, indexable_ty); |
| 27488 | |
| 27489 | return block.addBinOp(.ptr_elem_val, indexable, elem_index); |
| 27490 | }, |
| 27491 | .one => { |
| 27492 | arr_sent: { |
| 27493 | if (child_ty.zigTypeTag(zcu) != .array) break :arr_sent; |
| 27494 | const sentinel = child_ty.sentinel(zcu) orelse break :arr_sent; |
| 27495 | const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse break :arr_sent; |
| 27496 | const index = try sema.usizeCast(block, src, index_val.toUnsignedInt(zcu)); |
| 27497 | if (index != child_ty.arrayLen(zcu)) break :arr_sent; |
| 27498 | return .fromValue(sentinel); |
| 27499 | } |
| 27500 | const elem_ptr = try sema.elemPtr(block, indexable_src, indexable, elem_index, elem_index_src, false, oob_safety); |
| 27501 | return sema.analyzeLoad(block, indexable_src, elem_ptr, elem_index_src); |
| 27502 | }, |
| 27503 | } |
| 27504 | }, |
| 27505 | .array => return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety), |
| 27506 | .vector => { |
| 27507 | // TODO: If the index is a vector, the result should be a vector. |
| 27508 | return sema.elemValArray(block, src, indexable_src, indexable, elem_index_src, elem_index, oob_safety); |
| 27509 | }, |
| 27510 | .@"struct" => { |
| 27511 | // Tuple field access. |
| 27512 | const index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index }); |
| 27513 | const index: u32 = @intCast(index_val.toUnsignedInt(zcu)); |
| 27514 | return sema.tupleField(block, indexable_src, indexable, elem_index_src, index); |
| 27515 | }, |
| 27516 | else => unreachable, |
| 27517 | } |
| 27518 | } |
| 27519 | |
| 27520 | /// Called when the index or indexable is runtime known. |
| 27521 | /// Asserts that the layout of `elem_ty` is already resolved. |
| 27522 | fn validateRuntimeElemAccess( |
| 27523 | sema: *Sema, |
| 27524 | block: *Block, |
| 27525 | elem_index_src: LazySrcLoc, |
| 27526 | elem_ty: Type, |
| 27527 | parent_src: LazySrcLoc, |
| 27528 | ) CompileError!void { |
| 27529 | const zcu = sema.pt.zcu; |
| 27530 | |
| 27531 | if (elem_ty.comptimeOnly(zcu)) { |
| 27532 | const msg = msg: { |
| 27533 | const msg = try sema.errMsg( |
| 27534 | elem_index_src, |
| 27535 | "values of type '{f}' must be comptime-known, but index value is runtime-known", |
| 27536 | .{elem_ty.fmt(sema.pt)}, |
| 27537 | ); |
| 27538 | errdefer msg.destroy(sema.gpa); |
| 27539 | |
| 27540 | try sema.explainWhyTypeIsComptime(msg, parent_src, elem_ty); |
| 27541 | |
| 27542 | break :msg msg; |
| 27543 | }; |
| 27544 | return sema.failWithOwnedErrorMsg(block, msg); |
| 27545 | } |
| 27546 | } |
| 27547 | |
| 27548 | /// Validates `elem_index`, and returns a pointer to that field using `structFieldPtrByIndex`. |
| 27549 | /// |
| 27550 | /// Asserts that the type of `tuple_ptr` is a single-item pointer whose child type is a tuple. |
| 27551 | fn tupleElemPtr( |
| 27552 | sema: *Sema, |
| 27553 | block: *Block, |
| 27554 | src: LazySrcLoc, |
| 27555 | tuple_ptr: Air.Inst.Ref, |
| 27556 | elem_index: Air.Inst.Ref, |
| 27557 | elem_index_src: LazySrcLoc, |
| 27558 | ) CompileError!Air.Inst.Ref { |
| 27559 | const pt = sema.pt; |
| 27560 | const zcu = pt.zcu; |
| 27561 | const tuple_ptr_ty = sema.typeOf(tuple_ptr); |
| 27562 | assert(tuple_ptr_ty.isSinglePointer(zcu)); |
| 27563 | const tuple_ty = tuple_ptr_ty.childType(zcu); |
| 27564 | assert(tuple_ty.isTuple(zcu)); |
| 27565 | |
| 27566 | const field_count = tuple_ty.structFieldCount(zcu); |
| 27567 | if (field_count == 0) { |
| 27568 | return sema.fail(block, src, "indexing into empty tuple is not allowed", .{}); |
| 27569 | } |
| 27570 | |
| 27571 | const elem_index_val = try sema.resolveConstDefinedValue(block, elem_index_src, elem_index, .{ .simple = .tuple_field_index }); |
| 27572 | const index = elem_index_val.getUnsignedInt(zcu); |
| 27573 | if (index == null or index.? >= field_count) { |
| 27574 | return sema.fail(block, elem_index_src, "index '{f}' out of bounds of tuple '{f}'", .{ |
| 27575 | elem_index_val.fmtValueSema(pt, sema), tuple_ty.fmt(pt), |
| 27576 | }); |
| 27577 | } |
| 27578 | |
| 27579 | return sema.structFieldPtrByIndex(block, src, tuple_ptr, @intCast(index.?), tuple_ty); |
| 27580 | } |
| 27581 | |
| 27582 | fn tupleField( |
| 27583 | sema: *Sema, |
| 27584 | block: *Block, |
| 27585 | tuple_src: LazySrcLoc, |
| 27586 | tuple: Air.Inst.Ref, |
| 27587 | field_index_src: LazySrcLoc, |
| 27588 | field_index: u32, |
| 27589 | ) CompileError!Air.Inst.Ref { |
| 27590 | const pt = sema.pt; |
| 27591 | const zcu = pt.zcu; |
| 27592 | const tuple_ty = sema.typeOf(tuple); |
| 27593 | const field_count = tuple_ty.structFieldCount(zcu); |
| 27594 | |
| 27595 | if (field_count == 0) { |
| 27596 | return sema.fail(block, tuple_src, "indexing into empty tuple is not allowed", .{}); |
| 27597 | } |
| 27598 | |
| 27599 | if (field_index >= field_count) { |
| 27600 | return sema.fail(block, field_index_src, "index {d} outside tuple of length {d}", .{ |
| 27601 | field_index, field_count, |
| 27602 | }); |
| 27603 | } |
| 27604 | |
| 27605 | const field_ty = tuple_ty.fieldType(field_index, zcu); |
| 27606 | |
| 27607 | if (try tuple_ty.structFieldValueComptime(pt, field_index)) |default_value| { |
| 27608 | return Air.internedToRef(default_value.toIntern()); // comptime field |
| 27609 | } |
| 27610 | |
| 27611 | if (sema.resolveValue(tuple)) |tuple_val| { |
| 27612 | if (tuple_val.isUndef(zcu)) return pt.undefRef(field_ty); |
| 27613 | return Air.internedToRef((try tuple_val.fieldValue(pt, field_index)).toIntern()); |
| 27614 | } |
| 27615 | |
| 27616 | try sema.validateRuntimeElemAccess(block, field_index_src, field_ty, tuple_src); |
| 27617 | |
| 27618 | return block.addStructFieldVal(tuple, field_index, field_ty); |
| 27619 | } |
| 27620 | |
| 27621 | fn elemValArray( |
| 27622 | sema: *Sema, |
| 27623 | block: *Block, |
| 27624 | src: LazySrcLoc, |
| 27625 | array_src: LazySrcLoc, |
| 27626 | array: Air.Inst.Ref, |
| 27627 | elem_index_src: LazySrcLoc, |
| 27628 | elem_index: Air.Inst.Ref, |
| 27629 | oob_safety: bool, |
| 27630 | ) CompileError!Air.Inst.Ref { |
| 27631 | const pt = sema.pt; |
| 27632 | const zcu = pt.zcu; |
| 27633 | const array_ty = sema.typeOf(array); |
| 27634 | const array_sent = array_ty.sentinel(zcu); |
| 27635 | const array_len = array_ty.arrayLen(zcu); |
| 27636 | const array_len_s = array_len + @intFromBool(array_sent != null); |
| 27637 | const elem_ty = array_ty.childType(zcu); |
| 27638 | |
| 27639 | if (array_len_s == 0) { |
| 27640 | return sema.fail(block, array_src, "indexing into empty array is not allowed", .{}); |
| 27641 | } |
| 27642 | |
| 27643 | const maybe_undef_array_val = sema.resolveValue(array); |
| 27644 | // index must be defined since it can access out of bounds |
| 27645 | const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); |
| 27646 | |
| 27647 | if (maybe_index_val) |index_val| { |
| 27648 | const index: usize = @intCast(index_val.toUnsignedInt(zcu)); |
| 27649 | if (array_sent) |s| { |
| 27650 | if (index == array_len) { |
| 27651 | return Air.internedToRef(s.toIntern()); |
| 27652 | } |
| 27653 | } |
| 27654 | if (index >= array_len_s) { |
| 27655 | const sentinel_label: []const u8 = if (array_sent != null) " +1 (sentinel)" else ""; |
| 27656 | return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); |
| 27657 | } |
| 27658 | } |
| 27659 | if (maybe_undef_array_val) |array_val| { |
| 27660 | if (array_val.isUndef(zcu)) { |
| 27661 | return pt.undefRef(elem_ty); |
| 27662 | } |
| 27663 | if (maybe_index_val) |index_val| { |
| 27664 | const index: usize = @intCast(index_val.toUnsignedInt(zcu)); |
| 27665 | return .fromValue(try array_val.elemValue(pt, index)); |
| 27666 | } |
| 27667 | // Since the array is comptime-known, it might be OPV, in which case the index is irrelevant. |
| 27668 | if (try elem_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 27669 | } |
| 27670 | |
| 27671 | try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, array_src); |
| 27672 | try sema.validateRuntimeValue(block, array_src, array); |
| 27673 | |
| 27674 | if (oob_safety and block.wantSafety()) { |
| 27675 | // Runtime check is only needed if unable to comptime check. |
| 27676 | if (maybe_index_val == null) { |
| 27677 | const len_inst = try pt.intRef(.usize, array_len); |
| 27678 | const cmp_op: Air.Inst.Tag = if (array_sent != null) .cmp_lte else .cmp_lt; |
| 27679 | try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); |
| 27680 | } |
| 27681 | } |
| 27682 | |
| 27683 | return block.addBinOp(.array_elem_val, array, elem_index); |
| 27684 | } |
| 27685 | |
| 27686 | fn elemPtrVector( |
| 27687 | sema: *Sema, |
| 27688 | block: *Block, |
| 27689 | vector_ptr_src: LazySrcLoc, |
| 27690 | vector_ptr: Air.Inst.Ref, |
| 27691 | elem_index_src: LazySrcLoc, |
| 27692 | elem_index: Air.Inst.Ref, |
| 27693 | init: bool, |
| 27694 | ) CompileError!Air.Inst.Ref { |
| 27695 | const pt = sema.pt; |
| 27696 | const zcu = pt.zcu; |
| 27697 | const vector_ptr_ty = sema.typeOf(vector_ptr); |
| 27698 | const vector_ty = vector_ptr_ty.childType(zcu); |
| 27699 | assert(vector_ty.zigTypeTag(zcu) == .vector); |
| 27700 | const vector_len = vector_ty.vectorLen(zcu); |
| 27701 | |
| 27702 | if (vector_len == 0) { |
| 27703 | return sema.fail(block, vector_ptr_src, "cannot index into empty vector", .{}); |
| 27704 | } |
| 27705 | |
| 27706 | const maybe_vector_ptr_val = sema.resolveValue(vector_ptr); |
| 27707 | // The index must not be undefined since it can be out of bounds. |
| 27708 | const index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index) orelse { |
| 27709 | return sema.fail(block, elem_index_src, "vector index not comptime known", .{}); |
| 27710 | }; |
| 27711 | const index = index_val.toUnsignedInt(zcu); |
| 27712 | if (index >= vector_len) { |
| 27713 | return sema.fail(block, elem_index_src, "index {d} outside vector of length {d}", .{ index, vector_len }); |
| 27714 | } |
| 27715 | |
| 27716 | const elem_ty = vector_ty.childType(zcu); |
| 27717 | |
| 27718 | const vector_ptr_info = vector_ptr_ty.ptrInfo(zcu); |
| 27719 | const elem_ptr_ty = try pt.ptrType(.{ |
| 27720 | .child = elem_ty.toIntern(), |
| 27721 | .flags = .{ |
| 27722 | .size = .one, |
| 27723 | .alignment = vector_ptr_info.flags.alignment, |
| 27724 | .is_const = vector_ptr_info.flags.is_const, |
| 27725 | .is_volatile = vector_ptr_info.flags.is_volatile, |
| 27726 | .is_allowzero = vector_ptr_info.flags.is_allowzero, |
| 27727 | .address_space = vector_ptr_info.flags.address_space, |
| 27728 | .vector_index = @fromBackingInt(@intCast(index)), |
| 27729 | }, |
| 27730 | .packed_offset = .{ |
| 27731 | .host_size = @intCast(vector_len), |
| 27732 | .bit_offset = 0, |
| 27733 | }, |
| 27734 | }); |
| 27735 | |
| 27736 | if (maybe_vector_ptr_val) |ptr_val| { |
| 27737 | if (ptr_val.isUndef(zcu)) return pt.undefRef(elem_ptr_ty); |
| 27738 | return .fromValue(try pt.getCoerced(ptr_val, elem_ptr_ty)); |
| 27739 | } |
| 27740 | |
| 27741 | if (!init) { |
| 27742 | try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, vector_ptr_src); |
| 27743 | try sema.validateRuntimeValue(block, vector_ptr_src, vector_ptr); |
| 27744 | } |
| 27745 | |
| 27746 | return block.addTyOp(.ptr_cast, elem_ptr_ty, vector_ptr); |
| 27747 | } |
| 27748 | |
| 27749 | fn elemPtrSpirvRuntimeArray( |
| 27750 | sema: *Sema, |
| 27751 | block: *Block, |
| 27752 | array_ptr: Air.Inst.Ref, |
| 27753 | elem_index: Air.Inst.Ref, |
| 27754 | ) CompileError!Air.Inst.Ref { |
| 27755 | const pt = sema.pt; |
| 27756 | const zcu = pt.zcu; |
| 27757 | const array_ptr_ty = sema.typeOf(array_ptr); |
| 27758 | assert(array_ptr_ty.ptrSize(zcu) == .one); |
| 27759 | const array_ty = array_ptr_ty.childType(zcu); |
| 27760 | assert(array_ty.isSpirvRuntimeArray(zcu)); |
| 27761 | const elem_ptr_ty = try array_ptr_ty.elemPtrType(null, pt); |
| 27762 | return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty); |
| 27763 | } |
| 27764 | |
| 27765 | /// Asserts that the layout of the array is already resolved. |
| 27766 | fn elemPtrArray( |
| 27767 | sema: *Sema, |
| 27768 | block: *Block, |
| 27769 | src: LazySrcLoc, |
| 27770 | array_ptr_src: LazySrcLoc, |
| 27771 | array_ptr: Air.Inst.Ref, |
| 27772 | elem_index_src: LazySrcLoc, |
| 27773 | elem_index: Air.Inst.Ref, |
| 27774 | init: bool, |
| 27775 | oob_safety: bool, |
| 27776 | ) CompileError!Air.Inst.Ref { |
| 27777 | const pt = sema.pt; |
| 27778 | const zcu = pt.zcu; |
| 27779 | const array_ptr_ty = sema.typeOf(array_ptr); |
| 27780 | assert(array_ptr_ty.ptrSize(zcu) == .one); |
| 27781 | const array_ty = array_ptr_ty.childType(zcu); |
| 27782 | assert(array_ty.zigTypeTag(zcu) == .array); |
| 27783 | const array_sent = array_ty.sentinel(zcu) != null; |
| 27784 | const array_len = array_ty.arrayLen(zcu); |
| 27785 | const array_len_s = array_len + @intFromBool(array_sent); |
| 27786 | |
| 27787 | if (array_len_s == 0) { |
| 27788 | return sema.fail(block, array_ptr_src, "cannot index into empty array", .{}); |
| 27789 | } |
| 27790 | |
| 27791 | const maybe_undef_array_ptr_val = sema.resolveValue(array_ptr); |
| 27792 | // The index must not be undefined since it can be out of bounds. |
| 27793 | const maybe_index: ?u64 = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { |
| 27794 | const index = index_val.toUnsignedInt(zcu); |
| 27795 | if (index >= array_len_s) { |
| 27796 | const sentinel_label: []const u8 = if (array_sent) " +1 (sentinel)" else ""; |
| 27797 | return sema.fail(block, elem_index_src, "index {d} outside array of length {d}{s}", .{ index, array_len, sentinel_label }); |
| 27798 | } |
| 27799 | break :o index; |
| 27800 | } else null; |
| 27801 | |
| 27802 | array_ty.assertHasLayout(zcu); |
| 27803 | const elem_ptr_ty = try array_ptr_ty.elemPtrType(maybe_index, pt); |
| 27804 | |
| 27805 | if (maybe_undef_array_ptr_val) |array_ptr_val| { |
| 27806 | if (array_ptr_val.isUndef(zcu)) { |
| 27807 | return pt.undefRef(elem_ptr_ty); |
| 27808 | } |
| 27809 | if (maybe_index) |index| { |
| 27810 | return .fromValue(try array_ptr_val.ptrElem(index, pt)); |
| 27811 | } |
| 27812 | } |
| 27813 | |
| 27814 | if (!init) { |
| 27815 | try sema.validateRuntimeElemAccess(block, elem_index_src, array_ty.childType(zcu), array_ptr_src); |
| 27816 | try sema.validateRuntimeValue(block, array_ptr_src, array_ptr); |
| 27817 | } |
| 27818 | |
| 27819 | // Runtime check is only needed if unable to comptime check. |
| 27820 | if (oob_safety and block.wantSafety() and maybe_index == null) { |
| 27821 | const len_inst = try pt.intRef(.usize, array_len); |
| 27822 | const cmp_op: Air.Inst.Tag = if (array_sent) .cmp_lte else .cmp_lt; |
| 27823 | try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); |
| 27824 | } |
| 27825 | |
| 27826 | if (array_ty.childType(zcu).abiSize(zcu) == 0) { |
| 27827 | // zero-bit child type; just bitcast the pointer |
| 27828 | return block.addTyOp(.ptr_cast, elem_ptr_ty, array_ptr); |
| 27829 | } |
| 27830 | |
| 27831 | return block.addPtrElemPtr(array_ptr, elem_index, elem_ptr_ty); |
| 27832 | } |
| 27833 | |
| 27834 | /// Asserts that the layout of the slice element type is already resolved. |
| 27835 | fn elemValSlice( |
| 27836 | sema: *Sema, |
| 27837 | block: *Block, |
| 27838 | src: LazySrcLoc, |
| 27839 | slice_src: LazySrcLoc, |
| 27840 | slice: Air.Inst.Ref, |
| 27841 | elem_index_src: LazySrcLoc, |
| 27842 | elem_index: Air.Inst.Ref, |
| 27843 | oob_safety: bool, |
| 27844 | ) CompileError!Air.Inst.Ref { |
| 27845 | const pt = sema.pt; |
| 27846 | const zcu = pt.zcu; |
| 27847 | const slice_ty = sema.typeOf(slice); |
| 27848 | assert(slice_ty.isSlice(zcu)); |
| 27849 | const slice_sent = slice_ty.sentinel(zcu) != null; |
| 27850 | const elem_ty = slice_ty.childType(zcu); |
| 27851 | |
| 27852 | elem_ty.assertHasLayout(zcu); |
| 27853 | |
| 27854 | // slice must be defined since it can dereferenced as null |
| 27855 | const maybe_slice_val = try sema.resolveDefinedValue(block, slice_src, slice); |
| 27856 | // index must be defined since it can index out of bounds |
| 27857 | const maybe_index_val = try sema.resolveDefinedValue(block, elem_index_src, elem_index); |
| 27858 | |
| 27859 | if (maybe_slice_val) |slice_val| { |
| 27860 | const slice_len = slice_val.sliceLen(zcu); |
| 27861 | const slice_len_s = slice_len + @intFromBool(slice_sent); |
| 27862 | if (slice_len_s == 0) { |
| 27863 | return sema.fail(block, slice_src, "indexing into empty slice is not allowed", .{}); |
| 27864 | } |
| 27865 | if (maybe_index_val) |index_val| { |
| 27866 | const index: usize = @intCast(index_val.toUnsignedInt(zcu)); |
| 27867 | if (index >= slice_len_s) { |
| 27868 | const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; |
| 27869 | return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); |
| 27870 | } |
| 27871 | const elem_ptr_val = try slice_val.ptrElem(index, pt); |
| 27872 | return sema.analyzeLoad(block, src, .fromValue(elem_ptr_val), slice_src); |
| 27873 | } |
| 27874 | } |
| 27875 | |
| 27876 | if (try elem_ty.onePossibleValue(pt)) |opv| return .fromValue(opv); |
| 27877 | |
| 27878 | try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ty, slice_src); |
| 27879 | try sema.validateRuntimeValue(block, slice_src, slice); |
| 27880 | |
| 27881 | if (oob_safety and block.wantSafety()) { |
| 27882 | const len_inst = if (maybe_slice_val) |slice_val| |
| 27883 | try pt.intRef(.usize, slice_val.sliceLen(zcu)) |
| 27884 | else |
| 27885 | try block.addTyOp(.slice_len, .usize, slice); |
| 27886 | const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; |
| 27887 | try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); |
| 27888 | } |
| 27889 | try sema.checkLogicalPtrOperation(block, src, slice_ty); |
| 27890 | return block.addBinOp(.slice_elem_val, slice, elem_index); |
| 27891 | } |
| 27892 | |
| 27893 | /// Asserts that the layout of the slice element type is already resolved. |
| 27894 | fn elemPtrSlice( |
| 27895 | sema: *Sema, |
| 27896 | block: *Block, |
| 27897 | src: LazySrcLoc, |
| 27898 | slice_src: LazySrcLoc, |
| 27899 | slice: Air.Inst.Ref, |
| 27900 | elem_index_src: LazySrcLoc, |
| 27901 | elem_index: Air.Inst.Ref, |
| 27902 | oob_safety: bool, |
| 27903 | ) CompileError!Air.Inst.Ref { |
| 27904 | const pt = sema.pt; |
| 27905 | const zcu = pt.zcu; |
| 27906 | const slice_ty = sema.typeOf(slice); |
| 27907 | assert(slice_ty.isSlice(zcu)); |
| 27908 | const slice_sent = slice_ty.sentinel(zcu) != null; |
| 27909 | const elem_ty = slice_ty.childType(zcu); |
| 27910 | elem_ty.assertHasLayout(zcu); |
| 27911 | |
| 27912 | const maybe_undef_slice_val = sema.resolveValue(slice); |
| 27913 | // The index must not be undefined since it can be out of bounds. |
| 27914 | const offset: ?u64 = if (try sema.resolveDefinedValue(block, elem_index_src, elem_index)) |index_val| o: { |
| 27915 | break :o index_val.toUnsignedInt(zcu); |
| 27916 | } else null; |
| 27917 | |
| 27918 | const elem_ptr_ty = try slice_ty.elemPtrType(offset, pt); |
| 27919 | assert(elem_ptr_ty.childType(zcu).toIntern() == elem_ty.toIntern()); |
| 27920 | |
| 27921 | if (maybe_undef_slice_val) |slice_val| { |
| 27922 | if (slice_val.isUndef(zcu)) { |
| 27923 | return pt.undefRef(elem_ptr_ty); |
| 27924 | } |
| 27925 | const slice_len = slice_val.sliceLen(zcu); |
| 27926 | const slice_len_s = slice_len + @intFromBool(slice_sent); |
| 27927 | if (slice_len_s == 0) { |
| 27928 | return sema.fail(block, slice_src, "cannot index into empty slice", .{}); |
| 27929 | } |
| 27930 | if (offset) |index| { |
| 27931 | if (index >= slice_len_s) { |
| 27932 | const sentinel_label: []const u8 = if (slice_sent) " +1 (sentinel)" else ""; |
| 27933 | return sema.fail(block, elem_index_src, "index {d} outside slice of length {d}{s}", .{ index, slice_len, sentinel_label }); |
| 27934 | } |
| 27935 | return .fromValue(try slice_val.ptrElem(index, pt)); |
| 27936 | } |
| 27937 | } |
| 27938 | |
| 27939 | try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_src); |
| 27940 | try sema.validateRuntimeValue(block, slice_src, slice); |
| 27941 | try sema.checkLogicalPtrOperation(block, src, slice_ty); |
| 27942 | |
| 27943 | if (oob_safety and block.wantSafety()) { |
| 27944 | const len_inst = len: { |
| 27945 | if (maybe_undef_slice_val) |slice_val| |
| 27946 | if (!slice_val.isUndef(zcu)) |
| 27947 | break :len try pt.intRef(.usize, slice_val.sliceLen(zcu)); |
| 27948 | break :len try block.addTyOp(.slice_len, .usize, slice); |
| 27949 | }; |
| 27950 | const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; |
| 27951 | try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); |
| 27952 | } |
| 27953 | if (elem_ty.abiSize(zcu) == 0) { |
| 27954 | // zero-bit child type; just extract the pointer and bitcast it |
| 27955 | const slice_ptr = try block.addTyOp(.slice_ptr, slice_ty.slicePtrFieldType(zcu), slice); |
| 27956 | return block.addTyOp(.ptr_cast, elem_ptr_ty, slice_ptr); |
| 27957 | } |
| 27958 | return block.addSliceElemPtr(slice, elem_index, elem_ptr_ty); |
| 27959 | } |
| 27960 | |
| 27961 | pub fn coerce( |
| 27962 | sema: *Sema, |
| 27963 | block: *Block, |
| 27964 | dest_ty_unresolved: Type, |
| 27965 | inst: Air.Inst.Ref, |
| 27966 | inst_src: LazySrcLoc, |
| 27967 | ) CompileError!Air.Inst.Ref { |
| 27968 | return sema.coerceExtra(block, dest_ty_unresolved, inst, inst_src, .{}) catch |err| switch (err) { |
| 27969 | error.NotCoercible => unreachable, |
| 27970 | else => |e| return e, |
| 27971 | }; |
| 27972 | } |
| 27973 | |
| 27974 | const CoercionError = CompileError || error{ |
| 27975 | /// When coerce is called recursively, this error should be returned instead of using `fail` |
| 27976 | /// to ensure correct types in compile errors. |
| 27977 | NotCoercible, |
| 27978 | }; |
| 27979 | |
| 27980 | const CoerceOpts = struct { |
| 27981 | /// Should coerceExtra emit error messages. |
| 27982 | report_err: bool = true, |
| 27983 | /// Ignored if `report_err == false`. |
| 27984 | is_ret: bool = false, |
| 27985 | /// Should coercion to comptime_int emit an error message. |
| 27986 | no_cast_to_comptime_int: bool = false, |
| 27987 | |
| 27988 | param_src: struct { |
| 27989 | func_inst: Air.Inst.Ref = .none, |
| 27990 | param_i: u32 = undefined, |
| 27991 | |
| 27992 | fn get(info: @This(), sema: *Sema) !?LazySrcLoc { |
| 27993 | if (info.func_inst == .none) return null; |
| 27994 | const func_inst = try sema.funcDeclSrcInst(info.func_inst) orelse return null; |
| 27995 | return .{ |
| 27996 | .base_node_inst = func_inst, |
| 27997 | .offset = .{ .fn_proto_param_type = .{ |
| 27998 | .fn_proto_node_offset = .zero, |
| 27999 | .param_index = info.param_i, |
| 28000 | } }, |
| 28001 | }; |
| 28002 | } |
| 28003 | } = .{ .func_inst = .none, .param_i = undefined }, |
| 28004 | }; |
| 28005 | |
| 28006 | fn coerceExtra( |
| 28007 | sema: *Sema, |
| 28008 | block: *Block, |
| 28009 | dest_ty: Type, |
| 28010 | inst: Air.Inst.Ref, |
| 28011 | inst_src: LazySrcLoc, |
| 28012 | opts: CoerceOpts, |
| 28013 | ) CoercionError!Air.Inst.Ref { |
| 28014 | const pt = sema.pt; |
| 28015 | const zcu = pt.zcu; |
| 28016 | const comp = zcu.comp; |
| 28017 | const gpa = comp.gpa; |
| 28018 | const io = comp.io; |
| 28019 | const ip = &zcu.intern_pool; |
| 28020 | |
| 28021 | if (dest_ty.isGenericPoison()) return inst; |
| 28022 | |
| 28023 | const dest_ty_src = inst_src; // TODO better source location |
| 28024 | const inst_ty = sema.typeOf(inst); |
| 28025 | const target = zcu.getTarget(); |
| 28026 | |
| 28027 | inst_ty.assertHasLayout(zcu); |
| 28028 | try sema.ensureLayoutResolved(dest_ty, inst_src, .coerce); |
| 28029 | |
| 28030 | // If the types are the same, we can return the operand. |
| 28031 | if (dest_ty.eql(inst_ty)) |
| 28032 | return inst; |
| 28033 | |
| 28034 | const maybe_inst_val = sema.resolveValue(inst); |
| 28035 | |
| 28036 | var in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val); |
| 28037 | if (in_memory_result == .ok) { |
| 28038 | if (maybe_inst_val) |val| { |
| 28039 | return .fromValue(try pt.getCoerced(val, dest_ty)); |
| 28040 | } |
| 28041 | const coerced: Air.Inst.Ref = switch (in_memory_result.ok) { |
| 28042 | .none => coerced: { |
| 28043 | const @"addrspace" = target_util.defaultAddressSpace(zcu.getTarget(), .local); |
| 28044 | const src_ptr_ty = try pt.ptrType(.{ |
| 28045 | .child = inst_ty.toIntern(), |
| 28046 | .flags = .{ .size = .one, .address_space = @"addrspace" }, |
| 28047 | }); |
| 28048 | const dest_ptr_ty = try pt.ptrType(.{ |
| 28049 | .child = dest_ty.toIntern(), |
| 28050 | .flags = .{ .size = .one, .address_space = @"addrspace" }, |
| 28051 | }); |
| 28052 | const ptr = try block.addTy(.alloc, src_ptr_ty); |
| 28053 | _ = try block.addBinOp(.store_safe, ptr, inst); |
| 28054 | const casted_ptr = try block.addTyOp(.ptr_cast, dest_ptr_ty, ptr); |
| 28055 | break :coerced try block.addTyOp(.load, dest_ty, casted_ptr); |
| 28056 | }, |
| 28057 | .same_type => unreachable, // we checked for equal types just above |
| 28058 | .bit_cast => try block.addTyOp(.bit_cast, dest_ty, inst), |
| 28059 | .ptr_cast => try block.addTyOp(.ptr_cast, dest_ty, inst), |
| 28060 | .error_cast => try block.addTyOp(.error_cast, dest_ty, inst), |
| 28061 | }; |
| 28062 | try sema.checkKnownAllocPtr(block, inst, coerced); |
| 28063 | return coerced; |
| 28064 | } |
| 28065 | |
| 28066 | switch (dest_ty.zigTypeTag(zcu)) { |
| 28067 | .optional => optional: { |
| 28068 | if (maybe_inst_val) |val| { |
| 28069 | // undefined sets the optional bit also to undefined. |
| 28070 | if (val.toIntern() == .undef) { |
| 28071 | return .fromValue(try dest_ty.onePossibleValue(pt) orelse try pt.undefValue(dest_ty)); |
| 28072 | } |
| 28073 | |
| 28074 | // null to ?T |
| 28075 | if (val.toIntern() == .null_value) { |
| 28076 | return Air.internedToRef((try pt.intern(.{ .opt = .{ |
| 28077 | .ty = dest_ty.toIntern(), |
| 28078 | .val = .none, |
| 28079 | } }))); |
| 28080 | } |
| 28081 | } |
| 28082 | |
| 28083 | // cast from ?*T and ?[*]T to ?*anyopaque |
| 28084 | // but don't do it if the source type is a double pointer |
| 28085 | if (dest_ty.isPtrLikeOptional(zcu) and |
| 28086 | dest_ty.nullablePtrElem(zcu).toIntern() == .anyopaque_type and |
| 28087 | inst_ty.isPtrAtRuntime(zcu)) |
| 28088 | anyopaque_check: { |
| 28089 | if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :optional; |
| 28090 | const elem_ty = inst_ty.nullablePtrElem(zcu); |
| 28091 | if (elem_ty.zigTypeTag(zcu) == .pointer or elem_ty.isPtrLikeOptional(zcu)) { |
| 28092 | in_memory_result = .{ .double_ptr_to_anyopaque = .{ |
| 28093 | .actual = inst_ty, |
| 28094 | .wanted = dest_ty, |
| 28095 | } }; |
| 28096 | break :optional; |
| 28097 | } |
| 28098 | // Let the logic below handle wrapping the optional now that |
| 28099 | // it has been checked to correctly coerce. |
| 28100 | if (!inst_ty.isPtrLikeOptional(zcu)) break :anyopaque_check; |
| 28101 | return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); |
| 28102 | } |
| 28103 | |
| 28104 | // T to ?T |
| 28105 | const child_type = dest_ty.optionalChild(zcu); |
| 28106 | const intermediate = sema.coerceExtra(block, child_type, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { |
| 28107 | error.NotCoercible => { |
| 28108 | if (in_memory_result == .no_match) { |
| 28109 | // Try to give more useful notes |
| 28110 | in_memory_result = try sema.coerceInMemoryAllowed(block, child_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val); |
| 28111 | } |
| 28112 | break :optional; |
| 28113 | }, |
| 28114 | else => |e| return e, |
| 28115 | }; |
| 28116 | return try sema.wrapOptional(block, dest_ty, intermediate, inst_src); |
| 28117 | }, |
| 28118 | .pointer => pointer: { |
| 28119 | const dest_info = dest_ty.ptrInfo(zcu); |
| 28120 | |
| 28121 | // Function body to function pointer. |
| 28122 | if (inst_ty.zigTypeTag(zcu) == .@"fn") { |
| 28123 | const fn_val = sema.resolveValue(inst).?; |
| 28124 | const fn_nav = switch (zcu.intern_pool.indexToKey(fn_val.toIntern())) { |
| 28125 | .func => |f| f.owner_nav, |
| 28126 | .@"extern" => |e| e.owner_nav, |
| 28127 | else => unreachable, |
| 28128 | }; |
| 28129 | const inst_as_ptr = try sema.analyzeNavRef(block, inst_src, fn_nav); |
| 28130 | return sema.coerce(block, dest_ty, inst_as_ptr, inst_src); |
| 28131 | } |
| 28132 | |
| 28133 | // *T to *[1]T |
| 28134 | single_item: { |
| 28135 | if (dest_info.flags.size != .one) break :single_item; |
| 28136 | if (!inst_ty.isSinglePointer(zcu)) break :single_item; |
| 28137 | if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; |
| 28138 | const ptr_elem_ty = inst_ty.childType(zcu); |
| 28139 | const array_ty: Type = .fromInterned(dest_info.child); |
| 28140 | if (array_ty.zigTypeTag(zcu) != .array) break :single_item; |
| 28141 | const array_elem_ty = array_ty.childType(zcu); |
| 28142 | if (array_ty.arrayLen(zcu) != 1) break :single_item; |
| 28143 | const dest_is_mut = !dest_info.flags.is_const; |
| 28144 | switch (try sema.coerceInMemoryAllowed(block, array_elem_ty, ptr_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, null)) { |
| 28145 | .ok => {}, |
| 28146 | else => break :single_item, |
| 28147 | } |
| 28148 | return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); |
| 28149 | } |
| 28150 | |
| 28151 | // Coercions where the source is a single pointer to an array. |
| 28152 | src_array_ptr: { |
| 28153 | if (!inst_ty.isSinglePointer(zcu)) break :src_array_ptr; |
| 28154 | if (dest_info.flags.size == .one) break :src_array_ptr; // `*[n]T` -> `*T` isn't valid |
| 28155 | if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; |
| 28156 | const array_ty = inst_ty.childType(zcu); |
| 28157 | if (array_ty.zigTypeTag(zcu) != .array) break :src_array_ptr; |
| 28158 | const array_elem_type = array_ty.childType(zcu); |
| 28159 | const dest_is_mut = !dest_info.flags.is_const; |
| 28160 | |
| 28161 | const dst_elem_type: Type = .fromInterned(dest_info.child); |
| 28162 | const elem_res = try sema.coerceInMemoryAllowed(block, dst_elem_type, array_elem_type, dest_is_mut, target, dest_ty_src, inst_src, null); |
| 28163 | switch (elem_res) { |
| 28164 | .ok => {}, |
| 28165 | else => { |
| 28166 | in_memory_result = .{ .ptr_child = .{ |
| 28167 | .child = try elem_res.dupe(sema.arena), |
| 28168 | .actual = array_elem_type, |
| 28169 | .wanted = dst_elem_type, |
| 28170 | } }; |
| 28171 | break :src_array_ptr; |
| 28172 | }, |
| 28173 | } |
| 28174 | |
| 28175 | if (dest_info.sentinel != .none) { |
| 28176 | if (array_ty.sentinel(zcu)) |inst_sent| { |
| 28177 | if (dest_info.sentinel != |
| 28178 | (try pt.getCoerced(inst_sent, dst_elem_type)).toIntern()) |
| 28179 | { |
| 28180 | in_memory_result = .{ .ptr_sentinel = .{ |
| 28181 | .actual = inst_sent, |
| 28182 | .wanted = Value.fromInterned(dest_info.sentinel), |
| 28183 | .ty = dst_elem_type, |
| 28184 | } }; |
| 28185 | break :src_array_ptr; |
| 28186 | } |
| 28187 | } else { |
| 28188 | in_memory_result = .{ .ptr_sentinel = .{ |
| 28189 | .actual = Value.@"unreachable", |
| 28190 | .wanted = Value.fromInterned(dest_info.sentinel), |
| 28191 | .ty = dst_elem_type, |
| 28192 | } }; |
| 28193 | break :src_array_ptr; |
| 28194 | } |
| 28195 | } |
| 28196 | |
| 28197 | switch (dest_info.flags.size) { |
| 28198 | .slice => { |
| 28199 | // *[N]T to []T |
| 28200 | return sema.coerceArrayPtrToSlice(block, dest_ty, inst, inst_src); |
| 28201 | }, |
| 28202 | .c => { |
| 28203 | // *[N]T to [*c]T |
| 28204 | return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); |
| 28205 | }, |
| 28206 | .many => { |
| 28207 | // *[N]T to [*]T |
| 28208 | return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); |
| 28209 | }, |
| 28210 | .one => unreachable, // early exit at top of block |
| 28211 | } |
| 28212 | } |
| 28213 | |
| 28214 | // coercion from C pointer |
| 28215 | if (inst_ty.isCPtr(zcu)) src_c_ptr: { |
| 28216 | if (dest_info.flags.size == .slice) break :src_c_ptr; |
| 28217 | if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :src_c_ptr; |
| 28218 | // In this case we must add a safety check because the C pointer |
| 28219 | // could be null. |
| 28220 | const src_elem_ty = inst_ty.childType(zcu); |
| 28221 | const dest_is_mut = !dest_info.flags.is_const; |
| 28222 | const dst_elem_type: Type = .fromInterned(dest_info.child); |
| 28223 | switch (try sema.coerceInMemoryAllowed(block, dst_elem_type, src_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, null)) { |
| 28224 | .ok => {}, |
| 28225 | else => break :src_c_ptr, |
| 28226 | } |
| 28227 | return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); |
| 28228 | } |
| 28229 | |
| 28230 | // cast from *T and [*]T to *anyopaque |
| 28231 | // but don't do it if the source type is a double pointer |
| 28232 | if (dest_info.child == .anyopaque_type and inst_ty.zigTypeTag(zcu) == .pointer) to_anyopaque: { |
| 28233 | if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :pointer; |
| 28234 | const elem_ty = inst_ty.childType(zcu); |
| 28235 | if (elem_ty.zigTypeTag(zcu) == .pointer or elem_ty.isPtrLikeOptional(zcu)) { |
| 28236 | in_memory_result = .{ .double_ptr_to_anyopaque = .{ |
| 28237 | .actual = inst_ty, |
| 28238 | .wanted = dest_ty, |
| 28239 | } }; |
| 28240 | break :pointer; |
| 28241 | } |
| 28242 | if (dest_ty.isSlice(zcu)) break :to_anyopaque; |
| 28243 | if (inst_ty.isSlice(zcu)) { |
| 28244 | in_memory_result = .{ .slice_to_anyopaque = .{ |
| 28245 | .actual = inst_ty, |
| 28246 | .wanted = dest_ty, |
| 28247 | } }; |
| 28248 | break :pointer; |
| 28249 | } |
| 28250 | return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); |
| 28251 | } |
| 28252 | |
| 28253 | switch (dest_info.flags.size) { |
| 28254 | // coercion to C pointer |
| 28255 | .c => switch (inst_ty.zigTypeTag(zcu)) { |
| 28256 | .null => return Air.internedToRef(try pt.intern(.{ .ptr = .{ |
| 28257 | .ty = dest_ty.toIntern(), |
| 28258 | .base_addr = .int, |
| 28259 | .byte_offset = 0, |
| 28260 | } })), |
| 28261 | .comptime_int => { |
| 28262 | const addr = sema.coerceExtra(block, .usize, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { |
| 28263 | error.NotCoercible => break :pointer, |
| 28264 | else => |e| return e, |
| 28265 | }; |
| 28266 | return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src); |
| 28267 | }, |
| 28268 | .int => { |
| 28269 | const ptr_size_ty: Type = switch (inst_ty.intInfo(zcu).signedness) { |
| 28270 | .signed => .isize, |
| 28271 | .unsigned => .usize, |
| 28272 | }; |
| 28273 | const addr = sema.coerceExtra(block, ptr_size_ty, inst, inst_src, .{ .report_err = false }) catch |err| switch (err) { |
| 28274 | error.NotCoercible => { |
| 28275 | // Try to give more useful notes |
| 28276 | in_memory_result = try sema.coerceInMemoryAllowed(block, ptr_size_ty, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val); |
| 28277 | break :pointer; |
| 28278 | }, |
| 28279 | else => |e| return e, |
| 28280 | }; |
| 28281 | return try sema.coerceCompatiblePtrs(block, dest_ty, addr, inst_src); |
| 28282 | }, |
| 28283 | .pointer => p: { |
| 28284 | if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p; |
| 28285 | const inst_info = inst_ty.ptrInfo(zcu); |
| 28286 | switch (try sema.coerceInMemoryAllowed( |
| 28287 | block, |
| 28288 | .fromInterned(dest_info.child), |
| 28289 | .fromInterned(inst_info.child), |
| 28290 | !dest_info.flags.is_const, |
| 28291 | target, |
| 28292 | dest_ty_src, |
| 28293 | inst_src, |
| 28294 | null, |
| 28295 | )) { |
| 28296 | .ok => {}, |
| 28297 | else => break :p, |
| 28298 | } |
| 28299 | if (inst_info.flags.size == .slice) { |
| 28300 | assert(dest_info.sentinel == .none); |
| 28301 | if (inst_info.sentinel == .none or |
| 28302 | inst_info.sentinel != (try pt.intValue(.fromInterned(inst_info.child), 0)).toIntern()) |
| 28303 | break :p; |
| 28304 | |
| 28305 | const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); |
| 28306 | return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src); |
| 28307 | } |
| 28308 | return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); |
| 28309 | }, |
| 28310 | else => {}, |
| 28311 | }, |
| 28312 | // []T to *[n]T |
| 28313 | .one => slice_to_array_ptr: { |
| 28314 | if (!inst_ty.isSlice(zcu)) break :slice_to_array_ptr; |
| 28315 | if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :slice_to_array_ptr; |
| 28316 | const array_ty: Type = .fromInterned(dest_info.child); |
| 28317 | if (array_ty.zigTypeTag(zcu) != .array) break :slice_to_array_ptr; |
| 28318 | const inst_val = maybe_inst_val orelse { |
| 28319 | if (!opts.report_err) return error.NotCoercible; |
| 28320 | return sema.fail( |
| 28321 | block, |
| 28322 | inst_src, |
| 28323 | "coercion from slice to array pointer type '{f}' requires length to be known at compile-time", |
| 28324 | .{dest_ty.fmt(pt)}, |
| 28325 | ); |
| 28326 | }; |
| 28327 | |
| 28328 | const slice: InternPool.Key.Slice = slice: { |
| 28329 | switch (ip.indexToKey(inst_val.toIntern())) { |
| 28330 | .undef => {}, |
| 28331 | .slice => |slice| if (slice.len != .undef_usize) break :slice slice, |
| 28332 | else => unreachable, |
| 28333 | } |
| 28334 | if (!opts.report_err) return error.NotCoercible; |
| 28335 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 28336 | const msg = try sema.errMsg(inst_src, "slice with undefined length cannot cast into array pointer type '{f}'", .{ |
| 28337 | dest_ty.fmt(pt), |
| 28338 | }); |
| 28339 | errdefer msg.destroy(gpa); |
| 28340 | try sema.errNote(inst_src, msg, "length of slice must be defined and match length of array type", .{}); |
| 28341 | break :msg msg; |
| 28342 | }); |
| 28343 | }; |
| 28344 | const slice_len = Value.fromInterned(slice.len).toUnsignedInt(zcu); |
| 28345 | if (array_ty.arrayLen(zcu) != slice_len) { |
| 28346 | if (!opts.report_err) return error.NotCoercible; |
| 28347 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 28348 | const msg = try sema.errMsg(inst_src, "slice of length {d} cannot cast into array pointer type '{f}'", .{ |
| 28349 | slice_len, dest_ty.fmt(pt), |
| 28350 | }); |
| 28351 | errdefer msg.destroy(gpa); |
| 28352 | try sema.errNote(inst_src, msg, "length of slice must match length of array type", .{}); |
| 28353 | break :msg msg; |
| 28354 | }); |
| 28355 | } |
| 28356 | |
| 28357 | const inst_elem_ty = inst_ty.childType(zcu); |
| 28358 | const dest_elem_ty = array_ty.childType(zcu); |
| 28359 | const dest_is_mut = !dest_info.flags.is_const; |
| 28360 | switch (try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, dest_is_mut, target, dest_ty_src, inst_src, null)) { |
| 28361 | .ok => {}, |
| 28362 | else => |elem_res| { |
| 28363 | in_memory_result = .{ .ptr_child = .{ |
| 28364 | .child = try elem_res.dupe(sema.arena), |
| 28365 | .actual = inst_elem_ty, |
| 28366 | .wanted = dest_elem_ty, |
| 28367 | } }; |
| 28368 | break :slice_to_array_ptr; |
| 28369 | }, |
| 28370 | } |
| 28371 | |
| 28372 | if (array_ty.sentinel(zcu)) |array_sentinel| { |
| 28373 | if (inst_ty.sentinel(zcu)) |slice_sentinel| { |
| 28374 | if (array_sentinel.toIntern() != |
| 28375 | (try pt.getCoerced(slice_sentinel, dest_elem_ty)).toIntern()) |
| 28376 | { |
| 28377 | in_memory_result = .{ .ptr_sentinel = .{ |
| 28378 | .actual = slice_sentinel, |
| 28379 | .wanted = array_sentinel, |
| 28380 | .ty = dest_elem_ty, |
| 28381 | } }; |
| 28382 | break :slice_to_array_ptr; |
| 28383 | } |
| 28384 | } else { |
| 28385 | in_memory_result = .{ .ptr_sentinel = .{ |
| 28386 | .actual = .@"unreachable", |
| 28387 | .wanted = array_sentinel, |
| 28388 | .ty = dest_elem_ty, |
| 28389 | } }; |
| 28390 | break :slice_to_array_ptr; |
| 28391 | } |
| 28392 | } |
| 28393 | |
| 28394 | const array_ptr = try pt.sliceToArrayPtr(slice); |
| 28395 | return sema.coerceCompatiblePtrs(block, dest_ty, .fromValue(array_ptr), inst_src); |
| 28396 | }, |
| 28397 | .slice => to_slice: { |
| 28398 | if (inst_ty.zigTypeTag(zcu) == .array) { |
| 28399 | if (!opts.report_err) return error.NotCoercible; |
| 28400 | return sema.fail( |
| 28401 | block, |
| 28402 | inst_src, |
| 28403 | "array literal requires address-of operator (&) to coerce to slice type '{f}'", |
| 28404 | .{dest_ty.fmt(pt)}, |
| 28405 | ); |
| 28406 | } |
| 28407 | |
| 28408 | if (!inst_ty.isSinglePointer(zcu)) break :to_slice; |
| 28409 | const inst_child_ty = inst_ty.childType(zcu); |
| 28410 | if (!inst_child_ty.isTuple(zcu)) break :to_slice; |
| 28411 | |
| 28412 | // empty tuple to zero-length slice |
| 28413 | // note that this allows coercing to a mutable slice. |
| 28414 | if (inst_child_ty.structFieldCount(zcu) == 0) { |
| 28415 | const empty_array_ty = try pt.arrayType(.{ |
| 28416 | .len = 0, |
| 28417 | .child = dest_info.child, |
| 28418 | .sentinel = dest_info.sentinel, |
| 28419 | }); |
| 28420 | const empty_array_val = try pt.aggregateValue(empty_array_ty, &.{}); |
| 28421 | const empty_array_ptr = try sema.uavRef(empty_array_val); |
| 28422 | return sema.coerceArrayPtrToSlice(block, dest_ty, empty_array_ptr, inst_src); |
| 28423 | } |
| 28424 | |
| 28425 | // pointer to tuple to slice |
| 28426 | if (!dest_info.flags.is_const) { |
| 28427 | if (!opts.report_err) return error.NotCoercible; |
| 28428 | const err_msg = err_msg: { |
| 28429 | const err_msg = try sema.errMsg(inst_src, "cannot cast pointer to tuple to '{f}'", .{dest_ty.fmt(pt)}); |
| 28430 | errdefer err_msg.destroy(sema.gpa); |
| 28431 | try sema.errNote(dest_ty_src, err_msg, "pointers to tuples can only coerce to constant pointers", .{}); |
| 28432 | break :err_msg err_msg; |
| 28433 | }; |
| 28434 | return sema.failWithOwnedErrorMsg(block, err_msg); |
| 28435 | } |
| 28436 | return sema.coerceTupleToSlicePtrs(block, dest_ty, dest_ty_src, inst, inst_src); |
| 28437 | }, |
| 28438 | .many => p: { |
| 28439 | if (!inst_ty.isSlice(zcu)) break :p; |
| 28440 | if (!sema.checkPtrAttributes(dest_ty, inst_ty, &in_memory_result)) break :p; |
| 28441 | const inst_info = inst_ty.ptrInfo(zcu); |
| 28442 | |
| 28443 | switch (try sema.coerceInMemoryAllowed( |
| 28444 | block, |
| 28445 | .fromInterned(dest_info.child), |
| 28446 | .fromInterned(inst_info.child), |
| 28447 | !dest_info.flags.is_const, |
| 28448 | target, |
| 28449 | dest_ty_src, |
| 28450 | inst_src, |
| 28451 | null, |
| 28452 | )) { |
| 28453 | .ok => {}, |
| 28454 | else => break :p, |
| 28455 | } |
| 28456 | |
| 28457 | if (dest_info.sentinel == .none or inst_info.sentinel == .none or |
| 28458 | dest_info.sentinel != |
| 28459 | (try pt.getCoerced(.fromInterned(inst_info.sentinel), .fromInterned(dest_info.child))).toIntern()) |
| 28460 | break :p; |
| 28461 | |
| 28462 | const slice_ptr = try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty); |
| 28463 | return sema.coerceCompatiblePtrs(block, dest_ty, slice_ptr, inst_src); |
| 28464 | }, |
| 28465 | } |
| 28466 | }, |
| 28467 | .int, .comptime_int => switch (inst_ty.zigTypeTag(zcu)) { |
| 28468 | .float, .comptime_float => float: { |
| 28469 | const val = maybe_inst_val orelse { |
| 28470 | if (dest_ty.zigTypeTag(zcu) == .comptime_int) { |
| 28471 | if (!opts.report_err) return error.NotCoercible; |
| 28472 | return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_int }); |
| 28473 | } |
| 28474 | break :float; |
| 28475 | }; |
| 28476 | const result_val = try sema.intFromFloat(block, inst_src, val, inst_ty, dest_ty, .exact); |
| 28477 | return Air.internedToRef(result_val.toIntern()); |
| 28478 | }, |
| 28479 | .int, .comptime_int => { |
| 28480 | if (maybe_inst_val) |val| { |
| 28481 | // comptime-known integer to other number |
| 28482 | if (!val.intFitsInType(dest_ty, null, zcu)) { |
| 28483 | if (!opts.report_err) return error.NotCoercible; |
| 28484 | return sema.fail(block, inst_src, "type '{f}' cannot represent integer value '{f}'", .{ dest_ty.fmt(pt), val.fmtValueSema(pt, sema) }); |
| 28485 | } |
| 28486 | return switch (zcu.intern_pool.indexToKey(val.toIntern())) { |
| 28487 | .undef => .fromValue(try dest_ty.onePossibleValue(pt) orelse try pt.undefValue(dest_ty)), |
| 28488 | .int => |int| Air.internedToRef( |
| 28489 | try zcu.intern_pool.getCoercedInts(gpa, io, pt.tid, int, dest_ty.toIntern()), |
| 28490 | ), |
| 28491 | else => unreachable, |
| 28492 | }; |
| 28493 | } |
| 28494 | if (dest_ty.zigTypeTag(zcu) == .comptime_int) { |
| 28495 | if (!opts.report_err) return error.NotCoercible; |
| 28496 | if (opts.no_cast_to_comptime_int) return inst; |
| 28497 | return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_int }); |
| 28498 | } |
| 28499 | |
| 28500 | // integer widening |
| 28501 | const dst_info = dest_ty.intInfo(zcu); |
| 28502 | const src_info = inst_ty.intInfo(zcu); |
| 28503 | if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or |
| 28504 | // small enough unsigned ints can get casted to large enough signed ints |
| 28505 | (dst_info.signedness == .signed and dst_info.bits > src_info.bits)) |
| 28506 | { |
| 28507 | try sema.requireRuntimeBlock(block, inst_src, null); |
| 28508 | return block.addTyOp(.int_cast, dest_ty, inst); |
| 28509 | } |
| 28510 | }, |
| 28511 | else => {}, |
| 28512 | }, |
| 28513 | .float, .comptime_float => switch (inst_ty.zigTypeTag(zcu)) { |
| 28514 | .comptime_float => { |
| 28515 | const val = sema.resolveValue(inst).?; |
| 28516 | const result_val = try val.floatCast(dest_ty, pt); |
| 28517 | return Air.internedToRef(result_val.toIntern()); |
| 28518 | }, |
| 28519 | .float => { |
| 28520 | if (maybe_inst_val) |val| { |
| 28521 | const result_val = try val.floatCast(dest_ty, pt); |
| 28522 | if (!val.eql(try result_val.floatCast(inst_ty, pt), inst_ty, zcu)) { |
| 28523 | if (!opts.report_err) return error.NotCoercible; |
| 28524 | return sema.fail( |
| 28525 | block, |
| 28526 | inst_src, |
| 28527 | "type '{f}' cannot represent float value '{f}'", |
| 28528 | .{ dest_ty.fmt(pt), val.fmtValueSema(pt, sema) }, |
| 28529 | ); |
| 28530 | } |
| 28531 | return Air.internedToRef(result_val.toIntern()); |
| 28532 | } else if (dest_ty.zigTypeTag(zcu) == .comptime_float) { |
| 28533 | if (!opts.report_err) return error.NotCoercible; |
| 28534 | return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_float }); |
| 28535 | } |
| 28536 | |
| 28537 | // float widening |
| 28538 | const src_bits = inst_ty.floatBits(target); |
| 28539 | const dst_bits = dest_ty.floatBits(target); |
| 28540 | if (dst_bits >= src_bits) { |
| 28541 | try sema.requireRuntimeBlock(block, inst_src, null); |
| 28542 | return block.addTyOp(.fpext, dest_ty, inst); |
| 28543 | } |
| 28544 | }, |
| 28545 | .int, .comptime_int => int: { |
| 28546 | const val = maybe_inst_val orelse { |
| 28547 | if (dest_ty.zigTypeTag(zcu) == .comptime_float) { |
| 28548 | if (!opts.report_err) return error.NotCoercible; |
| 28549 | return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_float }); |
| 28550 | } |
| 28551 | const int_info = inst_ty.intInfo(zcu); |
| 28552 | const int_precision = int_info.bits - @intFromBool(int_info.signedness == .signed); |
| 28553 | if (int_precision <= dest_ty.floatSignificandBits(target)) { |
| 28554 | try sema.requireRuntimeBlock(block, inst_src, null); |
| 28555 | return block.addTyOp(.float_from_int, dest_ty, inst); |
| 28556 | } |
| 28557 | break :int; |
| 28558 | }; |
| 28559 | if (val.isUndef(zcu)) { |
| 28560 | return .fromValue(try pt.undefValue(dest_ty)); |
| 28561 | } |
| 28562 | const result_val = try pt.floatValue(dest_ty, val.toFloat(f128, zcu)); |
| 28563 | const float = ip.indexToKey(result_val.toIntern()).float; |
| 28564 | var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined; |
| 28565 | const operand_big_int = val.toBigInt(&buffer, zcu); |
| 28566 | const fits = switch (float.storage) { |
| 28567 | inline else => |x| fits: { |
| 28568 | if (!std.math.isFinite(x)) break :fits false; |
| 28569 | var result_big_int: std.math.big.int.Mutable = .{ |
| 28570 | .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(x)), |
| 28571 | .len = undefined, |
| 28572 | .positive = undefined, |
| 28573 | }; |
| 28574 | switch (result_big_int.setFloat(x, .nearest_even)) { |
| 28575 | .inexact => break :fits false, |
| 28576 | .exact => {}, |
| 28577 | } |
| 28578 | break :fits result_big_int.toConst().eql(operand_big_int); |
| 28579 | }, |
| 28580 | }; |
| 28581 | if (!fits) { |
| 28582 | if (!opts.report_err) return error.NotCoercible; |
| 28583 | return sema.fail( |
| 28584 | block, |
| 28585 | inst_src, |
| 28586 | "type '{f}' cannot represent integer value '{f}'", |
| 28587 | .{ dest_ty.fmt(pt), val.fmtValue(pt) }, |
| 28588 | ); |
| 28589 | } |
| 28590 | return .fromValue(result_val); |
| 28591 | }, |
| 28592 | else => {}, |
| 28593 | }, |
| 28594 | .@"enum" => switch (inst_ty.zigTypeTag(zcu)) { |
| 28595 | .enum_literal => { |
| 28596 | // enum literal to enum |
| 28597 | const val = sema.resolveValue(inst).?; |
| 28598 | const string = zcu.intern_pool.indexToKey(val.toIntern()).enum_literal; |
| 28599 | const field_index = dest_ty.enumFieldIndex(string, zcu) orelse { |
| 28600 | if (!opts.report_err) return error.NotCoercible; |
| 28601 | return sema.fail(block, inst_src, "no field named '{f}' in enum '{f}'", .{ |
| 28602 | string.fmt(&zcu.intern_pool), dest_ty.fmt(pt), |
| 28603 | }); |
| 28604 | }; |
| 28605 | return Air.internedToRef((try pt.enumValueFieldIndex(dest_ty, @intCast(field_index))).toIntern()); |
| 28606 | }, |
| 28607 | .@"union" => if (inst_ty.unionTagType(zcu)) |enum_tag_ty| { |
| 28608 | // union to its own tag type |
| 28609 | if (enum_tag_ty.toIntern() == dest_ty.toIntern()) { |
| 28610 | return sema.unionToTag(block, inst); |
| 28611 | } |
| 28612 | }, |
| 28613 | else => {}, |
| 28614 | }, |
| 28615 | .error_union => switch (inst_ty.zigTypeTag(zcu)) { |
| 28616 | // E to E!T |
| 28617 | .error_set => if (sema.wrapErrorUnionSet(block, dest_ty, inst, inst_src)) |res| { |
| 28618 | return res; |
| 28619 | } else |err| switch (err) { |
| 28620 | error.NotCoercible => if (in_memory_result == .no_match) { |
| 28621 | // Try to give more useful notes |
| 28622 | const err_set_type = dest_ty.errorUnionSet(zcu); |
| 28623 | in_memory_result = try sema.coerceInMemoryAllowed(block, err_set_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val); |
| 28624 | }, |
| 28625 | else => |e| return e, |
| 28626 | }, |
| 28627 | // T to E!T |
| 28628 | else => if (sema.wrapErrorUnionPayload(block, dest_ty, inst, inst_src)) |res| { |
| 28629 | return res; |
| 28630 | } else |err| switch (err) { |
| 28631 | error.NotCoercible => if (in_memory_result == .no_match) { |
| 28632 | // Try to give more useful notes |
| 28633 | const payload_type = dest_ty.errorUnionPayload(zcu); |
| 28634 | in_memory_result = try sema.coerceInMemoryAllowed(block, payload_type, inst_ty, false, target, dest_ty_src, inst_src, maybe_inst_val); |
| 28635 | }, |
| 28636 | else => |e| return e, |
| 28637 | }, |
| 28638 | }, |
| 28639 | .@"union" => switch (inst_ty.zigTypeTag(zcu)) { |
| 28640 | .@"enum", .enum_literal => return sema.coerceEnumToUnion(block, dest_ty, dest_ty_src, inst, inst_src), |
| 28641 | else => {}, |
| 28642 | }, |
| 28643 | .array => switch (inst_ty.zigTypeTag(zcu)) { |
| 28644 | .array => array_to_array: { |
| 28645 | // Array coercions are allowed only if the child is IMC and the sentinel is unchanged or removed. |
| 28646 | if (.ok != try sema.coerceInMemoryAllowed( |
| 28647 | block, |
| 28648 | dest_ty.childType(zcu), |
| 28649 | inst_ty.childType(zcu), |
| 28650 | false, |
| 28651 | target, |
| 28652 | dest_ty_src, |
| 28653 | inst_src, |
| 28654 | null, |
| 28655 | )) { |
| 28656 | break :array_to_array; |
| 28657 | } |
| 28658 | |
| 28659 | if (dest_ty.sentinel(zcu)) |dest_sent| { |
| 28660 | const src_sent = inst_ty.sentinel(zcu) orelse break :array_to_array; |
| 28661 | if (dest_sent.toIntern() != (try pt.getCoerced(src_sent, dest_ty.childType(zcu))).toIntern()) { |
| 28662 | break :array_to_array; |
| 28663 | } |
| 28664 | } |
| 28665 | |
| 28666 | return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src); |
| 28667 | }, |
| 28668 | .vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src), |
| 28669 | .@"struct" => { |
| 28670 | if (inst_ty.isTuple(zcu)) { |
| 28671 | return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); |
| 28672 | } |
| 28673 | }, |
| 28674 | else => {}, |
| 28675 | }, |
| 28676 | .vector => switch (inst_ty.zigTypeTag(zcu)) { |
| 28677 | .array, .vector => return sema.coerceArrayLike(block, dest_ty, dest_ty_src, inst, inst_src), |
| 28678 | .@"struct" => { |
| 28679 | if (inst_ty.isTuple(zcu)) { |
| 28680 | return sema.coerceTupleToArray(block, dest_ty, dest_ty_src, inst, inst_src); |
| 28681 | } |
| 28682 | }, |
| 28683 | else => {}, |
| 28684 | }, |
| 28685 | .@"struct" => blk: { |
| 28686 | if (dest_ty.isTuple(zcu) and inst_ty.isTuple(zcu)) { |
| 28687 | return sema.coerceTupleToTuple(block, dest_ty, inst, inst_src) catch |err| switch (err) { |
| 28688 | error.NotCoercible => break :blk, |
| 28689 | else => |e| return e, |
| 28690 | }; |
| 28691 | } |
| 28692 | }, |
| 28693 | else => {}, |
| 28694 | } |
| 28695 | |
| 28696 | const dest_is_npv = switch (dest_ty.classify(zcu)) { |
| 28697 | .no_possible_value => true, |
| 28698 | .one_possible_value => if (inst == .undef) { |
| 28699 | return .fromValue((try dest_ty.onePossibleValue(pt)).?); |
| 28700 | } else false, |
| 28701 | .runtime, .fully_comptime, .partially_comptime => if (inst == .undef) { |
| 28702 | return .fromValue(try pt.undefValue(dest_ty)); |
| 28703 | } else false, |
| 28704 | }; |
| 28705 | |
| 28706 | if (!opts.report_err) return error.NotCoercible; |
| 28707 | |
| 28708 | if (opts.is_ret and dest_ty.zigTypeTag(zcu) == .noreturn) { |
| 28709 | const msg = msg: { |
| 28710 | const msg = try sema.errMsg(inst_src, "function declared 'noreturn' returns", .{}); |
| 28711 | errdefer msg.destroy(sema.gpa); |
| 28712 | |
| 28713 | const ret_ty_src: LazySrcLoc = .{ |
| 28714 | .base_node_inst = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).srcInst(ip), |
| 28715 | .offset = .{ .node_offset_fn_type_ret_ty = .zero }, |
| 28716 | }; |
| 28717 | try sema.errNote(ret_ty_src, msg, "'noreturn' declared here", .{}); |
| 28718 | break :msg msg; |
| 28719 | }; |
| 28720 | return sema.failWithOwnedErrorMsg(block, msg); |
| 28721 | } |
| 28722 | |
| 28723 | const msg = msg: { |
| 28724 | const msg = try sema.typeMismatchErrMsg(inst_src, dest_ty, inst_ty); |
| 28725 | errdefer msg.destroy(sema.gpa); |
| 28726 | |
| 28727 | if (dest_is_npv) { |
| 28728 | try sema.errNote(inst_src, msg, "cannot coerce to uninstantiable type '{f}'", .{dest_ty.fmt(pt)}); |
| 28729 | } |
| 28730 | |
| 28731 | // E!T to T |
| 28732 | if (inst_ty.zigTypeTag(zcu) == .error_union and |
| 28733 | (try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty.errorUnionPayload(zcu), false, target, dest_ty_src, inst_src, null)) == .ok) |
| 28734 | { |
| 28735 | try sema.errNote(inst_src, msg, "cannot convert error union to payload type", .{}); |
| 28736 | try sema.errNote(inst_src, msg, "consider using 'try', 'catch', or 'if'", .{}); |
| 28737 | } |
| 28738 | |
| 28739 | // ?T to T |
| 28740 | if (inst_ty.zigTypeTag(zcu) == .optional and |
| 28741 | (try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty.optionalChild(zcu), false, target, dest_ty_src, inst_src, null)) == .ok) |
| 28742 | { |
| 28743 | try sema.errNote(inst_src, msg, "cannot convert optional to payload type", .{}); |
| 28744 | try sema.errNote(inst_src, msg, "consider using '.?', 'orelse', or 'if'", .{}); |
| 28745 | } |
| 28746 | |
| 28747 | try in_memory_result.report(sema, inst_src, msg); |
| 28748 | |
| 28749 | // Add notes about function return type |
| 28750 | if (opts.is_ret and |
| 28751 | !zcu.test_functions.contains(zcu.funcInfo(sema.func_index).owner_nav)) |
| 28752 | { |
| 28753 | const ret_ty_src: LazySrcLoc = .{ |
| 28754 | .base_node_inst = ip.getNav(zcu.funcInfo(sema.func_index).owner_nav).srcInst(ip), |
| 28755 | .offset = .{ .node_offset_fn_type_ret_ty = .zero }, |
| 28756 | }; |
| 28757 | if (inst_ty.isError(zcu) and !dest_ty.isError(zcu)) { |
| 28758 | try sema.errNote(ret_ty_src, msg, "function cannot return an error", .{}); |
| 28759 | } else { |
| 28760 | try sema.errNote(ret_ty_src, msg, "function return type declared here", .{}); |
| 28761 | } |
| 28762 | } |
| 28763 | |
| 28764 | if (try opts.param_src.get(sema)) |param_src| { |
| 28765 | try sema.errNote(param_src, msg, "parameter type declared here", .{}); |
| 28766 | } |
| 28767 | |
| 28768 | // TODO maybe add "cannot store an error in type '{f}'" note |
| 28769 | |
| 28770 | break :msg msg; |
| 28771 | }; |
| 28772 | return sema.failWithOwnedErrorMsg(block, msg); |
| 28773 | } |
| 28774 | |
| 28775 | const InMemoryCoercionResult = union(enum) { |
| 28776 | ok: Strategy, |
| 28777 | no_match: Pair, |
| 28778 | int_not_coercible: Int, |
| 28779 | comptime_int_not_coercible: TypeValuePair, |
| 28780 | error_union_payload: PairAndChild, |
| 28781 | array_len: IntPair, |
| 28782 | array_sentinel: Sentinel, |
| 28783 | array_elem: PairAndChild, |
| 28784 | vector_len: IntPair, |
| 28785 | vector_elem: PairAndChild, |
| 28786 | optional_shape: Pair, |
| 28787 | optional_child: PairAndChild, |
| 28788 | from_anyerror, |
| 28789 | missing_error: []const InternPool.NullTerminatedString, |
| 28790 | /// true if wanted is var args |
| 28791 | fn_var_args: bool, |
| 28792 | /// true if wanted is generic |
| 28793 | fn_generic: bool, |
| 28794 | fn_param_count: IntPair, |
| 28795 | fn_param_noalias: IntPair, |
| 28796 | fn_param_comptime: ComptimeParam, |
| 28797 | fn_param: Param, |
| 28798 | fn_cc: CC, |
| 28799 | fn_return_type: PairAndChild, |
| 28800 | ptr_child: PairAndChild, |
| 28801 | ptr_addrspace: AddressSpace, |
| 28802 | ptr_sentinel: Sentinel, |
| 28803 | ptr_size: Size, |
| 28804 | ptr_const: Pair, |
| 28805 | ptr_volatile: Pair, |
| 28806 | ptr_allowzero: Pair, |
| 28807 | ptr_bit_range: BitRange, |
| 28808 | ptr_alignment: AlignPair, |
| 28809 | double_ptr_to_anyopaque: Pair, |
| 28810 | slice_to_anyopaque: Pair, |
| 28811 | |
| 28812 | const Strategy = enum { |
| 28813 | /// There isn't a special strategy for this particular coercion---we'll just need to |
| 28814 | /// reinterpret the bytes in memory. |
| 28815 | none, |
| 28816 | |
| 28817 | /// The source and destination types are equal, so no explicit cast operation is necessary. |
| 28818 | same_type, |
| 28819 | /// The coercion can be lowered to `Air.Inst.Tag.bit_cast`. |
| 28820 | bit_cast, |
| 28821 | /// The coercion can be lowered to `Air.Inst.Tag.ptr_cast`. |
| 28822 | ptr_cast, |
| 28823 | /// The coercion can be lowered to `Air.Inst.Tag.error_cast`. |
| 28824 | error_cast, |
| 28825 | }; |
| 28826 | |
| 28827 | const Pair = struct { |
| 28828 | actual: Type, |
| 28829 | wanted: Type, |
| 28830 | }; |
| 28831 | |
| 28832 | const TypeValuePair = struct { |
| 28833 | actual: Value, |
| 28834 | wanted: Type, |
| 28835 | }; |
| 28836 | |
| 28837 | const PairAndChild = struct { |
| 28838 | child: *InMemoryCoercionResult, |
| 28839 | actual: Type, |
| 28840 | wanted: Type, |
| 28841 | }; |
| 28842 | |
| 28843 | const Param = struct { |
| 28844 | child: *InMemoryCoercionResult, |
| 28845 | actual: Type, |
| 28846 | wanted: Type, |
| 28847 | index: u64, |
| 28848 | }; |
| 28849 | |
| 28850 | const ComptimeParam = struct { |
| 28851 | index: u64, |
| 28852 | wanted: bool, |
| 28853 | }; |
| 28854 | |
| 28855 | const Sentinel = struct { |
| 28856 | // unreachable_value indicates no sentinel |
| 28857 | actual: Value, |
| 28858 | wanted: Value, |
| 28859 | ty: Type, |
| 28860 | }; |
| 28861 | |
| 28862 | const Int = struct { |
| 28863 | actual_signedness: std.lang.Signedness, |
| 28864 | wanted_signedness: std.lang.Signedness, |
| 28865 | actual_bits: u16, |
| 28866 | wanted_bits: u16, |
| 28867 | }; |
| 28868 | |
| 28869 | const IntPair = struct { |
| 28870 | actual: u64, |
| 28871 | wanted: u64, |
| 28872 | }; |
| 28873 | |
| 28874 | const AlignPair = struct { |
| 28875 | actual: Alignment, |
| 28876 | wanted: Alignment, |
| 28877 | }; |
| 28878 | |
| 28879 | const Size = struct { |
| 28880 | actual: std.lang.Type.Pointer.Size, |
| 28881 | wanted: std.lang.Type.Pointer.Size, |
| 28882 | }; |
| 28883 | |
| 28884 | const AddressSpace = struct { |
| 28885 | actual: std.lang.AddressSpace, |
| 28886 | wanted: std.lang.AddressSpace, |
| 28887 | }; |
| 28888 | |
| 28889 | const CC = struct { |
| 28890 | actual: std.lang.CallingConvention, |
| 28891 | wanted: std.lang.CallingConvention, |
| 28892 | }; |
| 28893 | |
| 28894 | const BitRange = struct { |
| 28895 | actual_host: u16, |
| 28896 | wanted_host: u16, |
| 28897 | actual_offset: u16, |
| 28898 | wanted_offset: u16, |
| 28899 | }; |
| 28900 | |
| 28901 | fn dupe(child: *const InMemoryCoercionResult, arena: Allocator) !*InMemoryCoercionResult { |
| 28902 | const res = try arena.create(InMemoryCoercionResult); |
| 28903 | res.* = child.*; |
| 28904 | return res; |
| 28905 | } |
| 28906 | |
| 28907 | fn report(res: *const InMemoryCoercionResult, sema: *Sema, src: LazySrcLoc, msg: *Zcu.ErrorMsg) !void { |
| 28908 | const pt = sema.pt; |
| 28909 | var cur = res; |
| 28910 | while (true) switch (cur.*) { |
| 28911 | .ok => unreachable, |
| 28912 | .no_match => |types| { |
| 28913 | try sema.addDeclaredHereNote(msg, types.wanted); |
| 28914 | try sema.addDeclaredHereNote(msg, types.actual); |
| 28915 | break; |
| 28916 | }, |
| 28917 | .int_not_coercible => |int| { |
| 28918 | try sema.errNote(src, msg, "{s} {d}-bit int cannot represent all possible {s} {d}-bit values", .{ |
| 28919 | @tagName(int.wanted_signedness), int.wanted_bits, @tagName(int.actual_signedness), int.actual_bits, |
| 28920 | }); |
| 28921 | break; |
| 28922 | }, |
| 28923 | .comptime_int_not_coercible => |int| { |
| 28924 | try sema.errNote(src, msg, "type '{f}' cannot represent value '{f}'", .{ |
| 28925 | int.wanted.fmt(pt), int.actual.fmtValueSema(pt, sema), |
| 28926 | }); |
| 28927 | break; |
| 28928 | }, |
| 28929 | .error_union_payload => |pair| { |
| 28930 | try sema.errNote(src, msg, "error union payload '{f}' cannot cast into error union payload '{f}'", .{ |
| 28931 | pair.actual.fmt(pt), pair.wanted.fmt(pt), |
| 28932 | }); |
| 28933 | cur = pair.child; |
| 28934 | }, |
| 28935 | .array_len => |lens| { |
| 28936 | try sema.errNote(src, msg, "array of length {d} cannot cast into an array of length {d}", .{ |
| 28937 | lens.actual, lens.wanted, |
| 28938 | }); |
| 28939 | break; |
| 28940 | }, |
| 28941 | .array_sentinel => |sentinel| { |
| 28942 | if (sentinel.wanted.toIntern() == .unreachable_value) { |
| 28943 | try sema.errNote(src, msg, "source array cannot be guaranteed to maintain '{f}' sentinel", .{ |
| 28944 | sentinel.actual.fmtValueSema(pt, sema), |
| 28945 | }); |
| 28946 | } else if (sentinel.actual.toIntern() == .unreachable_value) { |
| 28947 | try sema.errNote(src, msg, "destination array requires '{f}' sentinel", .{ |
| 28948 | sentinel.wanted.fmtValueSema(pt, sema), |
| 28949 | }); |
| 28950 | } else { |
| 28951 | try sema.errNote(src, msg, "array sentinel '{f}' cannot cast into array sentinel '{f}'", .{ |
| 28952 | sentinel.actual.fmtValueSema(pt, sema), sentinel.wanted.fmtValueSema(pt, sema), |
| 28953 | }); |
| 28954 | } |
| 28955 | break; |
| 28956 | }, |
| 28957 | .array_elem => |pair| { |
| 28958 | try sema.errNote(src, msg, "array element type '{f}' cannot cast into array element type '{f}'", .{ |
| 28959 | pair.actual.fmt(pt), pair.wanted.fmt(pt), |
| 28960 | }); |
| 28961 | cur = pair.child; |
| 28962 | }, |
| 28963 | .vector_len => |lens| { |
| 28964 | try sema.errNote(src, msg, "vector of length {d} cannot cast into a vector of length {d}", .{ |
| 28965 | lens.actual, lens.wanted, |
| 28966 | }); |
| 28967 | break; |
| 28968 | }, |
| 28969 | .vector_elem => |pair| { |
| 28970 | try sema.errNote(src, msg, "vector element type '{f}' cannot cast into vector element type '{f}'", .{ |
| 28971 | pair.actual.fmt(pt), pair.wanted.fmt(pt), |
| 28972 | }); |
| 28973 | cur = pair.child; |
| 28974 | }, |
| 28975 | .optional_shape => |pair| { |
| 28976 | try sema.errNote(src, msg, "optional type child '{f}' cannot cast into optional type child '{f}'", .{ |
| 28977 | pair.actual.optionalChild(pt.zcu).fmt(pt), pair.wanted.optionalChild(pt.zcu).fmt(pt), |
| 28978 | }); |
| 28979 | break; |
| 28980 | }, |
| 28981 | .optional_child => |pair| { |
| 28982 | try sema.errNote(src, msg, "optional type child '{f}' cannot cast into optional type child '{f}'", .{ |
| 28983 | pair.actual.fmt(pt), pair.wanted.fmt(pt), |
| 28984 | }); |
| 28985 | cur = pair.child; |
| 28986 | }, |
| 28987 | .from_anyerror => { |
| 28988 | try sema.errNote(src, msg, "global error set cannot cast into a smaller set", .{}); |
| 28989 | break; |
| 28990 | }, |
| 28991 | .missing_error => |missing_errors| { |
| 28992 | for (missing_errors) |err| { |
| 28993 | try sema.errNote(src, msg, "'error.{f}' not a member of destination error set", .{err.fmt(&pt.zcu.intern_pool)}); |
| 28994 | } |
| 28995 | break; |
| 28996 | }, |
| 28997 | .fn_var_args => |wanted_var_args| { |
| 28998 | if (wanted_var_args) { |
| 28999 | try sema.errNote(src, msg, "non-variadic function cannot cast into a variadic function", .{}); |
| 29000 | } else { |
| 29001 | try sema.errNote(src, msg, "variadic function cannot cast into a non-variadic function", .{}); |
| 29002 | } |
| 29003 | break; |
| 29004 | }, |
| 29005 | .fn_generic => |wanted_generic| { |
| 29006 | if (wanted_generic) { |
| 29007 | try sema.errNote(src, msg, "non-generic function cannot cast into a generic function", .{}); |
| 29008 | } else { |
| 29009 | try sema.errNote(src, msg, "generic function cannot cast into a non-generic function", .{}); |
| 29010 | } |
| 29011 | break; |
| 29012 | }, |
| 29013 | .fn_param_count => |lens| { |
| 29014 | try sema.errNote(src, msg, "function with {d} parameters cannot cast into a function with {d} parameters", .{ |
| 29015 | lens.actual, lens.wanted, |
| 29016 | }); |
| 29017 | break; |
| 29018 | }, |
| 29019 | .fn_param_noalias => |param| { |
| 29020 | var index: u6 = 0; |
| 29021 | var actual_noalias = false; |
| 29022 | while (true) : (index += 1) { |
| 29023 | const actual: u1 = @truncate(param.actual >> index); |
| 29024 | const wanted: u1 = @truncate(param.wanted >> index); |
| 29025 | if (actual != wanted) { |
| 29026 | actual_noalias = actual == 1; |
| 29027 | break; |
| 29028 | } |
| 29029 | } |
| 29030 | if (!actual_noalias) { |
| 29031 | try sema.errNote(src, msg, "regular parameter {d} cannot cast into a noalias parameter", .{index}); |
| 29032 | } else { |
| 29033 | try sema.errNote(src, msg, "noalias parameter {d} cannot cast into a regular parameter", .{index}); |
| 29034 | } |
| 29035 | break; |
| 29036 | }, |
| 29037 | .fn_param_comptime => |param| { |
| 29038 | if (param.wanted) { |
| 29039 | try sema.errNote(src, msg, "non-comptime parameter {d} cannot cast into a comptime parameter", .{param.index}); |
| 29040 | } else { |
| 29041 | try sema.errNote(src, msg, "comptime parameter {d} cannot cast into a non-comptime parameter", .{param.index}); |
| 29042 | } |
| 29043 | break; |
| 29044 | }, |
| 29045 | .fn_param => |param| { |
| 29046 | try sema.errNote(src, msg, "parameter {d} '{f}' cannot cast into '{f}'", .{ |
| 29047 | param.index, param.actual.fmt(pt), param.wanted.fmt(pt), |
| 29048 | }); |
| 29049 | cur = param.child; |
| 29050 | }, |
| 29051 | .fn_cc => |cc| { |
| 29052 | try sema.errNote(src, msg, "calling convention '{s}' cannot cast into calling convention '{s}'", .{ @tagName(cc.actual), @tagName(cc.wanted) }); |
| 29053 | break; |
| 29054 | }, |
| 29055 | .fn_return_type => |pair| { |
| 29056 | try sema.errNote(src, msg, "return type '{f}' cannot cast into return type '{f}'", .{ |
| 29057 | pair.actual.fmt(pt), pair.wanted.fmt(pt), |
| 29058 | }); |
| 29059 | cur = pair.child; |
| 29060 | }, |
| 29061 | .ptr_child => |pair| { |
| 29062 | try sema.errNote(src, msg, "pointer type child '{f}' cannot cast into pointer type child '{f}'", .{ |
| 29063 | pair.actual.fmt(pt), pair.wanted.fmt(pt), |
| 29064 | }); |
| 29065 | cur = pair.child; |
| 29066 | }, |
| 29067 | .ptr_addrspace => |@"addrspace"| { |
| 29068 | try sema.errNote(src, msg, "address space '{s}' cannot cast into address space '{s}'", .{ @tagName(@"addrspace".actual), @tagName(@"addrspace".wanted) }); |
| 29069 | break; |
| 29070 | }, |
| 29071 | .ptr_sentinel => |sentinel| { |
| 29072 | if (sentinel.actual.toIntern() != .unreachable_value) { |
| 29073 | try sema.errNote(src, msg, "pointer sentinel '{f}' cannot cast into pointer sentinel '{f}'", .{ |
| 29074 | sentinel.actual.fmtValueSema(pt, sema), sentinel.wanted.fmtValueSema(pt, sema), |
| 29075 | }); |
| 29076 | } else { |
| 29077 | try sema.errNote(src, msg, "destination pointer requires '{f}' sentinel", .{ |
| 29078 | sentinel.wanted.fmtValueSema(pt, sema), |
| 29079 | }); |
| 29080 | } |
| 29081 | break; |
| 29082 | }, |
| 29083 | .ptr_size => |size| { |
| 29084 | try sema.errNote(src, msg, "a {s} cannot cast into a {s}", .{ pointerSizeString(size.actual), pointerSizeString(size.wanted) }); |
| 29085 | break; |
| 29086 | }, |
| 29087 | .ptr_allowzero => |pair| { |
| 29088 | const wanted_allow_zero = pair.wanted.ptrAllowsZero(pt.zcu); |
| 29089 | const actual_allow_zero = pair.actual.ptrAllowsZero(pt.zcu); |
| 29090 | if (actual_allow_zero and !wanted_allow_zero) { |
| 29091 | try sema.errNote(src, msg, "'{f}' could have null values which are illegal in type '{f}'", .{ |
| 29092 | pair.actual.fmt(pt), pair.wanted.fmt(pt), |
| 29093 | }); |
| 29094 | } else { |
| 29095 | try sema.errNote(src, msg, "mutable '{f}' would allow illegal null values stored to type '{f}'", .{ |
| 29096 | pair.wanted.fmt(pt), pair.actual.fmt(pt), |
| 29097 | }); |
| 29098 | } |
| 29099 | break; |
| 29100 | }, |
| 29101 | .ptr_const => |pair| { |
| 29102 | const wanted_const = pair.wanted.isConstPtr(pt.zcu); |
| 29103 | const actual_const = pair.actual.isConstPtr(pt.zcu); |
| 29104 | if (actual_const and !wanted_const) { |
| 29105 | try sema.errNote(src, msg, "cast discards const qualifier", .{}); |
| 29106 | } else { |
| 29107 | try sema.errNote(src, msg, "mutable '{f}' would allow illegal const pointers stored to type '{f}'", .{ |
| 29108 | pair.wanted.fmt(pt), pair.actual.fmt(pt), |
| 29109 | }); |
| 29110 | } |
| 29111 | break; |
| 29112 | }, |
| 29113 | .ptr_volatile => |pair| { |
| 29114 | const wanted_volatile = pair.wanted.isVolatilePtr(pt.zcu); |
| 29115 | const actual_volatile = pair.actual.isVolatilePtr(pt.zcu); |
| 29116 | if (actual_volatile and !wanted_volatile) { |
| 29117 | try sema.errNote(src, msg, "cast discards volatile qualifier", .{}); |
| 29118 | } else { |
| 29119 | try sema.errNote(src, msg, "mutable '{f}' would allow illegal volatile pointers stored to type '{f}'", .{ |
| 29120 | pair.wanted.fmt(pt), pair.actual.fmt(pt), |
| 29121 | }); |
| 29122 | } |
| 29123 | break; |
| 29124 | }, |
| 29125 | .ptr_bit_range => |bit_range| { |
| 29126 | if (bit_range.actual_host != bit_range.wanted_host) { |
| 29127 | try sema.errNote(src, msg, "pointer host size '{d}' cannot cast into pointer host size '{d}'", .{ |
| 29128 | bit_range.actual_host, bit_range.wanted_host, |
| 29129 | }); |
| 29130 | } |
| 29131 | if (bit_range.actual_offset != bit_range.wanted_offset) { |
| 29132 | try sema.errNote(src, msg, "pointer bit offset '{d}' cannot cast into pointer bit offset '{d}'", .{ |
| 29133 | bit_range.actual_offset, bit_range.wanted_offset, |
| 29134 | }); |
| 29135 | } |
| 29136 | break; |
| 29137 | }, |
| 29138 | .ptr_alignment => |pair| { |
| 29139 | try sema.errNote(src, msg, "pointer alignment '{d}' cannot cast into pointer alignment '{d}'", .{ |
| 29140 | pair.actual.toByteUnits() orelse 0, pair.wanted.toByteUnits() orelse 0, |
| 29141 | }); |
| 29142 | break; |
| 29143 | }, |
| 29144 | .double_ptr_to_anyopaque => |pair| { |
| 29145 | try sema.errNote(src, msg, "cannot implicitly cast double pointer '{f}' to anyopaque pointer '{f}'", .{ |
| 29146 | pair.actual.fmt(pt), pair.wanted.fmt(pt), |
| 29147 | }); |
| 29148 | break; |
| 29149 | }, |
| 29150 | .slice_to_anyopaque => |pair| { |
| 29151 | try sema.errNote(src, msg, "cannot implicitly cast slice '{f}' to anyopaque pointer '{f}'", .{ |
| 29152 | pair.actual.fmt(pt), pair.wanted.fmt(pt), |
| 29153 | }); |
| 29154 | try sema.errNote(src, msg, "consider using '.ptr'", .{}); |
| 29155 | break; |
| 29156 | }, |
| 29157 | }; |
| 29158 | } |
| 29159 | }; |
| 29160 | |
| 29161 | fn pointerSizeString(size: std.lang.Type.Pointer.Size) []const u8 { |
| 29162 | return switch (size) { |
| 29163 | .one => "single pointer", |
| 29164 | .many => "many pointer", |
| 29165 | .c => "C pointer", |
| 29166 | .slice => "slice", |
| 29167 | }; |
| 29168 | } |
| 29169 | |
| 29170 | /// If types `A` and `B` have identical representations in runtime memory, they are considered |
| 29171 | /// "in-memory coercible". This is a subset of normal coercions. Not only can `A` coerce to `B`, but |
| 29172 | /// also, coercions can happen through pointers. For instance, `*const A` can coerce to `*const B`. |
| 29173 | /// |
| 29174 | /// If this function is called, the coercion must be applied, or a compile error emitted if `.ok` |
| 29175 | /// is not returned. This is because this function may modify inferred error sets to make a |
| 29176 | /// coercion possible, even if `.ok` is not returned. |
| 29177 | pub fn coerceInMemoryAllowed( |
| 29178 | sema: *Sema, |
| 29179 | block: *Block, |
| 29180 | dest_ty: Type, |
| 29181 | src_ty: Type, |
| 29182 | /// If `true`, this query comes from an attempted coercion of the form `*Src` -> `*Dest`, where |
| 29183 | /// both pointers are mutable. If this coercion is allowed, one could store to the `*Dest` and |
| 29184 | /// load from the `*Src` to effectively perform an in-memory coercion from `Dest` to `Src`. |
| 29185 | /// Therefore, when `dest_is_mut`, the in-memory coercion must be valid in *both directions*. |
| 29186 | dest_is_mut: bool, |
| 29187 | target: *const std.Target, |
| 29188 | dest_src: LazySrcLoc, |
| 29189 | src_src: LazySrcLoc, |
| 29190 | src_val: ?Value, |
| 29191 | ) CompileError!InMemoryCoercionResult { |
| 29192 | const pt = sema.pt; |
| 29193 | const zcu = pt.zcu; |
| 29194 | |
| 29195 | if (src_val) |val| { |
| 29196 | assert(val.typeOf(zcu).toIntern() == src_ty.toIntern()); |
| 29197 | } |
| 29198 | |
| 29199 | if (dest_ty.eql(src_ty)) |
| 29200 | return .{ .ok = .same_type }; |
| 29201 | |
| 29202 | const dest_tag = dest_ty.zigTypeTag(zcu); |
| 29203 | const src_tag = src_ty.zigTypeTag(zcu); |
| 29204 | |
| 29205 | // Differently-named integers with the same number of bits. |
| 29206 | if (dest_tag == .int and src_tag == .int) { |
| 29207 | const dest_info = dest_ty.intInfo(zcu); |
| 29208 | const src_info = src_ty.intInfo(zcu); |
| 29209 | |
| 29210 | if (dest_info.signedness == src_info.signedness and |
| 29211 | dest_info.bits == src_info.bits) |
| 29212 | { |
| 29213 | return .{ .ok = .bit_cast }; |
| 29214 | } |
| 29215 | |
| 29216 | if ((src_info.signedness == dest_info.signedness and dest_info.bits < src_info.bits) or |
| 29217 | // small enough unsigned ints can get casted to large enough signed ints |
| 29218 | (dest_info.signedness == .signed and src_info.signedness == .unsigned and dest_info.bits <= src_info.bits) or |
| 29219 | (dest_info.signedness == .unsigned and src_info.signedness == .signed)) |
| 29220 | { |
| 29221 | return .{ .int_not_coercible = .{ |
| 29222 | .actual_signedness = src_info.signedness, |
| 29223 | .wanted_signedness = dest_info.signedness, |
| 29224 | .actual_bits = src_info.bits, |
| 29225 | .wanted_bits = dest_info.bits, |
| 29226 | } }; |
| 29227 | } |
| 29228 | } |
| 29229 | |
| 29230 | // Comptime int to regular int. |
| 29231 | if (dest_tag == .int and src_tag == .comptime_int) { |
| 29232 | if (src_val) |val| { |
| 29233 | if (!val.intFitsInType(dest_ty, null, zcu)) { |
| 29234 | return .{ .comptime_int_not_coercible = .{ .wanted = dest_ty, .actual = val } }; |
| 29235 | } |
| 29236 | } |
| 29237 | } |
| 29238 | |
| 29239 | // Differently-named floats with the same number of bits. |
| 29240 | if (dest_tag == .float and src_tag == .float) { |
| 29241 | const dest_bits = dest_ty.floatBits(target); |
| 29242 | const src_bits = src_ty.floatBits(target); |
| 29243 | if (dest_bits == src_bits) { |
| 29244 | return .{ .ok = .bit_cast }; |
| 29245 | } |
| 29246 | } |
| 29247 | |
| 29248 | // Pointers / Pointer-like Optionals |
| 29249 | if (dest_ty.isPtrAtRuntime(zcu) and src_ty.isPtrAtRuntime(zcu)) { |
| 29250 | return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src); |
| 29251 | } |
| 29252 | |
| 29253 | // Slices |
| 29254 | if (dest_ty.isSlice(zcu) and src_ty.isSlice(zcu)) { |
| 29255 | return try sema.coerceInMemoryAllowedPtrs(block, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src); |
| 29256 | } |
| 29257 | |
| 29258 | // Functions |
| 29259 | if (dest_tag == .@"fn" and src_tag == .@"fn") { |
| 29260 | return try sema.coerceInMemoryAllowedFns(block, dest_ty, src_ty, dest_is_mut, target, dest_src, src_src); |
| 29261 | } |
| 29262 | |
| 29263 | // Error Unions |
| 29264 | if (dest_tag == .error_union and src_tag == .error_union) { |
| 29265 | const dest_payload = dest_ty.errorUnionPayload(zcu); |
| 29266 | const src_payload = src_ty.errorUnionPayload(zcu); |
| 29267 | const payload_strat = switch (try sema.coerceInMemoryAllowed(block, dest_payload, src_payload, dest_is_mut, target, dest_src, src_src, null)) { |
| 29268 | .ok => |strat| strat, |
| 29269 | else => |payload_result| return .{ .error_union_payload = .{ |
| 29270 | .child = try payload_result.dupe(sema.arena), |
| 29271 | .actual = src_payload, |
| 29272 | .wanted = dest_payload, |
| 29273 | } }, |
| 29274 | }; |
| 29275 | switch (try sema.coerceInMemoryAllowed(block, dest_ty.errorUnionSet(zcu), src_ty.errorUnionSet(zcu), dest_is_mut, target, dest_src, src_src, null)) { |
| 29276 | .ok => {}, |
| 29277 | else => |err_set_result| return err_set_result, |
| 29278 | } |
| 29279 | return switch (payload_strat) { |
| 29280 | .same_type => .{ .ok = .error_cast }, |
| 29281 | else => .{ .ok = .none }, |
| 29282 | }; |
| 29283 | } |
| 29284 | |
| 29285 | // Error Sets |
| 29286 | if (dest_tag == .error_set and src_tag == .error_set) { |
| 29287 | switch (try sema.coerceInMemoryAllowedErrorSets(block, dest_ty, src_ty, dest_src, src_src)) { |
| 29288 | .ok => |strat| assert(strat == .error_cast), |
| 29289 | else => |result| return result, |
| 29290 | } |
| 29291 | if (dest_is_mut) { |
| 29292 | // src -> dest is okay, but `dest_is_mut`, so it needs to be allowed in the other direction. |
| 29293 | switch (try sema.coerceInMemoryAllowedErrorSets(block, src_ty, dest_ty, src_src, dest_src)) { |
| 29294 | .ok => |strat| assert(strat == .error_cast), |
| 29295 | else => |result| return result, |
| 29296 | } |
| 29297 | } |
| 29298 | return .{ .ok = .error_cast }; |
| 29299 | } |
| 29300 | |
| 29301 | // Arrays |
| 29302 | if (dest_tag == .array and src_tag == .array) { |
| 29303 | const dest_info = dest_ty.arrayInfo(zcu); |
| 29304 | const src_info = src_ty.arrayInfo(zcu); |
| 29305 | if (dest_info.len != src_info.len) { |
| 29306 | return .{ .array_len = .{ |
| 29307 | .actual = src_info.len, |
| 29308 | .wanted = dest_info.len, |
| 29309 | } }; |
| 29310 | } |
| 29311 | |
| 29312 | const child = try sema.coerceInMemoryAllowed(block, dest_info.elem_type, src_info.elem_type, dest_is_mut, target, dest_src, src_src, null); |
| 29313 | const child_strat = switch (child) { |
| 29314 | .ok => |strat| strat, |
| 29315 | .no_match => |no_match| return .{ .no_match = no_match }, |
| 29316 | else => { |
| 29317 | return .{ .array_elem = .{ |
| 29318 | .child = try child.dupe(sema.arena), |
| 29319 | .actual = src_info.elem_type, |
| 29320 | .wanted = dest_info.elem_type, |
| 29321 | } }; |
| 29322 | }, |
| 29323 | }; |
| 29324 | const ok_sent = (dest_info.sentinel == null and src_info.sentinel == null) or |
| 29325 | (src_info.sentinel != null and |
| 29326 | dest_info.sentinel != null and |
| 29327 | dest_info.sentinel.?.eql( |
| 29328 | try pt.getCoerced(src_info.sentinel.?, dest_info.elem_type), |
| 29329 | dest_info.elem_type, |
| 29330 | zcu, |
| 29331 | )); |
| 29332 | if (!ok_sent) { |
| 29333 | return .{ .array_sentinel = .{ |
| 29334 | .actual = src_info.sentinel orelse Value.@"unreachable", |
| 29335 | .wanted = dest_info.sentinel orelse Value.@"unreachable", |
| 29336 | .ty = dest_info.elem_type, |
| 29337 | } }; |
| 29338 | } |
| 29339 | return .{ .ok = switch (child_strat) { |
| 29340 | .bit_cast => .bit_cast, |
| 29341 | else => .none, |
| 29342 | } }; |
| 29343 | } |
| 29344 | |
| 29345 | // Vectors |
| 29346 | if (dest_tag == .vector and src_tag == .vector) { |
| 29347 | const dest_len = dest_ty.vectorLen(zcu); |
| 29348 | const src_len = src_ty.vectorLen(zcu); |
| 29349 | if (dest_len != src_len) { |
| 29350 | return .{ .vector_len = .{ |
| 29351 | .actual = src_len, |
| 29352 | .wanted = dest_len, |
| 29353 | } }; |
| 29354 | } |
| 29355 | |
| 29356 | const dest_elem_ty = dest_ty.scalarType(zcu); |
| 29357 | const src_elem_ty = src_ty.scalarType(zcu); |
| 29358 | switch (try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src, null)) { |
| 29359 | .ok => |child_strat| return .{ .ok = switch (child_strat) { |
| 29360 | .bit_cast => .bit_cast, |
| 29361 | .ptr_cast => .ptr_cast, |
| 29362 | else => .none, |
| 29363 | } }, |
| 29364 | else => |child_result| return .{ .vector_elem = .{ |
| 29365 | .child = try child_result.dupe(sema.arena), |
| 29366 | .actual = src_elem_ty, |
| 29367 | .wanted = dest_elem_ty, |
| 29368 | } }, |
| 29369 | } |
| 29370 | } |
| 29371 | |
| 29372 | // Optionals |
| 29373 | if (dest_tag == .optional and src_tag == .optional) { |
| 29374 | if (dest_ty.isPtrAtRuntime(zcu) or src_ty.isPtrAtRuntime(zcu)) { |
| 29375 | // Only one is, because we already handled when both are. |
| 29376 | return .{ .optional_shape = .{ |
| 29377 | .actual = src_ty, |
| 29378 | .wanted = dest_ty, |
| 29379 | } }; |
| 29380 | } |
| 29381 | const dest_child_type = dest_ty.optionalChild(zcu); |
| 29382 | const src_child_type = src_ty.optionalChild(zcu); |
| 29383 | |
| 29384 | const child = try sema.coerceInMemoryAllowed(block, dest_child_type, src_child_type, dest_is_mut, target, dest_src, src_src, null); |
| 29385 | if (child != .ok) { |
| 29386 | return .{ .optional_child = .{ |
| 29387 | .child = try child.dupe(sema.arena), |
| 29388 | .actual = src_child_type, |
| 29389 | .wanted = dest_child_type, |
| 29390 | } }; |
| 29391 | } |
| 29392 | |
| 29393 | return .{ .ok = .none }; |
| 29394 | } |
| 29395 | |
| 29396 | // Tuples (with in-memory-coercible fields) |
| 29397 | if (dest_ty.isTuple(zcu) and src_ty.isTuple(zcu)) tuple: { |
| 29398 | if (dest_ty.structFieldCount(zcu) != src_ty.structFieldCount(zcu)) break :tuple; |
| 29399 | const field_count = dest_ty.structFieldCount(zcu); |
| 29400 | for (0..field_count) |field_idx| { |
| 29401 | if (dest_ty.structFieldIsComptime(field_idx, zcu) != src_ty.structFieldIsComptime(field_idx, zcu)) break :tuple; |
| 29402 | const dest_field_ty = dest_ty.fieldType(field_idx, zcu); |
| 29403 | const src_field_ty = src_ty.fieldType(field_idx, zcu); |
| 29404 | const field = try sema.coerceInMemoryAllowed(block, dest_field_ty, src_field_ty, dest_is_mut, target, dest_src, src_src, null); |
| 29405 | if (field != .ok) break :tuple; |
| 29406 | } |
| 29407 | return .{ .ok = .none }; |
| 29408 | } |
| 29409 | |
| 29410 | return .{ .no_match = .{ |
| 29411 | .actual = dest_ty, |
| 29412 | .wanted = src_ty, |
| 29413 | } }; |
| 29414 | } |
| 29415 | |
| 29416 | fn coerceInMemoryAllowedErrorSets( |
| 29417 | sema: *Sema, |
| 29418 | block: *Block, |
| 29419 | dest_ty: Type, |
| 29420 | src_ty: Type, |
| 29421 | dest_src: LazySrcLoc, |
| 29422 | src_src: LazySrcLoc, |
| 29423 | ) !InMemoryCoercionResult { |
| 29424 | const pt = sema.pt; |
| 29425 | const zcu = pt.zcu; |
| 29426 | const gpa = sema.gpa; |
| 29427 | const ip = &zcu.intern_pool; |
| 29428 | |
| 29429 | const dest_set: InternPool.Key.ErrorSetType = err_set: switch (dest_ty.toIntern()) { |
| 29430 | .anyerror_type => return .{ .ok = .error_cast }, |
| 29431 | .adhoc_inferred_error_set_type => { |
| 29432 | // We are trying to coerce an error set to the current function's |
| 29433 | // inferred error set. |
| 29434 | const dst_ies = sema.fn_ret_ty_ies.?; |
| 29435 | try dst_ies.addErrorSet(src_ty, ip, sema.arena); |
| 29436 | return .{ .ok = .error_cast }; |
| 29437 | }, |
| 29438 | else => |err_set_ty| switch (ip.indexToKey(err_set_ty)) { |
| 29439 | .inferred_error_set_type => |func_index| { |
| 29440 | if (sema.fn_ret_ty_ies) |dst_ies| { |
| 29441 | if (dst_ies.func == func_index) { |
| 29442 | // We are trying to coerce an error set to the current function's |
| 29443 | // inferred error set. |
| 29444 | try dst_ies.addErrorSet(src_ty, ip, sema.arena); |
| 29445 | return .{ .ok = .error_cast }; |
| 29446 | } |
| 29447 | } |
| 29448 | try sema.ensureFuncIesResolved(block, dest_src, func_index); |
| 29449 | continue :err_set ip.funcIesResolvedUnordered(func_index); |
| 29450 | }, |
| 29451 | .error_set_type => |err_set| err_set, |
| 29452 | else => unreachable, |
| 29453 | }, |
| 29454 | }; |
| 29455 | |
| 29456 | const src_names: InternPool.NullTerminatedString.Slice = err_set: switch (src_ty.toIntern()) { |
| 29457 | .anyerror_type => return .from_anyerror, |
| 29458 | else => |err_set_ty| switch (ip.indexToKey(err_set_ty)) { |
| 29459 | .inferred_error_set_type => |func_index| { |
| 29460 | try sema.ensureFuncIesResolved(block, src_src, func_index); |
| 29461 | continue :err_set ip.funcIesResolvedUnordered(func_index); |
| 29462 | }, |
| 29463 | .error_set_type => |err_set| err_set.names, |
| 29464 | else => unreachable, |
| 29465 | }, |
| 29466 | }; |
| 29467 | |
| 29468 | var missing_error_buf: std.ArrayList(InternPool.NullTerminatedString) = .empty; |
| 29469 | defer missing_error_buf.deinit(gpa); |
| 29470 | |
| 29471 | for (src_names.get(ip)) |name| { |
| 29472 | if (dest_set.nameIndex(ip, name) == null) { |
| 29473 | try missing_error_buf.append(gpa, name); |
| 29474 | } |
| 29475 | } |
| 29476 | |
| 29477 | if (missing_error_buf.items.len != 0) { |
| 29478 | return .{ .missing_error = try sema.arena.dupe( |
| 29479 | InternPool.NullTerminatedString, |
| 29480 | missing_error_buf.items, |
| 29481 | ) }; |
| 29482 | } |
| 29483 | |
| 29484 | return .{ .ok = .error_cast }; |
| 29485 | } |
| 29486 | |
| 29487 | fn coerceInMemoryAllowedFns( |
| 29488 | sema: *Sema, |
| 29489 | block: *Block, |
| 29490 | dest_ty: Type, |
| 29491 | src_ty: Type, |
| 29492 | /// If set, the coercion must be valid in both directions. |
| 29493 | dest_is_mut: bool, |
| 29494 | target: *const std.Target, |
| 29495 | dest_src: LazySrcLoc, |
| 29496 | src_src: LazySrcLoc, |
| 29497 | ) !InMemoryCoercionResult { |
| 29498 | const pt = sema.pt; |
| 29499 | const zcu = pt.zcu; |
| 29500 | const ip = &zcu.intern_pool; |
| 29501 | |
| 29502 | const dest_info = zcu.typeToFunc(dest_ty).?; |
| 29503 | const src_info = zcu.typeToFunc(src_ty).?; |
| 29504 | |
| 29505 | { |
| 29506 | if (dest_info.is_var_args != src_info.is_var_args) { |
| 29507 | return .{ .fn_var_args = dest_info.is_var_args }; |
| 29508 | } |
| 29509 | |
| 29510 | const callconv_ok = callconvCoerceAllowed(target, src_info.cc, dest_info.cc) and |
| 29511 | (!dest_is_mut or callconvCoerceAllowed(target, dest_info.cc, src_info.cc)); |
| 29512 | |
| 29513 | if (!callconv_ok) { |
| 29514 | return .{ .fn_cc = .{ |
| 29515 | .actual = src_info.cc, |
| 29516 | .wanted = dest_info.cc, |
| 29517 | } }; |
| 29518 | } |
| 29519 | |
| 29520 | try sema.ensureLayoutResolved(src_ty, src_src, .coerce); |
| 29521 | try sema.ensureLayoutResolved(dest_ty, dest_src, .coerce); |
| 29522 | const src_is_runtime = src_ty.fnHasRuntimeBits(zcu); |
| 29523 | const dest_is_runtime = dest_ty.fnHasRuntimeBits(zcu); |
| 29524 | if (src_is_runtime != dest_is_runtime) return .{ .fn_generic = !dest_is_runtime }; |
| 29525 | |
| 29526 | if (!switch (src_info.return_type) { |
| 29527 | .generic_poison_type => true, |
| 29528 | .noreturn_type => !dest_is_mut, |
| 29529 | else => false, |
| 29530 | }) { |
| 29531 | const rt = try sema.coerceInMemoryAllowed( |
| 29532 | block, |
| 29533 | .fromInterned(dest_info.return_type), |
| 29534 | .fromInterned(src_info.return_type), |
| 29535 | dest_is_mut, |
| 29536 | target, |
| 29537 | dest_src, |
| 29538 | src_src, |
| 29539 | null, |
| 29540 | ); |
| 29541 | if (rt != .ok) return .{ .fn_return_type = .{ |
| 29542 | .child = try rt.dupe(sema.arena), |
| 29543 | .actual = .fromInterned(src_info.return_type), |
| 29544 | .wanted = .fromInterned(dest_info.return_type), |
| 29545 | } }; |
| 29546 | } |
| 29547 | } |
| 29548 | |
| 29549 | const params_len = params_len: { |
| 29550 | if (dest_info.param_types.len != src_info.param_types.len) { |
| 29551 | return .{ .fn_param_count = .{ |
| 29552 | .actual = src_info.param_types.len, |
| 29553 | .wanted = dest_info.param_types.len, |
| 29554 | } }; |
| 29555 | } |
| 29556 | |
| 29557 | if (dest_info.noalias_bits != src_info.noalias_bits) { |
| 29558 | return .{ .fn_param_noalias = .{ |
| 29559 | .actual = src_info.noalias_bits, |
| 29560 | .wanted = dest_info.noalias_bits, |
| 29561 | } }; |
| 29562 | } |
| 29563 | |
| 29564 | break :params_len dest_info.param_types.len; |
| 29565 | }; |
| 29566 | |
| 29567 | for (0..params_len) |param_i| { |
| 29568 | const dest_param_ty: Type = .fromInterned(dest_info.param_types.get(ip)[param_i]); |
| 29569 | const src_param_ty: Type = .fromInterned(src_info.param_types.get(ip)[param_i]); |
| 29570 | |
| 29571 | comptime_param: { |
| 29572 | const src_is_comptime = src_info.paramIsComptime(@intCast(param_i)); |
| 29573 | const dest_is_comptime = dest_info.paramIsComptime(@intCast(param_i)); |
| 29574 | if (src_is_comptime == dest_is_comptime) break :comptime_param; |
| 29575 | if (!dest_is_mut and src_is_comptime and !dest_is_comptime and dest_param_ty.comptimeOnly(zcu)) { |
| 29576 | // A parameter which is marked `comptime` can drop that annotation if the type is comptime-only. |
| 29577 | // The function remains generic, and the parameter is going to be comptime-resolved either way, |
| 29578 | // so this just affects whether or not the argument is comptime-evaluated at the call site. |
| 29579 | break :comptime_param; |
| 29580 | } |
| 29581 | return .{ .fn_param_comptime = .{ |
| 29582 | .index = param_i, |
| 29583 | .wanted = dest_is_comptime, |
| 29584 | } }; |
| 29585 | } |
| 29586 | |
| 29587 | if (!src_param_ty.isGenericPoison() and !dest_param_ty.isGenericPoison()) { |
| 29588 | // Note: Cast direction is reversed here. |
| 29589 | const param = try sema.coerceInMemoryAllowed(block, src_param_ty, dest_param_ty, dest_is_mut, target, dest_src, src_src, null); |
| 29590 | if (param != .ok) { |
| 29591 | return .{ .fn_param = .{ |
| 29592 | .child = try param.dupe(sema.arena), |
| 29593 | .actual = src_param_ty, |
| 29594 | .wanted = dest_param_ty, |
| 29595 | .index = param_i, |
| 29596 | } }; |
| 29597 | } |
| 29598 | } |
| 29599 | } |
| 29600 | |
| 29601 | return .{ .ok = .none }; |
| 29602 | } |
| 29603 | |
| 29604 | fn callconvCoerceAllowed( |
| 29605 | target: *const std.Target, |
| 29606 | src_cc: std.lang.CallingConvention, |
| 29607 | dest_cc: std.lang.CallingConvention, |
| 29608 | ) bool { |
| 29609 | const Tag = std.lang.CallingConvention.Tag; |
| 29610 | if (@as(Tag, src_cc) != @as(Tag, dest_cc)) return false; |
| 29611 | |
| 29612 | switch (src_cc) { |
| 29613 | inline else => |src_data, tag| { |
| 29614 | const dest_data = @field(dest_cc, @tagName(tag)); |
| 29615 | if (@TypeOf(src_data) != void and @hasField(@TypeOf(src_data), "incoming_stack_alignment")) { |
| 29616 | const default_stack_align = target.stackAlignment(); |
| 29617 | const src_stack_align = src_data.incoming_stack_alignment orelse default_stack_align; |
| 29618 | const dest_stack_align = dest_data.incoming_stack_alignment orelse default_stack_align; |
| 29619 | if (dest_stack_align < src_stack_align) return false; |
| 29620 | } |
| 29621 | switch (@TypeOf(src_data)) { |
| 29622 | void, std.lang.CallingConvention.CommonOptions => {}, |
| 29623 | std.lang.CallingConvention.X86RegparmOptions => { |
| 29624 | if (src_data.register_params != dest_data.register_params) return false; |
| 29625 | }, |
| 29626 | std.lang.CallingConvention.ArcInterruptOptions => { |
| 29627 | if (src_data.type != dest_data.type) return false; |
| 29628 | }, |
| 29629 | std.lang.CallingConvention.ArmInterruptOptions => { |
| 29630 | if (src_data.type != dest_data.type) return false; |
| 29631 | }, |
| 29632 | std.lang.CallingConvention.MicroblazeInterruptOptions => { |
| 29633 | if (src_data.type != dest_data.type) return false; |
| 29634 | }, |
| 29635 | std.lang.CallingConvention.MipsInterruptOptions => { |
| 29636 | if (src_data.mode != dest_data.mode) return false; |
| 29637 | }, |
| 29638 | std.lang.CallingConvention.RiscvInterruptOptions => { |
| 29639 | if (src_data.mode != dest_data.mode) return false; |
| 29640 | }, |
| 29641 | std.lang.CallingConvention.ShInterruptOptions => { |
| 29642 | if (src_data.save != dest_data.save) return false; |
| 29643 | }, |
| 29644 | std.lang.CallingConvention.SpirvKernelOptions, |
| 29645 | std.lang.CallingConvention.SpirvFragmentOptions, |
| 29646 | std.lang.CallingConvention.SpirvMeshOptions, |
| 29647 | => {}, |
| 29648 | else => comptime unreachable, |
| 29649 | } |
| 29650 | }, |
| 29651 | } |
| 29652 | return true; |
| 29653 | } |
| 29654 | |
| 29655 | fn coerceInMemoryAllowedPtrs( |
| 29656 | sema: *Sema, |
| 29657 | block: *Block, |
| 29658 | dest_ty: Type, |
| 29659 | src_ty: Type, |
| 29660 | /// If set, the coercion must be valid in both directions. |
| 29661 | dest_is_mut: bool, |
| 29662 | target: *const std.Target, |
| 29663 | dest_src: LazySrcLoc, |
| 29664 | src_src: LazySrcLoc, |
| 29665 | ) !InMemoryCoercionResult { |
| 29666 | const pt = sema.pt; |
| 29667 | const zcu = pt.zcu; |
| 29668 | const comp = zcu.comp; |
| 29669 | const gpa = comp.gpa; |
| 29670 | const io = comp.io; |
| 29671 | |
| 29672 | const dest_info = dest_ty.ptrInfo(zcu); |
| 29673 | const src_info = src_ty.ptrInfo(zcu); |
| 29674 | |
| 29675 | const ok_ptr_size = src_info.flags.size == dest_info.flags.size or |
| 29676 | src_info.flags.size == .c or dest_info.flags.size == .c; |
| 29677 | if (!ok_ptr_size) { |
| 29678 | return .{ .ptr_size = .{ |
| 29679 | .actual = src_info.flags.size, |
| 29680 | .wanted = dest_info.flags.size, |
| 29681 | } }; |
| 29682 | } |
| 29683 | |
| 29684 | const ok_const = src_info.flags.is_const == dest_info.flags.is_const or |
| 29685 | (!dest_is_mut and dest_info.flags.is_const); |
| 29686 | |
| 29687 | if (!ok_const) return .{ .ptr_const = .{ |
| 29688 | .actual = src_ty, |
| 29689 | .wanted = dest_ty, |
| 29690 | } }; |
| 29691 | |
| 29692 | const ok_volatile = src_info.flags.is_volatile == dest_info.flags.is_volatile or |
| 29693 | (!dest_is_mut and dest_info.flags.is_volatile); |
| 29694 | |
| 29695 | if (!ok_volatile) return .{ .ptr_volatile = .{ |
| 29696 | .actual = src_ty, |
| 29697 | .wanted = dest_ty, |
| 29698 | } }; |
| 29699 | |
| 29700 | const dest_allowzero = dest_ty.ptrAllowsZero(zcu); |
| 29701 | const src_allowzero = src_ty.ptrAllowsZero(zcu); |
| 29702 | const ok_allowzero = src_allowzero == dest_allowzero or |
| 29703 | (!dest_is_mut and dest_allowzero); |
| 29704 | |
| 29705 | if (!ok_allowzero) return .{ .ptr_allowzero = .{ |
| 29706 | .actual = src_ty, |
| 29707 | .wanted = dest_ty, |
| 29708 | } }; |
| 29709 | |
| 29710 | if (dest_info.flags.address_space != src_info.flags.address_space) { |
| 29711 | return .{ .ptr_addrspace = .{ |
| 29712 | .actual = src_info.flags.address_space, |
| 29713 | .wanted = dest_info.flags.address_space, |
| 29714 | } }; |
| 29715 | } |
| 29716 | |
| 29717 | const dest_child: Type = .fromInterned(dest_info.child); |
| 29718 | const src_child: Type = .fromInterned(src_info.child); |
| 29719 | const child = try sema.coerceInMemoryAllowed( |
| 29720 | block, |
| 29721 | dest_child, |
| 29722 | src_child, |
| 29723 | // We must also include `dest_is_mut`. |
| 29724 | // Otherwise, this code is valid: |
| 29725 | // |
| 29726 | // const b: B = ...; |
| 29727 | // var pa: *const A = undefined; |
| 29728 | // const ppa: **const A = &pa; |
| 29729 | // const ppb: **const B = ppa; // <-- this is what that allows |
| 29730 | // ppb.* = &b; |
| 29731 | // const a: A = pa.*; |
| 29732 | // |
| 29733 | // ...effectively performing an in-memory coercion from B to A. |
| 29734 | dest_is_mut or !dest_info.flags.is_const, |
| 29735 | target, |
| 29736 | dest_src, |
| 29737 | src_src, |
| 29738 | null, |
| 29739 | ); |
| 29740 | if (child != .ok) allow: { |
| 29741 | // As a special case, we also allow coercing `*[n:s]T` to `*[n]T`, akin to dropping the sentinel from a slice. |
| 29742 | // `*[n:s]T` cannot coerce in memory to `*[n]T` since they have different sizes. |
| 29743 | // |
| 29744 | // We must once again include `dest_is_mut` because `**[n:s]T -> **[n]T` |
| 29745 | // is not allowed, as it would make it possible to assign an illegal value |
| 29746 | // to the sentinel-terminated side. |
| 29747 | if (!dest_is_mut and src_child.zigTypeTag(zcu) == .array and dest_child.zigTypeTag(zcu) == .array and |
| 29748 | src_child.arrayLen(zcu) == dest_child.arrayLen(zcu) and |
| 29749 | src_child.sentinel(zcu) != null and dest_child.sentinel(zcu) == null and |
| 29750 | .ok == try sema.coerceInMemoryAllowed(block, dest_child.childType(zcu), src_child.childType(zcu), !dest_info.flags.is_const, target, dest_src, src_src, null)) |
| 29751 | { |
| 29752 | break :allow; |
| 29753 | } |
| 29754 | return .{ .ptr_child = .{ |
| 29755 | .child = try child.dupe(sema.arena), |
| 29756 | .actual = .fromInterned(src_info.child), |
| 29757 | .wanted = .fromInterned(dest_info.child), |
| 29758 | } }; |
| 29759 | } |
| 29760 | |
| 29761 | if (src_info.packed_offset.host_size != dest_info.packed_offset.host_size or |
| 29762 | src_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) |
| 29763 | { |
| 29764 | return .{ .ptr_bit_range = .{ |
| 29765 | .actual_host = src_info.packed_offset.host_size, |
| 29766 | .wanted_host = dest_info.packed_offset.host_size, |
| 29767 | .actual_offset = src_info.packed_offset.bit_offset, |
| 29768 | .wanted_offset = dest_info.packed_offset.bit_offset, |
| 29769 | } }; |
| 29770 | } |
| 29771 | |
| 29772 | const sentinel_ok = ok: { |
| 29773 | const ss = src_info.sentinel; |
| 29774 | const ds = dest_info.sentinel; |
| 29775 | if (ss == .none and ds == .none) break :ok true; |
| 29776 | if (ss != .none and ds != .none) { |
| 29777 | if (ds == try zcu.intern_pool.getCoerced(gpa, io, pt.tid, ss, dest_info.child)) break :ok true; |
| 29778 | } |
| 29779 | if (src_info.flags.size == .c) break :ok true; |
| 29780 | if (!dest_is_mut and dest_info.sentinel == .none) break :ok true; |
| 29781 | break :ok false; |
| 29782 | }; |
| 29783 | |
| 29784 | if (!sentinel_ok) { |
| 29785 | return .{ .ptr_sentinel = .{ |
| 29786 | .actual = switch (src_info.sentinel) { |
| 29787 | .none => Value.@"unreachable", |
| 29788 | else => Value.fromInterned(src_info.sentinel), |
| 29789 | }, |
| 29790 | .wanted = switch (dest_info.sentinel) { |
| 29791 | .none => Value.@"unreachable", |
| 29792 | else => Value.fromInterned(dest_info.sentinel), |
| 29793 | }, |
| 29794 | .ty = .fromInterned(dest_info.child), |
| 29795 | } }; |
| 29796 | } |
| 29797 | |
| 29798 | // If both pointers have alignment 0, it means they both want ABI alignment. |
| 29799 | // In this case, if they share the same child type, no need to resolve |
| 29800 | // pointee type alignment. Otherwise both pointee types must have their alignment |
| 29801 | // resolved and we compare the alignment numerically. |
| 29802 | if (src_info.flags.alignment != .none or dest_info.flags.alignment != .none or |
| 29803 | dest_info.child != src_info.child) |
| 29804 | { |
| 29805 | const src_align = if (src_info.flags.alignment == .none) a: { |
| 29806 | try sema.ensureLayoutResolved(src_child, src_src, .align_check); |
| 29807 | break :a src_child.abiAlignment(zcu); |
| 29808 | } else src_info.flags.alignment; |
| 29809 | const dest_align = if (dest_info.flags.alignment == .none) a: { |
| 29810 | try sema.ensureLayoutResolved(dest_child, dest_src, .align_check); |
| 29811 | break :a dest_child.abiAlignment(zcu); |
| 29812 | } else dest_info.flags.alignment; |
| 29813 | if (dest_align.compare(if (dest_is_mut) .neq else .gt, src_align)) { |
| 29814 | return .{ .ptr_alignment = .{ |
| 29815 | .actual = src_align, |
| 29816 | .wanted = dest_align, |
| 29817 | } }; |
| 29818 | } |
| 29819 | } |
| 29820 | |
| 29821 | return .{ .ok = .ptr_cast }; |
| 29822 | } |
| 29823 | |
| 29824 | fn coerceVarArgParam( |
| 29825 | sema: *Sema, |
| 29826 | block: *Block, |
| 29827 | inst: Air.Inst.Ref, |
| 29828 | inst_src: LazySrcLoc, |
| 29829 | ) !Air.Inst.Ref { |
| 29830 | if (block.is_typeof) return inst; |
| 29831 | |
| 29832 | const pt = sema.pt; |
| 29833 | const zcu = pt.zcu; |
| 29834 | const uncasted_ty = sema.typeOf(inst); |
| 29835 | const coerced = switch (uncasted_ty.zigTypeTag(zcu)) { |
| 29836 | // TODO consider casting to c_int/f64 if they fit |
| 29837 | .comptime_int, .comptime_float => return sema.fail( |
| 29838 | block, |
| 29839 | inst_src, |
| 29840 | "integer and float literals passed to variadic function must be casted to a fixed-size number type", |
| 29841 | .{}, |
| 29842 | ), |
| 29843 | .@"fn" => fn_ptr: { |
| 29844 | const fn_val = sema.resolveValue(inst).?; |
| 29845 | const fn_nav = zcu.funcInfo(fn_val.toIntern()).owner_nav; |
| 29846 | break :fn_ptr try sema.analyzeNavRef(block, inst_src, fn_nav); |
| 29847 | }, |
| 29848 | .array => return sema.fail(block, inst_src, "arrays must be passed by reference to variadic function", .{}), |
| 29849 | .float => float: { |
| 29850 | const target = zcu.getTarget(); |
| 29851 | const double_bits = target.cTypeBitSize(.double) orelse break :float inst; |
| 29852 | const inst_bits = uncasted_ty.floatBits(target); |
| 29853 | if (inst_bits >= double_bits) break :float inst; |
| 29854 | switch (double_bits) { |
| 29855 | 32 => break :float try sema.coerce(block, .f32, inst, inst_src), |
| 29856 | 64 => break :float try sema.coerce(block, .f64, inst, inst_src), |
| 29857 | else => unreachable, |
| 29858 | } |
| 29859 | }, |
| 29860 | else => if (uncasted_ty.isAbiInt(zcu)) int: { |
| 29861 | if (!uncasted_ty.validateExtern(.param_ty, zcu)) break :int inst; |
| 29862 | const target = zcu.getTarget(); |
| 29863 | const uncasted_info = uncasted_ty.intInfo(zcu); |
| 29864 | if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) { |
| 29865 | .signed => .int, |
| 29866 | .unsigned => .uint, |
| 29867 | }) orelse break :int inst) break :int try sema.coerce(block, switch (uncasted_info.signedness) { |
| 29868 | .signed => .c_int, |
| 29869 | .unsigned => .c_uint, |
| 29870 | }, inst, inst_src); |
| 29871 | if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) { |
| 29872 | .signed => .long, |
| 29873 | .unsigned => .ulong, |
| 29874 | }).?) break :int try sema.coerce(block, switch (uncasted_info.signedness) { |
| 29875 | .signed => .c_long, |
| 29876 | .unsigned => .c_ulong, |
| 29877 | }, inst, inst_src); |
| 29878 | if (uncasted_info.bits <= target.cTypeBitSize(switch (uncasted_info.signedness) { |
| 29879 | .signed => .longlong, |
| 29880 | .unsigned => .ulonglong, |
| 29881 | }).?) break :int try sema.coerce(block, switch (uncasted_info.signedness) { |
| 29882 | .signed => .c_longlong, |
| 29883 | .unsigned => .c_ulonglong, |
| 29884 | }, inst, inst_src); |
| 29885 | break :int inst; |
| 29886 | } else inst, |
| 29887 | }; |
| 29888 | |
| 29889 | const coerced_ty = sema.typeOf(coerced); |
| 29890 | if (!coerced_ty.validateExtern(.param_ty, zcu)) { |
| 29891 | const msg = msg: { |
| 29892 | const msg = try sema.errMsg(inst_src, "cannot pass '{f}' to variadic function", .{coerced_ty.fmt(pt)}); |
| 29893 | errdefer msg.destroy(sema.gpa); |
| 29894 | |
| 29895 | try sema.explainWhyTypeIsNotExtern(msg, inst_src, coerced_ty, .param_ty); |
| 29896 | |
| 29897 | try sema.addDeclaredHereNote(msg, coerced_ty); |
| 29898 | break :msg msg; |
| 29899 | }; |
| 29900 | return sema.failWithOwnedErrorMsg(block, msg); |
| 29901 | } |
| 29902 | return coerced; |
| 29903 | } |
| 29904 | |
| 29905 | // TODO migrate callsites to use storePtr2 instead. |
| 29906 | fn storePtr( |
| 29907 | sema: *Sema, |
| 29908 | block: *Block, |
| 29909 | src: LazySrcLoc, |
| 29910 | ptr: Air.Inst.Ref, |
| 29911 | uncasted_operand: Air.Inst.Ref, |
| 29912 | ) CompileError!void { |
| 29913 | const air_tag: Air.Inst.Tag = if (block.wantSafety()) .store_safe else .store; |
| 29914 | return sema.storePtr2(block, src, ptr, src, uncasted_operand, src, air_tag); |
| 29915 | } |
| 29916 | |
| 29917 | fn storePtr2( |
| 29918 | sema: *Sema, |
| 29919 | block: *Block, |
| 29920 | src: LazySrcLoc, |
| 29921 | ptr: Air.Inst.Ref, |
| 29922 | ptr_src: LazySrcLoc, |
| 29923 | uncasted_operand: Air.Inst.Ref, |
| 29924 | operand_src: LazySrcLoc, |
| 29925 | air_tag: Air.Inst.Tag, |
| 29926 | ) CompileError!void { |
| 29927 | const pt = sema.pt; |
| 29928 | const zcu = pt.zcu; |
| 29929 | const ptr_ty = sema.typeOf(ptr); |
| 29930 | if (ptr_ty.isConstPtr(zcu)) |
| 29931 | return sema.fail(block, ptr_src, "cannot assign to constant", .{}); |
| 29932 | |
| 29933 | const elem_ty = ptr_ty.childType(zcu); |
| 29934 | |
| 29935 | const is_ret = air_tag == .ret_ptr; |
| 29936 | |
| 29937 | const operand = sema.coerceExtra(block, elem_ty, uncasted_operand, operand_src, .{ .is_ret = is_ret }) catch |err| switch (err) { |
| 29938 | error.NotCoercible => unreachable, |
| 29939 | else => |e| return e, |
| 29940 | }; |
| 29941 | const maybe_operand_val = sema.resolveValue(operand); |
| 29942 | |
| 29943 | const comptime_only = switch (elem_ty.classify(zcu)) { |
| 29944 | .no_possible_value => unreachable, // the coercion should have failed |
| 29945 | .one_possible_value => return, // no actual store operation is necessary |
| 29946 | .runtime => false, |
| 29947 | .partially_comptime, .fully_comptime => true, |
| 29948 | }; |
| 29949 | |
| 29950 | const runtime_src = rs: { |
| 29951 | const ptr_val = try sema.resolveDefinedValue(block, ptr_src, ptr) orelse break :rs ptr_src; |
| 29952 | if (!sema.isComptimeMutablePtr(ptr_val)) break :rs ptr_src; |
| 29953 | const operand_val = maybe_operand_val orelse return sema.fail(block, ptr_src, "cannot store runtime value in compile time variable", .{}); |
| 29954 | return sema.storePtrVal(block, src, ptr_val, operand_val, elem_ty); |
| 29955 | }; |
| 29956 | |
| 29957 | // We're performing the store at runtime, so the pointee type must not be comptime-only. |
| 29958 | if (comptime_only) return sema.failWithOwnedErrorMsg(block, msg: { |
| 29959 | const msg = try sema.errMsg(src, "cannot store comptime-only type '{f}' at runtime", .{elem_ty.fmt(pt)}); |
| 29960 | errdefer msg.destroy(zcu.gpa); |
| 29961 | try sema.errNote(ptr_src, msg, "operation is runtime due to this pointer", .{}); |
| 29962 | break :msg msg; |
| 29963 | }); |
| 29964 | |
| 29965 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 29966 | |
| 29967 | const store_inst = if (is_ret) |
| 29968 | try block.addBinOp(.store, ptr, operand) |
| 29969 | else |
| 29970 | try block.addBinOp(air_tag, ptr, operand); |
| 29971 | |
| 29972 | try sema.checkComptimeKnownStore(block, store_inst, operand_src); |
| 29973 | |
| 29974 | return; |
| 29975 | } |
| 29976 | |
| 29977 | /// Given an AIR store instruction, checks whether we are performing a |
| 29978 | /// comptime-known store to a local alloc, and updates `maybe_comptime_allocs` |
| 29979 | /// accordingly. |
| 29980 | /// Handles calling `validateRuntimeValue` if the store is runtime for any reason. |
| 29981 | fn checkComptimeKnownStore(sema: *Sema, block: *Block, store_inst_ref: Air.Inst.Ref, store_src: LazySrcLoc) !void { |
| 29982 | const store_inst = store_inst_ref.toIndex().?; |
| 29983 | const inst_data = sema.air_instructions.items(.data)[@backingInt(store_inst)].bin_op; |
| 29984 | const operand = inst_data.rhs; |
| 29985 | |
| 29986 | known: { |
| 29987 | const ptr = inst_data.lhs.toIndex() orelse { |
| 29988 | const ptr_val: Value = .fromInterned(inst_data.lhs.toInterned().?); |
| 29989 | if (sema.isComptimeMutablePtr(ptr_val)) { |
| 29990 | return; |
| 29991 | } else { |
| 29992 | break :known; |
| 29993 | } |
| 29994 | }; |
| 29995 | |
| 29996 | const maybe_base_alloc = sema.base_allocs.get(ptr) orelse break :known; |
| 29997 | const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(maybe_base_alloc) orelse break :known; |
| 29998 | |
| 29999 | if (sema.resolveValue(operand) != null and |
| 30000 | block.runtime_index == maybe_comptime_alloc.runtime_index) |
| 30001 | { |
| 30002 | try maybe_comptime_alloc.stores.append(sema.arena, .{ |
| 30003 | .inst = store_inst, |
| 30004 | .src = store_src, |
| 30005 | }); |
| 30006 | return; |
| 30007 | } |
| 30008 | |
| 30009 | // We're newly discovering that this alloc is runtime-known. |
| 30010 | try sema.markMaybeComptimeAllocRuntime(block, maybe_base_alloc); |
| 30011 | } |
| 30012 | |
| 30013 | try sema.validateRuntimeValue(block, store_src, operand); |
| 30014 | } |
| 30015 | |
| 30016 | /// Given an AIR instruction transforming a pointer (struct_field_ptr, |
| 30017 | /// ptr_elem_ptr, bitcast, etc), checks whether the base pointer refers to a |
| 30018 | /// local alloc, and updates `base_allocs` accordingly. |
| 30019 | fn checkKnownAllocPtr(sema: *Sema, block: *Block, base_ptr: Air.Inst.Ref, new_ptr: Air.Inst.Ref) !void { |
| 30020 | const base_ptr_inst = base_ptr.toIndex() orelse return; |
| 30021 | const new_ptr_inst = new_ptr.toIndex() orelse return; |
| 30022 | const alloc_inst = sema.base_allocs.get(base_ptr_inst) orelse return; |
| 30023 | try sema.base_allocs.put(sema.gpa, new_ptr_inst, alloc_inst); |
| 30024 | |
| 30025 | switch (sema.air_instructions.items(.tag)[@backingInt(new_ptr_inst)]) { |
| 30026 | .optional_payload_ptr_set, .errunion_payload_ptr_set => { |
| 30027 | const maybe_comptime_alloc = sema.maybe_comptime_allocs.getPtr(alloc_inst) orelse return; |
| 30028 | |
| 30029 | // This is functionally a store, since it writes the optional payload bit. |
| 30030 | // Thus, if it is behind a runtime condition, we must mark the alloc as runtime appropriately. |
| 30031 | if (block.runtime_index != maybe_comptime_alloc.runtime_index) { |
| 30032 | return sema.markMaybeComptimeAllocRuntime(block, alloc_inst); |
| 30033 | } |
| 30034 | |
| 30035 | try maybe_comptime_alloc.stores.append(sema.arena, .{ |
| 30036 | .inst = new_ptr_inst, |
| 30037 | .src = LazySrcLoc.unneeded, |
| 30038 | }); |
| 30039 | }, |
| 30040 | .ptr_elem_ptr => { |
| 30041 | const tmp_air = sema.getTmpAir(); |
| 30042 | const pl_idx = tmp_air.instructions.items(.data)[@backingInt(new_ptr_inst)].ty_pl.payload; |
| 30043 | const bin = tmp_air.extraData(Air.Bin, pl_idx).data; |
| 30044 | const index_ref = bin.rhs; |
| 30045 | |
| 30046 | // If the index value is runtime-known, this pointer is also runtime-known, so |
| 30047 | // we must in turn make the alloc value runtime-known. |
| 30048 | if (null == sema.resolveValue(index_ref)) { |
| 30049 | try sema.markMaybeComptimeAllocRuntime(block, alloc_inst); |
| 30050 | } |
| 30051 | }, |
| 30052 | else => {}, |
| 30053 | } |
| 30054 | } |
| 30055 | |
| 30056 | fn markMaybeComptimeAllocRuntime(sema: *Sema, block: *Block, alloc_inst: Air.Inst.Index) CompileError!void { |
| 30057 | const maybe_comptime_alloc = (sema.maybe_comptime_allocs.fetchRemove(alloc_inst) orelse return).value; |
| 30058 | // Since the alloc has been determined to be runtime, we must check that |
| 30059 | // all other stores to it are permitted to be runtime values. |
| 30060 | const slice = maybe_comptime_alloc.stores.slice(); |
| 30061 | for (slice.items(.inst), slice.items(.src)) |other_inst, other_src| { |
| 30062 | if (other_src.offset == .unneeded) { |
| 30063 | switch (sema.air_instructions.items(.tag)[@backingInt(other_inst)]) { |
| 30064 | .set_union_tag, .optional_payload_ptr_set, .errunion_payload_ptr_set => continue, |
| 30065 | else => unreachable, // assertion failure |
| 30066 | } |
| 30067 | } |
| 30068 | const other_data = sema.air_instructions.items(.data)[@backingInt(other_inst)].bin_op; |
| 30069 | const other_operand = other_data.rhs; |
| 30070 | try sema.validateRuntimeValue(block, other_src, other_operand); |
| 30071 | } |
| 30072 | } |
| 30073 | |
| 30074 | /// Call when you have Value objects rather than Air instructions, and you want to |
| 30075 | /// assert the store must be done at comptime. |
| 30076 | fn storePtrVal( |
| 30077 | sema: *Sema, |
| 30078 | block: *Block, |
| 30079 | src: LazySrcLoc, |
| 30080 | ptr_val: Value, |
| 30081 | operand_val: Value, |
| 30082 | operand_ty: Type, |
| 30083 | ) !void { |
| 30084 | const pt = sema.pt; |
| 30085 | const zcu = pt.zcu; |
| 30086 | const ip = &zcu.intern_pool; |
| 30087 | // TODO: audit use sites to eliminate this coercion |
| 30088 | const coerced_operand_val = try pt.getCoerced(operand_val, operand_ty); |
| 30089 | // TODO: audit use sites to eliminate this coercion |
| 30090 | const ptr_ty = try pt.ptrType(info: { |
| 30091 | var info = ptr_val.typeOf(zcu).ptrInfo(zcu); |
| 30092 | info.child = operand_ty.toIntern(); |
| 30093 | break :info info; |
| 30094 | }); |
| 30095 | const coerced_ptr_val = try pt.getCoerced(ptr_val, ptr_ty); |
| 30096 | |
| 30097 | switch (try sema.storeComptimePtr(block, src, coerced_ptr_val, coerced_operand_val)) { |
| 30098 | .success => {}, |
| 30099 | .runtime_store => unreachable, // use sites check this |
| 30100 | // TODO use failWithInvalidComptimeFieldStore |
| 30101 | .comptime_field_mismatch => return sema.fail( |
| 30102 | block, |
| 30103 | src, |
| 30104 | "value stored in comptime field does not match the default value of the field", |
| 30105 | .{}, |
| 30106 | ), |
| 30107 | .undef => return sema.failWithUseOfUndef(block, src, null), |
| 30108 | .err_payload => |err_name| return sema.fail(block, src, "attempt to unwrap error: {f}", .{err_name.fmt(ip)}), |
| 30109 | .null_payload => return sema.fail(block, src, "attempt to use null value", .{}), |
| 30110 | .inactive_union_field => return sema.fail(block, src, "access of inactive union field", .{}), |
| 30111 | .needed_well_defined => |ty| return sema.fail( |
| 30112 | block, |
| 30113 | src, |
| 30114 | "comptime dereference requires '{f}' to have a well-defined layout", |
| 30115 | .{ty.fmt(pt)}, |
| 30116 | ), |
| 30117 | .out_of_bounds => |ty| return sema.fail( |
| 30118 | block, |
| 30119 | src, |
| 30120 | "dereference of '{f}' exceeds bounds of containing decl of type '{f}'", |
| 30121 | .{ ptr_ty.fmt(pt), ty.fmt(pt) }, |
| 30122 | ), |
| 30123 | .exceeds_host_size => return sema.fail(block, src, "bit-pointer target exceeds host size", .{}), |
| 30124 | } |
| 30125 | } |
| 30126 | |
| 30127 | /// Asserts that the layout of `dest_ty` is already resolved. |
| 30128 | fn bitCastUnchecked( |
| 30129 | sema: *Sema, |
| 30130 | block: *Block, |
| 30131 | dest_ty: Type, |
| 30132 | inst: Air.Inst.Ref, |
| 30133 | ) CompileError!Air.Inst.Ref { |
| 30134 | const zcu = sema.pt.zcu; |
| 30135 | const old_ty = sema.typeOf(inst); |
| 30136 | |
| 30137 | old_ty.assertHasLayout(zcu); |
| 30138 | dest_ty.assertHasLayout(zcu); |
| 30139 | |
| 30140 | assert(old_ty.hasBitRepresentation(zcu)); |
| 30141 | assert(dest_ty.hasBitRepresentation(zcu)); |
| 30142 | assert(old_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer); |
| 30143 | assert(dest_ty.scalarType(zcu).zigTypeTag(zcu) != .pointer); |
| 30144 | assert(old_ty.bitSize(zcu) == dest_ty.bitSize(zcu)); |
| 30145 | |
| 30146 | if (sema.resolveValue(inst)) |val| { |
| 30147 | return .fromValue(try sema.bitCastVal(val, dest_ty)); |
| 30148 | } |
| 30149 | |
| 30150 | return block.addTyOp(.bit_cast, dest_ty, inst); |
| 30151 | } |
| 30152 | |
| 30153 | /// Supports only types which `@bitCast` supports, so pointers are *not* supported. |
| 30154 | pub fn bitCastVal( |
| 30155 | sema: *Sema, |
| 30156 | val: Value, |
| 30157 | dest_ty: Type, |
| 30158 | ) Allocator.Error!Value { |
| 30159 | const pt = sema.pt; |
| 30160 | const zcu = pt.zcu; |
| 30161 | const bit_size = dest_ty.bitSize(zcu); |
| 30162 | assert(val.typeOf(zcu).bitSize(zcu) == bit_size); |
| 30163 | if (val.isUndef(zcu)) { |
| 30164 | return pt.undefValue(dest_ty); |
| 30165 | } else { |
| 30166 | const buf = try sema.arena.alloc(u8, @intCast(@divCeil(bit_size, 8))); |
| 30167 | @memset(buf, 0); |
| 30168 | val.writeToPackedMemory(zcu, buf, 0); |
| 30169 | return .readFromPackedMemory(dest_ty, pt, buf, 0); |
| 30170 | } |
| 30171 | } |
| 30172 | |
| 30173 | fn errorCastUnchecked( |
| 30174 | sema: *Sema, |
| 30175 | block: *Block, |
| 30176 | dest_ty: Type, |
| 30177 | inst: Air.Inst.Ref, |
| 30178 | ) CompileError!Air.Inst.Ref { |
| 30179 | const pt = sema.pt; |
| 30180 | const zcu = pt.zcu; |
| 30181 | assert(dest_ty.zigTypeTag(zcu) == .error_set); |
| 30182 | assert(sema.typeOf(inst).zigTypeTag(zcu) == .error_set); |
| 30183 | if (sema.resolveValue(inst)) |val| { |
| 30184 | if (val.isUndef(zcu)) return pt.undefRef(dest_ty); |
| 30185 | return .fromIntern(try pt.intern(.{ .err = .{ |
| 30186 | .ty = dest_ty.toIntern(), |
| 30187 | .name = zcu.intern_pool.indexToKey(val.toIntern()).err.name, |
| 30188 | } })); |
| 30189 | } |
| 30190 | return block.addTyOp(.error_cast, dest_ty, inst); |
| 30191 | } |
| 30192 | |
| 30193 | fn checkSpirvSliceAllowed( |
| 30194 | sema: *Sema, |
| 30195 | block: *Block, |
| 30196 | src: LazySrcLoc, |
| 30197 | address_space: std.lang.AddressSpace, |
| 30198 | ) CompileError!void { |
| 30199 | const zcu = sema.pt.zcu; |
| 30200 | const target = zcu.getTarget(); |
| 30201 | |
| 30202 | if (!target.cpu.arch.isSpirV()) return; |
| 30203 | if (block.isComptime()) return; |
| 30204 | |
| 30205 | // This probably lets some invalid OpPtrAccessChains slip through, but it's better than nothing |
| 30206 | if (!target.cpu.has(.spirv, .variable_pointers) and !target.cpu.has(.spirv, .variable_pointers_storage_buffer)) { |
| 30207 | return sema.failWithOwnedErrorMsg( |
| 30208 | block, |
| 30209 | try sema.errMsg(src, "cannot construct slices without the 'variable_pointers' or 'variable_pointers_storage_buffer' features", .{}), |
| 30210 | ); |
| 30211 | } |
| 30212 | |
| 30213 | switch (address_space) { |
| 30214 | .shared, .storage_buffer => {}, |
| 30215 | else => { |
| 30216 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 30217 | const msg = try sema.errMsg(src, "cannot construct slice from address space '{t}'", .{address_space}); |
| 30218 | errdefer msg.destroy(sema.gpa); |
| 30219 | try sema.errNote(src, msg, "only 'shared' and 'storage_buffer' address spaces support slicing on SPIR-V", .{}); |
| 30220 | break :msg msg; |
| 30221 | }); |
| 30222 | }, |
| 30223 | } |
| 30224 | } |
| 30225 | |
| 30226 | fn coerceArrayPtrToSlice( |
| 30227 | sema: *Sema, |
| 30228 | block: *Block, |
| 30229 | dest_ty: Type, |
| 30230 | inst: Air.Inst.Ref, |
| 30231 | inst_src: LazySrcLoc, |
| 30232 | ) CompileError!Air.Inst.Ref { |
| 30233 | const pt = sema.pt; |
| 30234 | const zcu = pt.zcu; |
| 30235 | if (sema.resolveValue(inst)) |val| { |
| 30236 | const ptr_array_ty = sema.typeOf(inst); |
| 30237 | const array_ty = ptr_array_ty.childType(zcu); |
| 30238 | const slice_ptr_ty = dest_ty.slicePtrFieldType(zcu); |
| 30239 | const slice_ptr = try pt.getCoerced(val, slice_ptr_ty); |
| 30240 | const slice_val = try pt.intern(.{ .slice = .{ |
| 30241 | .ty = dest_ty.toIntern(), |
| 30242 | .ptr = slice_ptr.toIntern(), |
| 30243 | .len = (try pt.intValue(.usize, array_ty.arrayLen(zcu))).toIntern(), |
| 30244 | } }); |
| 30245 | return Air.internedToRef(slice_val); |
| 30246 | } |
| 30247 | try sema.checkSpirvSliceAllowed(block, inst_src, dest_ty.ptrInfo(zcu).flags.address_space); |
| 30248 | try sema.requireRuntimeBlock(block, inst_src, null); |
| 30249 | return block.addTyOp(.array_to_slice, dest_ty, inst); |
| 30250 | } |
| 30251 | |
| 30252 | fn checkPtrAttributes(sema: *Sema, dest_ty: Type, inst_ty: Type, in_memory_result: *InMemoryCoercionResult) bool { |
| 30253 | const pt = sema.pt; |
| 30254 | const zcu = pt.zcu; |
| 30255 | const dest_info = dest_ty.ptrInfo(zcu); |
| 30256 | const inst_info = inst_ty.ptrInfo(zcu); |
| 30257 | const len0 = (Type.fromInterned(inst_info.child).zigTypeTag(zcu) == .array and (Type.fromInterned(inst_info.child).arrayLenIncludingSentinel(zcu) == 0 or |
| 30258 | (Type.fromInterned(inst_info.child).arrayLen(zcu) == 0 and dest_info.sentinel == .none and dest_info.flags.size != .c and dest_info.flags.size != .many))) or |
| 30259 | (Type.fromInterned(inst_info.child).isTuple(zcu) and Type.fromInterned(inst_info.child).structFieldCount(zcu) == 0); |
| 30260 | |
| 30261 | const ok_const = (!inst_info.flags.is_const or dest_info.flags.is_const) or len0; |
| 30262 | const ok_volatile = !inst_info.flags.is_volatile or dest_info.flags.is_volatile; |
| 30263 | if (!ok_const) { |
| 30264 | in_memory_result.* = .{ .ptr_const = .{ |
| 30265 | .actual = inst_ty, |
| 30266 | .wanted = dest_ty, |
| 30267 | } }; |
| 30268 | return false; |
| 30269 | } |
| 30270 | if (!ok_volatile) { |
| 30271 | in_memory_result.* = .{ .ptr_volatile = .{ |
| 30272 | .actual = inst_ty, |
| 30273 | .wanted = dest_ty, |
| 30274 | } }; |
| 30275 | return false; |
| 30276 | } |
| 30277 | |
| 30278 | if (dest_info.flags.address_space != inst_info.flags.address_space) { |
| 30279 | in_memory_result.* = .{ .ptr_addrspace = .{ |
| 30280 | .actual = inst_info.flags.address_space, |
| 30281 | .wanted = dest_info.flags.address_space, |
| 30282 | } }; |
| 30283 | return false; |
| 30284 | } |
| 30285 | |
| 30286 | if (inst_info.packed_offset.host_size != dest_info.packed_offset.host_size or |
| 30287 | inst_info.packed_offset.bit_offset != dest_info.packed_offset.bit_offset) |
| 30288 | { |
| 30289 | in_memory_result.* = .{ .ptr_bit_range = .{ |
| 30290 | .actual_host = inst_info.packed_offset.host_size, |
| 30291 | .wanted_host = dest_info.packed_offset.host_size, |
| 30292 | .actual_offset = inst_info.packed_offset.bit_offset, |
| 30293 | .wanted_offset = dest_info.packed_offset.bit_offset, |
| 30294 | } }; |
| 30295 | return false; |
| 30296 | } |
| 30297 | |
| 30298 | if (inst_info.flags.alignment == .none and dest_info.flags.alignment == .none) return true; |
| 30299 | if (len0) return true; |
| 30300 | |
| 30301 | const inst_align = if (inst_info.flags.alignment != .none) |
| 30302 | inst_info.flags.alignment |
| 30303 | else |
| 30304 | Type.fromInterned(inst_info.child).abiAlignment(zcu); |
| 30305 | |
| 30306 | const dest_align = if (dest_info.flags.alignment != .none) |
| 30307 | dest_info.flags.alignment |
| 30308 | else |
| 30309 | Type.fromInterned(dest_info.child).abiAlignment(zcu); |
| 30310 | |
| 30311 | if (dest_align.compare(.gt, inst_align)) { |
| 30312 | in_memory_result.* = .{ .ptr_alignment = .{ |
| 30313 | .actual = inst_align, |
| 30314 | .wanted = dest_align, |
| 30315 | } }; |
| 30316 | return false; |
| 30317 | } |
| 30318 | return true; |
| 30319 | } |
| 30320 | |
| 30321 | fn coerceCompatiblePtrs( |
| 30322 | sema: *Sema, |
| 30323 | block: *Block, |
| 30324 | dest_ty: Type, |
| 30325 | inst: Air.Inst.Ref, |
| 30326 | inst_src: LazySrcLoc, |
| 30327 | ) !Air.Inst.Ref { |
| 30328 | const pt = sema.pt; |
| 30329 | const zcu = pt.zcu; |
| 30330 | const inst_ty = sema.typeOf(inst); |
| 30331 | if (sema.resolveValue(inst)) |val| { |
| 30332 | if (!val.isUndef(zcu) and val.isNull(zcu) and !dest_ty.isAllowzeroPtr(zcu)) { |
| 30333 | return sema.fail(block, inst_src, "null pointer casted to type '{f}'", .{dest_ty.fmt(pt)}); |
| 30334 | } |
| 30335 | // The comptime Value representation is compatible with both types. |
| 30336 | return Air.internedToRef( |
| 30337 | (try pt.getCoerced(val, dest_ty)).toIntern(), |
| 30338 | ); |
| 30339 | } |
| 30340 | try sema.requireRuntimeBlock(block, inst_src, null); |
| 30341 | const maybe_zero: bool = switch (inst_ty.toIntern()) { |
| 30342 | .usize_type, .isize_type => true, |
| 30343 | else => inst_ty.ptrAllowsZero(zcu), |
| 30344 | }; |
| 30345 | if (block.wantSafety() and maybe_zero and !dest_ty.ptrAllowsZero(zcu)) { |
| 30346 | try sema.checkLogicalPtrOperation(block, inst_src, inst_ty); |
| 30347 | const actual_ptr = if (inst_ty.isSlice(zcu)) |
| 30348 | try sema.analyzeSlicePtr(block, inst_src, inst, inst_ty) |
| 30349 | else |
| 30350 | inst; |
| 30351 | const ptr_int = try block.addTyOp(.int_from_ptr, .usize, actual_ptr); |
| 30352 | const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize); |
| 30353 | const ok = if (inst_ty.isSlice(zcu)) ok: { |
| 30354 | const len = try sema.analyzeSliceLen(block, inst_src, inst); |
| 30355 | const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize); |
| 30356 | break :ok try block.addBinOp(.bit_or, len_zero, is_non_zero); |
| 30357 | } else is_non_zero; |
| 30358 | try sema.addSafetyCheck(block, inst_src, ok, .cast_to_null); |
| 30359 | } |
| 30360 | const new_ptr: Air.Inst.Ref = switch (inst_ty.toIntern()) { |
| 30361 | .usize_type => try block.addTyOp(.ptr_from_int, dest_ty, inst), |
| 30362 | .isize_type => new_ptr: { |
| 30363 | const usize_inst = try block.addTyOp(.bit_cast, .usize, inst); |
| 30364 | break :new_ptr try block.addTyOp(.ptr_from_int, dest_ty, usize_inst); |
| 30365 | }, |
| 30366 | else => try block.addTyOp(.ptr_cast, dest_ty, inst), |
| 30367 | }; |
| 30368 | try sema.checkKnownAllocPtr(block, inst, new_ptr); |
| 30369 | return new_ptr; |
| 30370 | } |
| 30371 | |
| 30372 | /// Asserts that the layout of `union_ty` is already resolved. |
| 30373 | fn coerceEnumToUnion( |
| 30374 | sema: *Sema, |
| 30375 | block: *Block, |
| 30376 | union_ty: Type, |
| 30377 | union_ty_src: LazySrcLoc, |
| 30378 | inst: Air.Inst.Ref, |
| 30379 | inst_src: LazySrcLoc, |
| 30380 | ) !Air.Inst.Ref { |
| 30381 | const pt = sema.pt; |
| 30382 | const zcu = pt.zcu; |
| 30383 | const ip = &zcu.intern_pool; |
| 30384 | const inst_ty = sema.typeOf(inst); |
| 30385 | |
| 30386 | union_ty.assertHasLayout(zcu); |
| 30387 | |
| 30388 | const union_obj = zcu.typeToUnion(union_ty).?; |
| 30389 | const enum_ty: Type = .fromInterned(union_obj.enum_tag_type); |
| 30390 | const enum_obj = ip.loadEnumType(enum_ty.toIntern()); |
| 30391 | |
| 30392 | if (union_obj.tag_usage != .tagged) return sema.failWithOwnedErrorMsg(block, msg: { |
| 30393 | const msg = try sema.typeMismatchErrMsg(inst_src, union_ty, inst_ty); |
| 30394 | errdefer msg.destroy(sema.gpa); |
| 30395 | try sema.errNote(union_ty_src, msg, "cannot coerce enum to untagged union", .{}); |
| 30396 | try sema.addDeclaredHereNote(msg, union_ty); |
| 30397 | break :msg msg; |
| 30398 | }); |
| 30399 | |
| 30400 | const enum_tag = try sema.coerce(block, enum_ty, inst, inst_src); |
| 30401 | if (try sema.resolveDefinedValue(block, inst_src, enum_tag)) |val| { |
| 30402 | const field_index = union_ty.unionTagFieldIndex(val, pt.zcu) orelse { |
| 30403 | return sema.fail(block, inst_src, "union '{f}' has no tag with value '{f}'", .{ |
| 30404 | union_ty.fmt(pt), val.fmtValueSema(pt, sema), |
| 30405 | }); |
| 30406 | }; |
| 30407 | |
| 30408 | const field_name = enum_obj.field_names.get(ip)[field_index]; |
| 30409 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); |
| 30410 | switch (field_ty.classify(zcu)) { |
| 30411 | .one_possible_value => return .fromValue(try pt.unionValue( |
| 30412 | union_ty, |
| 30413 | val, |
| 30414 | (try field_ty.onePossibleValue(pt)).?, |
| 30415 | )), |
| 30416 | |
| 30417 | .no_possible_value => return sema.failWithOwnedErrorMsg(block, msg: { |
| 30418 | const msg = try sema.errMsg(inst_src, "cannot initialize union field with uninstantiable type '{f}'", .{field_ty.fmt(pt)}); |
| 30419 | errdefer msg.destroy(sema.gpa); |
| 30420 | try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{ |
| 30421 | field_name.fmt(ip), |
| 30422 | }); |
| 30423 | try sema.addDeclaredHereNote(msg, union_ty); |
| 30424 | break :msg msg; |
| 30425 | }), |
| 30426 | |
| 30427 | else => return sema.failWithOwnedErrorMsg(block, msg: { |
| 30428 | const msg = try sema.errMsg(inst_src, "coercion from enum '{f}' to union '{f}' must initialize '{f}' field '{f}'", .{ |
| 30429 | inst_ty.fmt(pt), union_ty.fmt(pt), |
| 30430 | field_ty.fmt(pt), field_name.fmt(ip), |
| 30431 | }); |
| 30432 | errdefer msg.destroy(sema.gpa); |
| 30433 | |
| 30434 | try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' declared here", .{field_name.fmt(ip)}); |
| 30435 | try sema.addDeclaredHereNote(msg, union_ty); |
| 30436 | break :msg msg; |
| 30437 | }), |
| 30438 | } |
| 30439 | } |
| 30440 | |
| 30441 | try sema.requireRuntimeBlock(block, inst_src, null); |
| 30442 | |
| 30443 | if (enum_ty.isNonexhaustiveEnum(zcu)) { |
| 30444 | const msg = msg: { |
| 30445 | const msg = try sema.errMsg(inst_src, "runtime coercion to union '{f}' from non-exhaustive enum", .{ |
| 30446 | union_ty.fmt(pt), |
| 30447 | }); |
| 30448 | errdefer msg.destroy(sema.gpa); |
| 30449 | try sema.addDeclaredHereNote(msg, enum_ty); |
| 30450 | break :msg msg; |
| 30451 | }; |
| 30452 | return sema.failWithOwnedErrorMsg(block, msg); |
| 30453 | } |
| 30454 | |
| 30455 | for (union_obj.field_types.get(ip)) |field_ty_ip| { |
| 30456 | if (Type.fromInterned(field_ty_ip).classify(zcu) != .one_possible_value) break; |
| 30457 | } else { |
| 30458 | // All fields are OPV, so the coercion is okay. |
| 30459 | if (try union_ty.onePossibleValue(pt)) |opv| { |
| 30460 | // The tag had redundant bits, but we've omitted the tag from the union's runtime layout, so the union is OPV and hence runtime-known. |
| 30461 | return .fromValue(opv); |
| 30462 | } else { |
| 30463 | // The union layout is just the tag, so we can bitcast the enum straight to the union. |
| 30464 | return block.addTyOp(.union_from_enum, union_ty, enum_tag); |
| 30465 | } |
| 30466 | } |
| 30467 | |
| 30468 | // The coercion is invalid because one or more fields is not OPV. |
| 30469 | |
| 30470 | const msg = msg: { |
| 30471 | const msg = try sema.errMsg( |
| 30472 | inst_src, |
| 30473 | "runtime coercion from enum '{f}' to union '{f}' which has non-void fields", |
| 30474 | .{ enum_ty.fmt(pt), union_ty.fmt(pt) }, |
| 30475 | ); |
| 30476 | errdefer msg.destroy(sema.gpa); |
| 30477 | |
| 30478 | for (0..union_obj.field_types.len) |field_index| { |
| 30479 | const field_name = enum_obj.field_names.get(ip)[field_index]; |
| 30480 | const field_ty: Type = .fromInterned(union_obj.field_types.get(ip)[field_index]); |
| 30481 | const ty_description: []const u8 = switch (field_ty.classify(zcu)) { |
| 30482 | .one_possible_value => continue, |
| 30483 | .no_possible_value => "uninstantiable type", |
| 30484 | else => "type", |
| 30485 | }; |
| 30486 | if (field_ty.classify(zcu) == .one_possible_value) continue; |
| 30487 | try sema.addFieldErrNote(union_ty, field_index, msg, "field '{f}' has {s} '{f}'", .{ |
| 30488 | field_name.fmt(ip), |
| 30489 | ty_description, |
| 30490 | field_ty.fmt(pt), |
| 30491 | }); |
| 30492 | } |
| 30493 | try sema.addDeclaredHereNote(msg, union_ty); |
| 30494 | break :msg msg; |
| 30495 | }; |
| 30496 | return sema.failWithOwnedErrorMsg(block, msg); |
| 30497 | } |
| 30498 | |
| 30499 | /// If the lengths match, coerces element-wise. |
| 30500 | fn coerceArrayLike( |
| 30501 | sema: *Sema, |
| 30502 | block: *Block, |
| 30503 | dest_ty: Type, |
| 30504 | dest_ty_src: LazySrcLoc, |
| 30505 | inst: Air.Inst.Ref, |
| 30506 | inst_src: LazySrcLoc, |
| 30507 | ) !Air.Inst.Ref { |
| 30508 | const pt = sema.pt; |
| 30509 | const zcu = pt.zcu; |
| 30510 | const inst_ty = sema.typeOf(inst); |
| 30511 | const target = zcu.getTarget(); |
| 30512 | |
| 30513 | const inst_len = inst_ty.arrayLen(zcu); |
| 30514 | const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(zcu)); |
| 30515 | if (dest_len != inst_len) { |
| 30516 | const msg = msg: { |
| 30517 | const msg = try sema.typeMismatchErrMsg(inst_src, dest_ty, inst_ty); |
| 30518 | errdefer msg.destroy(sema.gpa); |
| 30519 | try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len}); |
| 30520 | try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len}); |
| 30521 | break :msg msg; |
| 30522 | }; |
| 30523 | return sema.failWithOwnedErrorMsg(block, msg); |
| 30524 | } |
| 30525 | |
| 30526 | const dest_elem_ty = dest_ty.childType(zcu); |
| 30527 | if (dest_ty.isVector(zcu) and inst_ty.isVector(zcu) and sema.resolveValue(inst) == null) { |
| 30528 | const inst_elem_ty = inst_ty.childType(zcu); |
| 30529 | switch (dest_elem_ty.zigTypeTag(zcu)) { |
| 30530 | .int => if (inst_elem_ty.isInt(zcu)) { |
| 30531 | // integer widening |
| 30532 | const dst_info = dest_elem_ty.intInfo(zcu); |
| 30533 | const src_info = inst_elem_ty.intInfo(zcu); |
| 30534 | if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or |
| 30535 | // small enough unsigned ints can get casted to large enough signed ints |
| 30536 | (dst_info.signedness == .signed and dst_info.bits > src_info.bits)) |
| 30537 | { |
| 30538 | try sema.requireRuntimeBlock(block, inst_src, null); |
| 30539 | return block.addTyOp(.int_cast, dest_ty, inst); |
| 30540 | } |
| 30541 | }, |
| 30542 | .float => if (inst_elem_ty.isRuntimeFloat()) { |
| 30543 | // float widening |
| 30544 | const src_bits = inst_elem_ty.floatBits(target); |
| 30545 | const dst_bits = dest_elem_ty.floatBits(target); |
| 30546 | if (dst_bits >= src_bits) { |
| 30547 | try sema.requireRuntimeBlock(block, inst_src, null); |
| 30548 | return block.addTyOp(.fpext, dest_ty, inst); |
| 30549 | } |
| 30550 | }, |
| 30551 | else => {}, |
| 30552 | } |
| 30553 | } |
| 30554 | |
| 30555 | // Matching element types means no per-element work, so let the backend lower the conversion. |
| 30556 | if (dest_ty.isVector(zcu) and |
| 30557 | inst_ty.zigTypeTag(zcu) == .array and |
| 30558 | inst_ty.childType(zcu).toIntern() == dest_elem_ty.toIntern() and |
| 30559 | sema.resolveValue(inst) == null) |
| 30560 | { |
| 30561 | try sema.requireRuntimeBlock(block, inst_src, null); |
| 30562 | return block.addTyOp(.array_to_vector, dest_ty, inst); |
| 30563 | } |
| 30564 | |
| 30565 | const element_vals = try sema.arena.alloc(InternPool.Index, dest_len); |
| 30566 | const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len); |
| 30567 | var runtime_src: ?LazySrcLoc = null; |
| 30568 | |
| 30569 | for (element_vals, element_refs, 0..) |*val, *ref, i| { |
| 30570 | const index_ref = Air.internedToRef((try pt.intValue(.usize, i)).toIntern()); |
| 30571 | const src = inst_src; // TODO better source location |
| 30572 | const elem_src = inst_src; // TODO better source location |
| 30573 | const elem_ref = try sema.elemValArray(block, src, inst_src, inst, elem_src, index_ref, true); |
| 30574 | const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); |
| 30575 | ref.* = coerced; |
| 30576 | if (runtime_src == null) { |
| 30577 | if (sema.resolveValue(coerced)) |elem_val| { |
| 30578 | val.* = elem_val.toIntern(); |
| 30579 | } else { |
| 30580 | runtime_src = elem_src; |
| 30581 | } |
| 30582 | } |
| 30583 | } |
| 30584 | |
| 30585 | if (runtime_src) |rs| { |
| 30586 | try sema.requireRuntimeBlock(block, inst_src, rs); |
| 30587 | return block.addAggregateInit(dest_ty, element_refs); |
| 30588 | } |
| 30589 | |
| 30590 | return Air.internedToRef((try pt.aggregateValue(dest_ty, element_vals)).toIntern()); |
| 30591 | } |
| 30592 | |
| 30593 | /// If the lengths match, coerces element-wise. |
| 30594 | fn coerceTupleToArray( |
| 30595 | sema: *Sema, |
| 30596 | block: *Block, |
| 30597 | dest_ty: Type, |
| 30598 | dest_ty_src: LazySrcLoc, |
| 30599 | inst: Air.Inst.Ref, |
| 30600 | inst_src: LazySrcLoc, |
| 30601 | ) !Air.Inst.Ref { |
| 30602 | const pt = sema.pt; |
| 30603 | const zcu = pt.zcu; |
| 30604 | const inst_ty = sema.typeOf(inst); |
| 30605 | const inst_len = inst_ty.arrayLen(zcu); |
| 30606 | const dest_len = dest_ty.arrayLen(zcu); |
| 30607 | |
| 30608 | if (dest_len != inst_len) { |
| 30609 | const msg = msg: { |
| 30610 | const msg = try sema.typeMismatchErrMsg(inst_src, dest_ty, inst_ty); |
| 30611 | errdefer msg.destroy(sema.gpa); |
| 30612 | try sema.errNote(dest_ty_src, msg, "destination has length {d}", .{dest_len}); |
| 30613 | try sema.errNote(inst_src, msg, "source has length {d}", .{inst_len}); |
| 30614 | break :msg msg; |
| 30615 | }; |
| 30616 | return sema.failWithOwnedErrorMsg(block, msg); |
| 30617 | } |
| 30618 | |
| 30619 | const dest_elems = try sema.usizeCast(block, dest_ty_src, dest_len); |
| 30620 | const element_vals = try sema.arena.alloc(InternPool.Index, dest_elems); |
| 30621 | const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_elems); |
| 30622 | const dest_elem_ty = dest_ty.childType(zcu); |
| 30623 | |
| 30624 | var runtime_src: ?LazySrcLoc = null; |
| 30625 | for (element_vals, element_refs, 0..) |*val, *ref, i_usize| { |
| 30626 | const i: u32 = @intCast(i_usize); |
| 30627 | if (i_usize == inst_len) { |
| 30628 | const sentinel_val = dest_ty.sentinel(zcu).?; |
| 30629 | val.* = sentinel_val.toIntern(); |
| 30630 | ref.* = Air.internedToRef(sentinel_val.toIntern()); |
| 30631 | break; |
| 30632 | } |
| 30633 | const elem_src = inst_src; // TODO better source location |
| 30634 | const elem_ref = try sema.tupleField(block, inst_src, inst, elem_src, i); |
| 30635 | const coerced = try sema.coerce(block, dest_elem_ty, elem_ref, elem_src); |
| 30636 | ref.* = coerced; |
| 30637 | if (runtime_src == null) { |
| 30638 | if (sema.resolveValue(coerced)) |elem_val| { |
| 30639 | val.* = elem_val.toIntern(); |
| 30640 | } else { |
| 30641 | runtime_src = elem_src; |
| 30642 | } |
| 30643 | } |
| 30644 | } |
| 30645 | |
| 30646 | if (runtime_src) |rs| { |
| 30647 | try sema.requireRuntimeBlock(block, inst_src, rs); |
| 30648 | return block.addAggregateInit(dest_ty, element_refs); |
| 30649 | } |
| 30650 | |
| 30651 | return Air.internedToRef((try pt.aggregateValue(dest_ty, element_vals)).toIntern()); |
| 30652 | } |
| 30653 | |
| 30654 | /// If the lengths match, coerces element-wise. |
| 30655 | fn coerceTupleToSlicePtrs( |
| 30656 | sema: *Sema, |
| 30657 | block: *Block, |
| 30658 | slice_ty: Type, |
| 30659 | slice_ty_src: LazySrcLoc, |
| 30660 | ptr_tuple: Air.Inst.Ref, |
| 30661 | tuple_src: LazySrcLoc, |
| 30662 | ) !Air.Inst.Ref { |
| 30663 | const pt = sema.pt; |
| 30664 | const zcu = pt.zcu; |
| 30665 | const tuple_ty = sema.typeOf(ptr_tuple).childType(zcu); |
| 30666 | const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src); |
| 30667 | const slice_info = slice_ty.ptrInfo(zcu); |
| 30668 | const array_ty = try pt.arrayType(.{ |
| 30669 | .len = tuple_ty.structFieldCount(zcu), |
| 30670 | .sentinel = slice_info.sentinel, |
| 30671 | .child = slice_info.child, |
| 30672 | }); |
| 30673 | const array_inst = try sema.coerceTupleToArray(block, array_ty, slice_ty_src, tuple, tuple_src); |
| 30674 | const ptr_array = try sema.analyzeRef(block, slice_ty_src, array_inst, slice_info.flags.alignment); |
| 30675 | return sema.coerceArrayPtrToSlice(block, slice_ty, ptr_array, slice_ty_src); |
| 30676 | } |
| 30677 | |
| 30678 | /// If the lengths match, coerces element-wise. |
| 30679 | fn coerceTupleToArrayPtrs( |
| 30680 | sema: *Sema, |
| 30681 | block: *Block, |
| 30682 | ptr_array_ty: Type, |
| 30683 | array_ty_src: LazySrcLoc, |
| 30684 | ptr_tuple: Air.Inst.Ref, |
| 30685 | tuple_src: LazySrcLoc, |
| 30686 | ) !Air.Inst.Ref { |
| 30687 | const pt = sema.pt; |
| 30688 | const zcu = pt.zcu; |
| 30689 | const tuple = try sema.analyzeLoad(block, tuple_src, ptr_tuple, tuple_src); |
| 30690 | const ptr_info = ptr_array_ty.ptrInfo(zcu); |
| 30691 | const array_ty: Type = .fromInterned(ptr_info.child); |
| 30692 | const array_inst = try sema.coerceTupleToArray(block, array_ty, array_ty_src, tuple, tuple_src); |
| 30693 | const ptr_array = try sema.analyzeRef(block, array_ty_src, array_inst, ptr_info.flags.alignment); |
| 30694 | return ptr_array; |
| 30695 | } |
| 30696 | |
| 30697 | fn coerceTupleToTuple( |
| 30698 | sema: *Sema, |
| 30699 | block: *Block, |
| 30700 | tuple_ty: Type, |
| 30701 | inst: Air.Inst.Ref, |
| 30702 | inst_src: LazySrcLoc, |
| 30703 | ) !Air.Inst.Ref { |
| 30704 | const pt = sema.pt; |
| 30705 | const zcu = pt.zcu; |
| 30706 | const ip = &zcu.intern_pool; |
| 30707 | const dest_field_count = switch (ip.indexToKey(tuple_ty.toIntern())) { |
| 30708 | .tuple_type => |tuple_type| tuple_type.types.len, |
| 30709 | else => unreachable, |
| 30710 | }; |
| 30711 | const field_vals = try sema.arena.alloc(InternPool.Index, dest_field_count); |
| 30712 | const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len); |
| 30713 | @memset(field_refs, .none); |
| 30714 | |
| 30715 | const inst_ty = sema.typeOf(inst); |
| 30716 | const src_field_count = switch (ip.indexToKey(inst_ty.toIntern())) { |
| 30717 | .tuple_type => |tuple_type| tuple_type.types.len, |
| 30718 | else => unreachable, |
| 30719 | }; |
| 30720 | if (src_field_count > dest_field_count) return error.NotCoercible; |
| 30721 | |
| 30722 | var runtime_src: ?LazySrcLoc = null; |
| 30723 | for (0..dest_field_count) |field_index_usize| { |
| 30724 | const field_i: u32 = @intCast(field_index_usize); |
| 30725 | const field_src = inst_src; // TODO better source location |
| 30726 | |
| 30727 | const field_index: u32 = @intCast(field_index_usize); |
| 30728 | |
| 30729 | const field_ty, const default_val = field: { |
| 30730 | const tuple_type = ip.indexToKey(tuple_ty.toIntern()).tuple_type; |
| 30731 | break :field .{ |
| 30732 | tuple_type.types.get(ip)[field_index], |
| 30733 | tuple_type.values.get(ip)[field_index], |
| 30734 | }; |
| 30735 | }; |
| 30736 | |
| 30737 | const elem_ref = try sema.tupleField(block, inst_src, inst, field_src, field_i); |
| 30738 | const coerced = try sema.coerce(block, .fromInterned(field_ty), elem_ref, field_src); |
| 30739 | field_refs[field_index] = coerced; |
| 30740 | if (default_val != .none) { |
| 30741 | const init_val = sema.resolveValue(coerced) orelse { |
| 30742 | return sema.failWithNeededComptime(block, field_src, .{ .simple = .stored_to_comptime_field }); |
| 30743 | }; |
| 30744 | |
| 30745 | if (!init_val.eql(Value.fromInterned(default_val), .fromInterned(field_ty), pt.zcu)) { |
| 30746 | return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, field_i); |
| 30747 | } |
| 30748 | } |
| 30749 | if (runtime_src == null) { |
| 30750 | if (sema.resolveValue(coerced)) |field_val| { |
| 30751 | field_vals[field_index] = field_val.toIntern(); |
| 30752 | } else { |
| 30753 | runtime_src = field_src; |
| 30754 | } |
| 30755 | } |
| 30756 | } |
| 30757 | |
| 30758 | // Populate default field values and report errors for missing fields. |
| 30759 | var root_msg: ?*Zcu.ErrorMsg = null; |
| 30760 | errdefer if (root_msg) |msg| msg.destroy(sema.gpa); |
| 30761 | |
| 30762 | for (field_refs, 0..) |*field_ref, i_usize| { |
| 30763 | const i: u32 = @intCast(i_usize); |
| 30764 | if (field_ref.* != .none) continue; |
| 30765 | |
| 30766 | const default_val = ip.indexToKey(tuple_ty.toIntern()).tuple_type.values.get(ip)[i]; |
| 30767 | |
| 30768 | const field_src = inst_src; // TODO better source location |
| 30769 | if (default_val == .none) { |
| 30770 | const template = "missing tuple field: {d}"; |
| 30771 | if (root_msg) |msg| { |
| 30772 | try sema.errNote(field_src, msg, template, .{i}); |
| 30773 | } else { |
| 30774 | root_msg = try sema.errMsg(field_src, template, .{i}); |
| 30775 | } |
| 30776 | continue; |
| 30777 | } |
| 30778 | if (runtime_src == null) { |
| 30779 | field_vals[i] = default_val; |
| 30780 | } else { |
| 30781 | field_ref.* = Air.internedToRef(default_val); |
| 30782 | } |
| 30783 | } |
| 30784 | |
| 30785 | if (root_msg) |msg| { |
| 30786 | try sema.addDeclaredHereNote(msg, tuple_ty); |
| 30787 | root_msg = null; |
| 30788 | return sema.failWithOwnedErrorMsg(block, msg); |
| 30789 | } |
| 30790 | |
| 30791 | if (runtime_src) |rs| { |
| 30792 | try sema.requireRuntimeBlock(block, inst_src, rs); |
| 30793 | return block.addAggregateInit(tuple_ty, field_refs); |
| 30794 | } |
| 30795 | |
| 30796 | return Air.internedToRef((try pt.aggregateValue(tuple_ty, field_vals)).toIntern()); |
| 30797 | } |
| 30798 | |
| 30799 | fn analyzeNavVal( |
| 30800 | sema: *Sema, |
| 30801 | block: *Block, |
| 30802 | src: LazySrcLoc, |
| 30803 | nav_index: InternPool.Nav.Index, |
| 30804 | ) CompileError!Air.Inst.Ref { |
| 30805 | const ref = try sema.analyzeNavRefInner(block, src, nav_index, false); |
| 30806 | return sema.analyzeLoad(block, src, ref, src); |
| 30807 | } |
| 30808 | |
| 30809 | pub fn addReferenceEntry( |
| 30810 | sema: *Sema, |
| 30811 | opt_block: ?*Block, |
| 30812 | src: LazySrcLoc, |
| 30813 | referenced_unit: AnalUnit, |
| 30814 | ) !void { |
| 30815 | const zcu = sema.pt.zcu; |
| 30816 | const ip = &zcu.intern_pool; |
| 30817 | switch (referenced_unit.unwrap()) { |
| 30818 | .func => |f| assert(ip.unwrapCoercedFunc(f) == f), // for `.{ .func = f }`, `f` must be uncoerced |
| 30819 | else => {}, |
| 30820 | } |
| 30821 | const gop = try sema.references.getOrPut(sema.gpa, referenced_unit); |
| 30822 | if (gop.found_existing) return; |
| 30823 | try zcu.addUnitReference(sema.owner, referenced_unit, src, inline_frame: { |
| 30824 | const block = opt_block orelse break :inline_frame .none; |
| 30825 | const inlining = block.inlining orelse break :inline_frame .none; |
| 30826 | const frame = try inlining.refFrame(zcu); |
| 30827 | break :inline_frame frame.toOptional(); |
| 30828 | }); |
| 30829 | } |
| 30830 | |
| 30831 | pub fn addTypeReferenceEntry( |
| 30832 | sema: *Sema, |
| 30833 | src: LazySrcLoc, |
| 30834 | referenced_type: Type, |
| 30835 | ) !void { |
| 30836 | const zcu = sema.pt.zcu; |
| 30837 | const gop = try sema.type_references.getOrPut(sema.gpa, referenced_type.toIntern()); |
| 30838 | if (gop.found_existing) return; |
| 30839 | try zcu.addTypeReference(sema.owner, referenced_type.toIntern(), src); |
| 30840 | } |
| 30841 | |
| 30842 | fn ensureMemoizedStateResolved(sema: *Sema, src: LazySrcLoc, stage: InternPool.MemoizedStateStage) SemaError!void { |
| 30843 | const pt = sema.pt; |
| 30844 | |
| 30845 | const unit: AnalUnit = .wrap(.{ .memoized_state = stage }); |
| 30846 | try sema.addReferenceEntry(null, src, unit); |
| 30847 | try sema.declareDependency(.{ .memoized_state = stage }); |
| 30848 | |
| 30849 | const reason: Zcu.DependencyReason = .{ .src = src, .type_layout_reason = undefined }; |
| 30850 | if (pt.zcu.analysis_in_progress.contains(unit)) { |
| 30851 | return sema.failWithDependencyLoop(unit, &reason); |
| 30852 | } |
| 30853 | pt.ensureMemoizedStateUpToDate(stage, &reason) catch |err| switch (err) { |
| 30854 | error.AnalysisFail => return sema.failTransitive(.{ .failed_unit = unit }), |
| 30855 | else => |e| return e, |
| 30856 | }; |
| 30857 | } |
| 30858 | |
| 30859 | pub fn ensureNavResolved(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index, kind: enum { type, fully }) CompileError!void { |
| 30860 | const pt = sema.pt; |
| 30861 | const zcu = pt.zcu; |
| 30862 | const ip = &zcu.intern_pool; |
| 30863 | |
| 30864 | const nav = ip.getNav(nav_index); |
| 30865 | if (nav.analysis == null) { |
| 30866 | assert(nav.resolved.?.value != .none); |
| 30867 | return; |
| 30868 | } |
| 30869 | |
| 30870 | // Note that even if `nav.status == .resolved`, we must still trigger `ensureNavValUpToDate` |
| 30871 | // to make sure the value is up-to-date on incremental updates. |
| 30872 | |
| 30873 | const anal_unit: AnalUnit = .wrap(switch (kind) { |
| 30874 | .type => .{ .nav_ty = nav_index }, |
| 30875 | .fully => .{ .nav_val = nav_index }, |
| 30876 | }); |
| 30877 | try sema.addReferenceEntry(block, src, anal_unit); |
| 30878 | try sema.declareDependency(switch (kind) { |
| 30879 | .type => .{ .nav_ty = nav_index }, |
| 30880 | .fully => .{ .nav_val = nav_index }, |
| 30881 | }); |
| 30882 | |
| 30883 | const reason: Zcu.DependencyReason = .{ .src = src, .type_layout_reason = undefined }; |
| 30884 | |
| 30885 | if (zcu.analysis_in_progress.contains(anal_unit)) { |
| 30886 | return sema.failWithDependencyLoop(anal_unit, &reason); |
| 30887 | } |
| 30888 | |
| 30889 | switch (kind) { |
| 30890 | .type => { |
| 30891 | try zcu.ensureNavValAnalysisQueued(nav_index); |
| 30892 | return pt.ensureNavTypeUpToDate(nav_index, &reason) catch |err| switch (err) { |
| 30893 | error.AnalysisFail => return sema.failTransitive(.{ .failed_unit = anal_unit }), |
| 30894 | else => |e| return e, |
| 30895 | }; |
| 30896 | }, |
| 30897 | .fully => return pt.ensureNavValUpToDate(nav_index, &reason) catch |err| switch (err) { |
| 30898 | error.AnalysisFail => return sema.failTransitive(.{ .failed_unit = anal_unit }), |
| 30899 | else => |e| return e, |
| 30900 | }, |
| 30901 | } |
| 30902 | } |
| 30903 | |
| 30904 | fn optRefValue(sema: *Sema, opt_val: ?Value) !Value { |
| 30905 | const pt = sema.pt; |
| 30906 | const ptr_anyopaque_ty = try pt.singleConstPtrType(.anyopaque); |
| 30907 | const opt_ptr_anyopaque_ty = try pt.optionalType(ptr_anyopaque_ty.toIntern()); |
| 30908 | return .fromInterned(try pt.intern(.{ .opt = .{ |
| 30909 | .ty = opt_ptr_anyopaque_ty.toIntern(), |
| 30910 | .val = payload: { |
| 30911 | const val = opt_val orelse break :payload .none; |
| 30912 | const ptr_val = try pt.getCoerced(try pt.uavValue(val), ptr_anyopaque_ty); |
| 30913 | break :payload ptr_val.toIntern(); |
| 30914 | }, |
| 30915 | } })); |
| 30916 | } |
| 30917 | |
| 30918 | fn analyzeNavRef(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index) CompileError!Air.Inst.Ref { |
| 30919 | return sema.analyzeNavRefInner(block, src, nav_index, true); |
| 30920 | } |
| 30921 | |
| 30922 | /// Analyze a reference to the `Nav` at the given index. Ensures the underlying `Nav` is analyzed. |
| 30923 | /// If this pointer will be used directly, `is_ref` must be `true`. |
| 30924 | /// If this pointer will be immediately loaded (i.e. a `decl_val` instruction), `is_ref` must be `false`. |
| 30925 | fn analyzeNavRefInner(sema: *Sema, block: *Block, src: LazySrcLoc, orig_nav_index: InternPool.Nav.Index, is_ref: bool) CompileError!Air.Inst.Ref { |
| 30926 | const pt = sema.pt; |
| 30927 | const zcu = pt.zcu; |
| 30928 | const ip = &zcu.intern_pool; |
| 30929 | |
| 30930 | try sema.ensureNavResolved(block, src, orig_nav_index, if (is_ref) .type else .fully); |
| 30931 | |
| 30932 | const nav_index = nav: { |
| 30933 | const orig_nav = ip.getNav(orig_nav_index); |
| 30934 | if (orig_nav.resolved.?.is_extern_decl or ip.zigTypeTag(orig_nav.resolved.?.type) == .@"fn") { |
| 30935 | // A pointer to this `Nav` might actually be encoded as a pointer to a different `Nav` |
| 30936 | // because this is either an `extern` definition or an `extern` alias. (The latter case |
| 30937 | // is unsolved language weirdness; see https://github.com/ziglang/zig/issues/21027.) To |
| 30938 | // know for sure how to encode this pointer, we need to check the *value* of this `Nav`. |
| 30939 | const orig_nav_value = switch (is_ref) { |
| 30940 | false => orig_nav.resolved.?.value, |
| 30941 | true => orig_val: { |
| 30942 | try sema.ensureNavResolved(block, src, orig_nav_index, .fully); |
| 30943 | break :orig_val ip.getNav(orig_nav_index).resolved.?.value; |
| 30944 | }, |
| 30945 | }; |
| 30946 | switch (ip.indexToKey(orig_nav_value)) { |
| 30947 | .func => |f| break :nav f.owner_nav, |
| 30948 | .@"extern" => |e| break :nav e.owner_nav, |
| 30949 | else => {}, |
| 30950 | } |
| 30951 | } |
| 30952 | break :nav orig_nav_index; |
| 30953 | }; |
| 30954 | |
| 30955 | const nav_resolved = ip.getNav(nav_index).resolved.?; |
| 30956 | |
| 30957 | const is_runtime: bool = runtime: { |
| 30958 | if (nav_resolved.@"threadlocal") break :runtime true; |
| 30959 | if (nav_resolved.value == .none) { |
| 30960 | // This didn't come from `@extern`, so even if extern it couldn't be dllimport or pcrel. |
| 30961 | break :runtime false; |
| 30962 | } |
| 30963 | const @"extern" = switch (ip.indexToKey(nav_resolved.value)) { |
| 30964 | .@"extern" => |e| e, |
| 30965 | else => break :runtime false, |
| 30966 | }; |
| 30967 | if (@"extern".is_dll_import) break :runtime true; |
| 30968 | break :runtime switch (@"extern".relocation) { |
| 30969 | .any => false, |
| 30970 | .pcrel => true, |
| 30971 | }; |
| 30972 | }; |
| 30973 | |
| 30974 | const ptr_ty = try pt.ptrType(.{ |
| 30975 | .child = nav_resolved.type, |
| 30976 | .flags = .{ |
| 30977 | .alignment = nav_resolved.@"align", |
| 30978 | .is_const = nav_resolved.@"const", |
| 30979 | .address_space = nav_resolved.@"addrspace", |
| 30980 | }, |
| 30981 | }); |
| 30982 | |
| 30983 | if (is_runtime) { |
| 30984 | // This pointer is runtime-known; we need to emit an AIR instruction to create it. |
| 30985 | return block.addInst(.{ |
| 30986 | .tag = .runtime_nav_ptr, |
| 30987 | .data = .{ .ty_nav = .{ |
| 30988 | .ty = ptr_ty, |
| 30989 | .nav = nav_index, |
| 30990 | } }, |
| 30991 | }); |
| 30992 | } |
| 30993 | |
| 30994 | if (is_ref) { |
| 30995 | try sema.maybeQueueFuncBodyAnalysis(block, src, nav_index); |
| 30996 | } |
| 30997 | |
| 30998 | return Air.internedToRef((try pt.intern(.{ .ptr = .{ |
| 30999 | .ty = ptr_ty.toIntern(), |
| 31000 | .base_addr = .{ .nav = nav_index }, |
| 31001 | .byte_offset = 0, |
| 31002 | } }))); |
| 31003 | } |
| 31004 | |
| 31005 | fn maybeQueueFuncBodyAnalysis(sema: *Sema, block: *Block, src: LazySrcLoc, nav_index: InternPool.Nav.Index) !void { |
| 31006 | const pt = sema.pt; |
| 31007 | const zcu = pt.zcu; |
| 31008 | const ip = &zcu.intern_pool; |
| 31009 | |
| 31010 | // To avoid forcing too much resolution, let's first resolve the type, and check if it's a function. |
| 31011 | // If it is, we can resolve the *value*, and queue analysis as needed. |
| 31012 | |
| 31013 | try sema.ensureNavResolved(block, src, nav_index, .type); |
| 31014 | const nav_ty: Type = .fromInterned(ip.getNav(nav_index).resolved.?.type); |
| 31015 | if (nav_ty.zigTypeTag(zcu) != .@"fn") return; |
| 31016 | if (!nav_ty.fnHasRuntimeBits(zcu)) return; |
| 31017 | |
| 31018 | try sema.ensureNavResolved(block, src, nav_index, .fully); |
| 31019 | const nav_val = zcu.navValue(nav_index); |
| 31020 | if (!ip.isFuncBody(nav_val.toIntern())) return; |
| 31021 | |
| 31022 | const orig_fn_index = ip.unwrapCoercedFunc(nav_val.toIntern()); |
| 31023 | try sema.addReferenceEntry(block, src, .wrap(.{ .func = orig_fn_index })); |
| 31024 | try zcu.ensureFuncBodyAnalysisQueued(orig_fn_index); |
| 31025 | } |
| 31026 | |
| 31027 | fn analyzeRef( |
| 31028 | sema: *Sema, |
| 31029 | block: *Block, |
| 31030 | src: LazySrcLoc, |
| 31031 | operand: Air.Inst.Ref, |
| 31032 | alignment: Alignment, |
| 31033 | ) CompileError!Air.Inst.Ref { |
| 31034 | const pt = sema.pt; |
| 31035 | const zcu = pt.zcu; |
| 31036 | const operand_ty = sema.typeOf(operand); |
| 31037 | |
| 31038 | const address_space = target_util.defaultAddressSpace(zcu.getTarget(), .local); |
| 31039 | const ptr_type = try pt.ptrType(.{ |
| 31040 | .child = operand_ty.toIntern(), |
| 31041 | .flags = .{ |
| 31042 | .alignment = alignment, |
| 31043 | .is_const = true, |
| 31044 | .address_space = address_space, |
| 31045 | }, |
| 31046 | }); |
| 31047 | |
| 31048 | if (sema.resolveValue(operand)) |val| { |
| 31049 | switch (zcu.intern_pool.indexToKey(val.toIntern())) { |
| 31050 | .@"extern" => |e| return sema.analyzeNavRef(block, src, e.owner_nav), |
| 31051 | .func => |f| return sema.analyzeNavRef(block, src, f.owner_nav), |
| 31052 | else => return .fromIntern(try pt.intern(.{ .ptr = .{ |
| 31053 | .ty = ptr_type.toIntern(), |
| 31054 | .base_addr = .{ .uav = .{ |
| 31055 | .val = val.toIntern(), |
| 31056 | .orig_ty = ptr_type.toIntern(), |
| 31057 | } }, |
| 31058 | .byte_offset = 0, |
| 31059 | } })), |
| 31060 | } |
| 31061 | } |
| 31062 | |
| 31063 | // No `requireRuntimeBlock`; it's okay to `ref` to a runtime value in a comptime context, |
| 31064 | // it's just that we can only use the *type* of the result, since the value is runtime-known. |
| 31065 | |
| 31066 | const mut_ptr_type = try pt.ptrType(.{ |
| 31067 | .child = operand_ty.toIntern(), |
| 31068 | .flags = .{ |
| 31069 | .alignment = alignment, |
| 31070 | .is_const = false, |
| 31071 | .address_space = address_space, |
| 31072 | }, |
| 31073 | }); |
| 31074 | const alloc = try block.addTy(.alloc, mut_ptr_type); |
| 31075 | |
| 31076 | // In a comptime context, the store would fail, since the operand is runtime-known. But that's |
| 31077 | // okay; we don't actually need this store to succeed, since we're creating a runtime value in a |
| 31078 | // comptime scope, so the value can never be used aside from to get its type. |
| 31079 | if (!block.isComptime()) { |
| 31080 | try sema.storePtr(block, src, alloc, operand); |
| 31081 | } |
| 31082 | |
| 31083 | // Cast to the constant pointer type. We do this directly rather than going via `coerce` to |
| 31084 | // avoid errors in the `block.isComptime()` case. |
| 31085 | return block.addTyOp(.ptr_cast, ptr_type, alloc); |
| 31086 | } |
| 31087 | |
| 31088 | fn analyzeLoad( |
| 31089 | sema: *Sema, |
| 31090 | block: *Block, |
| 31091 | src: LazySrcLoc, |
| 31092 | ptr: Air.Inst.Ref, |
| 31093 | ptr_src: LazySrcLoc, |
| 31094 | ) CompileError!Air.Inst.Ref { |
| 31095 | const pt = sema.pt; |
| 31096 | const zcu = pt.zcu; |
| 31097 | const ptr_ty = sema.typeOf(ptr); |
| 31098 | const elem_ty = switch (ptr_ty.zigTypeTag(zcu)) { |
| 31099 | .pointer => ptr_ty.childType(zcu), |
| 31100 | else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ty.fmt(pt)}), |
| 31101 | }; |
| 31102 | |
| 31103 | try sema.ensureLayoutResolved(elem_ty, src, .ptr_access); |
| 31104 | |
| 31105 | if (elem_ty.isSpirvRuntimeArray(zcu)) { |
| 31106 | return sema.fail(block, src, "cannot load SPIR-V runtime array value", .{}); |
| 31107 | } |
| 31108 | |
| 31109 | const comptime_only = switch (elem_ty.classify(zcu)) { |
| 31110 | .no_possible_value => switch (elem_ty.zigTypeTag(zcu)) { |
| 31111 | .@"opaque" => return sema.fail(block, src, "cannot load opaque type '{f}'", .{elem_ty.fmt(pt)}), |
| 31112 | else => { |
| 31113 | // Loading an uninstantiable type always invokes Illegal Behavior. |
| 31114 | if (block.isComptime()) { |
| 31115 | return sema.fail(block, src, "cannot load uninstantiable type '{f}'", .{elem_ty.fmt(pt)}); |
| 31116 | } else if (block.wantSafety()) { |
| 31117 | try sema.safetyPanic(block, src, .load_uninstantiable_type); |
| 31118 | return .unreachable_value; |
| 31119 | } else { |
| 31120 | _ = try block.addNoOp(.unreach); |
| 31121 | return .unreachable_value; |
| 31122 | } |
| 31123 | }, |
| 31124 | }, |
| 31125 | .one_possible_value => return .fromValue((try elem_ty.onePossibleValue(pt)).?), |
| 31126 | .runtime => false, |
| 31127 | .partially_comptime, .fully_comptime => true, |
| 31128 | }; |
| 31129 | |
| 31130 | if (try sema.resolveDefinedValue(block, ptr_src, ptr)) |ptr_val| { |
| 31131 | if (switch (ptr_ty.ptrSize(zcu)) { |
| 31132 | .slice => try sema.maybeDerefSliceAsArray(block, src, ptr_val), |
| 31133 | else => try sema.pointerDeref(block, src, ptr_val, ptr_ty), |
| 31134 | }) |elem_val| { |
| 31135 | return .fromValue(elem_val); |
| 31136 | } |
| 31137 | } |
| 31138 | |
| 31139 | if (comptime_only) return sema.failWithOwnedErrorMsg(block, msg: { |
| 31140 | const msg = try sema.errMsg(src, "cannot load comptime-only type '{f}'", .{elem_ty.fmt(pt)}); |
| 31141 | errdefer msg.destroy(zcu.gpa); |
| 31142 | try sema.errNote(ptr_src, msg, "pointer of type '{f}' is runtime-known", .{ptr_ty.fmt(pt)}); |
| 31143 | break :msg msg; |
| 31144 | }); |
| 31145 | |
| 31146 | // https://github.com/ziglang/zig/issues/6597 |
| 31147 | if (block.wantSafety() and ptr_ty.isCPtr(zcu)) { |
| 31148 | const is_non_null = try block.addUnOp(.is_non_null, ptr); |
| 31149 | try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); |
| 31150 | } |
| 31151 | return block.addTyOp(.load, elem_ty, ptr); |
| 31152 | } |
| 31153 | |
| 31154 | fn analyzeSlicePtr( |
| 31155 | sema: *Sema, |
| 31156 | block: *Block, |
| 31157 | slice_src: LazySrcLoc, |
| 31158 | slice: Air.Inst.Ref, |
| 31159 | slice_ty: Type, |
| 31160 | ) CompileError!Air.Inst.Ref { |
| 31161 | const pt = sema.pt; |
| 31162 | const zcu = pt.zcu; |
| 31163 | const result_ty = slice_ty.slicePtrFieldType(zcu); |
| 31164 | if (sema.resolveValue(slice)) |val| { |
| 31165 | if (val.isUndef(zcu)) return pt.undefRef(result_ty); |
| 31166 | return Air.internedToRef(val.slicePtr(zcu).toIntern()); |
| 31167 | } |
| 31168 | try sema.requireRuntimeBlock(block, slice_src, null); |
| 31169 | return block.addTyOp(.slice_ptr, result_ty, slice); |
| 31170 | } |
| 31171 | |
| 31172 | fn analyzeOptionalSlicePtr( |
| 31173 | sema: *Sema, |
| 31174 | block: *Block, |
| 31175 | opt_slice_src: LazySrcLoc, |
| 31176 | opt_slice: Air.Inst.Ref, |
| 31177 | opt_slice_ty: Type, |
| 31178 | ) CompileError!Air.Inst.Ref { |
| 31179 | const pt = sema.pt; |
| 31180 | const zcu = pt.zcu; |
| 31181 | const slice_ty = opt_slice_ty.optionalChild(zcu); |
| 31182 | const result_ty = slice_ty.slicePtrFieldType(zcu); |
| 31183 | |
| 31184 | if (sema.resolveValue(opt_slice)) |opt_val| { |
| 31185 | if (opt_val.isUndef(zcu)) return pt.undefRef(result_ty); |
| 31186 | const slice_ptr: InternPool.Index = if (opt_val.optionalValue(zcu)) |val| |
| 31187 | val.slicePtr(zcu).toIntern() |
| 31188 | else |
| 31189 | .null_value; |
| 31190 | |
| 31191 | return Air.internedToRef(slice_ptr); |
| 31192 | } |
| 31193 | |
| 31194 | try sema.requireRuntimeBlock(block, opt_slice_src, null); |
| 31195 | |
| 31196 | const slice = try block.addTyOp(.optional_payload, slice_ty, opt_slice); |
| 31197 | return block.addTyOp(.slice_ptr, result_ty, slice); |
| 31198 | } |
| 31199 | |
| 31200 | fn analyzeSliceLen( |
| 31201 | sema: *Sema, |
| 31202 | block: *Block, |
| 31203 | src: LazySrcLoc, |
| 31204 | slice_inst: Air.Inst.Ref, |
| 31205 | ) CompileError!Air.Inst.Ref { |
| 31206 | const pt = sema.pt; |
| 31207 | const zcu = pt.zcu; |
| 31208 | if (sema.resolveValue(slice_inst)) |slice_val| { |
| 31209 | if (slice_val.isUndef(zcu)) { |
| 31210 | return .undef_usize; |
| 31211 | } |
| 31212 | return pt.intRef(.usize, slice_val.sliceLen(zcu)); |
| 31213 | } |
| 31214 | try sema.requireRuntimeBlock(block, src, null); |
| 31215 | return block.addTyOp(.slice_len, .usize, slice_inst); |
| 31216 | } |
| 31217 | |
| 31218 | fn analyzeIsNull( |
| 31219 | sema: *Sema, |
| 31220 | block: *Block, |
| 31221 | src: LazySrcLoc, |
| 31222 | operand: Air.Inst.Ref, |
| 31223 | invert_logic: bool, |
| 31224 | ) CompileError!Air.Inst.Ref { |
| 31225 | const pt = sema.pt; |
| 31226 | const zcu = pt.zcu; |
| 31227 | |
| 31228 | if (try sema.resolveIsNullFromType(block, src, sema.typeOf(operand))) |is_null| { |
| 31229 | return .fromValue(.makeBool(is_null != invert_logic)); // XOR |
| 31230 | } |
| 31231 | |
| 31232 | if (sema.resolveValue(operand)) |opt_val| { |
| 31233 | if (opt_val.isUndef(zcu)) { |
| 31234 | return pt.undefRef(.bool); |
| 31235 | } |
| 31236 | const is_null = opt_val.isNull(zcu); |
| 31237 | return .fromValue(.makeBool(is_null != invert_logic)); // XOR |
| 31238 | } |
| 31239 | |
| 31240 | const air_tag: Air.Inst.Tag = if (invert_logic) .is_non_null else .is_null; |
| 31241 | return block.addUnOp(air_tag, operand); |
| 31242 | } |
| 31243 | |
| 31244 | fn resolvePtrIsNonErrVal( |
| 31245 | sema: *Sema, |
| 31246 | block: *Block, |
| 31247 | src: LazySrcLoc, |
| 31248 | operand: Air.Inst.Ref, |
| 31249 | ) CompileError!?Value { |
| 31250 | const pt = sema.pt; |
| 31251 | const zcu = pt.zcu; |
| 31252 | const ptr_ty = sema.typeOf(operand); |
| 31253 | assert(ptr_ty.zigTypeTag(zcu) == .pointer); |
| 31254 | const child_ty = ptr_ty.childType(zcu); |
| 31255 | |
| 31256 | if (try sema.resolveIsNonErrFromType(block, src, child_ty)) |res| { |
| 31257 | return res; |
| 31258 | } |
| 31259 | assert(child_ty.zigTypeTag(zcu) == .error_union); |
| 31260 | |
| 31261 | if (sema.resolveValue(operand)) |eu_ptr_val| { |
| 31262 | if (eu_ptr_val.isUndef(zcu)) return .undef_bool; |
| 31263 | if (try sema.pointerDeref(block, src, eu_ptr_val, ptr_ty)) |err_union| { |
| 31264 | if (err_union.isUndef(zcu)) return .undef_bool; |
| 31265 | return .makeBool(err_union.getErrorName(zcu) == .none); |
| 31266 | } |
| 31267 | } |
| 31268 | |
| 31269 | return null; |
| 31270 | } |
| 31271 | |
| 31272 | fn resolveIsNonErrVal( |
| 31273 | sema: *Sema, |
| 31274 | block: *Block, |
| 31275 | src: LazySrcLoc, |
| 31276 | operand: Air.Inst.Ref, |
| 31277 | ) CompileError!?Value { |
| 31278 | const zcu = sema.pt.zcu; |
| 31279 | if (try sema.resolveIsNonErrFromType(block, src, sema.typeOf(operand))) |res| { |
| 31280 | return res; |
| 31281 | } |
| 31282 | assert(sema.typeOf(operand).zigTypeTag(zcu) == .error_union); |
| 31283 | |
| 31284 | if (sema.resolveValue(operand)) |err_union| { |
| 31285 | if (err_union.isUndef(zcu)) return .undef_bool; |
| 31286 | return .makeBool(err_union.getErrorName(zcu) == .none); |
| 31287 | } |
| 31288 | |
| 31289 | return null; |
| 31290 | } |
| 31291 | |
| 31292 | /// If `null` is the only possible value of type `ty`, returns `true`. |
| 31293 | /// If `null` is *not* a possible value of `ty`, returns `false`. |
| 31294 | /// Otherwise, if a value of type `ty` may or may not be `null`, returns `null`. |
| 31295 | /// |
| 31296 | /// Asserts that the layout of `ty` is resolved. |
| 31297 | fn resolveIsNullFromType( |
| 31298 | sema: *Sema, |
| 31299 | block: *Block, |
| 31300 | src: LazySrcLoc, |
| 31301 | ty: Type, |
| 31302 | ) CompileError!?bool { |
| 31303 | const zcu = sema.pt.zcu; |
| 31304 | ty.assertHasLayout(zcu); |
| 31305 | |
| 31306 | return switch (ty.zigTypeTag(zcu)) { |
| 31307 | else => false, |
| 31308 | .null => true, |
| 31309 | .pointer => switch (ty.ptrSize(zcu)) { |
| 31310 | .c => null, |
| 31311 | else => false, |
| 31312 | }, |
| 31313 | .optional => { |
| 31314 | const payload_ty = ty.optionalChild(zcu); |
| 31315 | if (payload_ty.classify(zcu) == .no_possible_value) { |
| 31316 | return true; // e.g. `?noreturn` |
| 31317 | } |
| 31318 | if (payload_ty.zigTypeTag(zcu) == .error_set and |
| 31319 | try sema.resolveErrSetIsEmpty(block, src, payload_ty)) |
| 31320 | { |
| 31321 | return true; // e.g. `?error{}` |
| 31322 | } |
| 31323 | return null; |
| 31324 | }, |
| 31325 | }; |
| 31326 | } |
| 31327 | |
| 31328 | fn resolveIsNonErrFromType( |
| 31329 | sema: *Sema, |
| 31330 | block: *Block, |
| 31331 | src: LazySrcLoc, |
| 31332 | operand_ty: Type, |
| 31333 | ) CompileError!?Value { |
| 31334 | const pt = sema.pt; |
| 31335 | const zcu = pt.zcu; |
| 31336 | const ot = operand_ty.zigTypeTag(zcu); |
| 31337 | if (ot != .error_set and ot != .error_union) return .true; |
| 31338 | if (ot == .error_set) return .false; |
| 31339 | assert(ot == .error_union); |
| 31340 | |
| 31341 | const payload_ty = operand_ty.errorUnionPayload(zcu); |
| 31342 | if (payload_ty.classify(zcu) == .no_possible_value) { |
| 31343 | return .false; |
| 31344 | } |
| 31345 | if (try sema.resolveErrSetIsEmpty(block, src, operand_ty.errorUnionSet(zcu))) { |
| 31346 | return .true; |
| 31347 | } |
| 31348 | return null; |
| 31349 | } |
| 31350 | |
| 31351 | /// Returns `true` iff the error set type `orig_err_set_ty` contains no errors. |
| 31352 | /// |
| 31353 | /// This is used to give comptime answers for whether `error{}!T` is an error or a payload, as well |
| 31354 | /// as whether `?error{}` is null. The type `error{}` cannot be NPV, as it has runtime bits, but the |
| 31355 | /// only value of that type which can exist is `undefined`; semantically it has no "legal" value. |
| 31356 | /// TODO: this runs into some unsolved language design questions about such types. Performing a |
| 31357 | /// coercion from `@as(E, undefined)` to `E!T` needs to semantically result in an `undefined` error |
| 31358 | /// union if our implementation is to be legal, and likewise for coercing `@as(E, undefined)` to |
| 31359 | /// `?E` (for an error set `E`) because our implementation uses the zero error value at runtime to |
| 31360 | /// represent `null`. The unsolved problem is the exact rules for `undefined` propagation through |
| 31361 | /// these types: for instance, what if `@as(u32, undfined)` is coerced to `?u32`? What about error |
| 31362 | /// union *payloads*, i.e. `@as(u32, undefined)` to `E!u32`? That one is analagous to the optional |
| 31363 | /// example in some ways, but right now I believe there is code which relies on that coercion giving |
| 31364 | /// a well-defined error union with an `undefined` payload. |
| 31365 | /// Relevant issues/discussions: |
| 31366 | /// * https://github.com/ziglang/zig/issues/1831 |
| 31367 | /// * https://github.com/ziglang/zig/issues/6762 |
| 31368 | /// * https://github.com/ziglang/zig/issues/1831#issuecomment-722129239 |
| 31369 | fn resolveErrSetIsEmpty( |
| 31370 | sema: *Sema, |
| 31371 | block: *Block, |
| 31372 | src: LazySrcLoc, |
| 31373 | orig_err_set_ty: Type, |
| 31374 | ) CompileError!bool { |
| 31375 | const ip = &sema.pt.zcu.intern_pool; |
| 31376 | err_set: switch (orig_err_set_ty.toIntern()) { |
| 31377 | .anyerror_type => return false, |
| 31378 | .adhoc_inferred_error_set_type => { |
| 31379 | // This is *our* error set; that is, we're currently analyzing the function |
| 31380 | // which owns it. Trying to resolve it now would cause a dependency loop. |
| 31381 | // Instead, accept that we don't know. |
| 31382 | return false; |
| 31383 | }, |
| 31384 | else => |err_set_ty| switch (ip.indexToKey(err_set_ty)) { |
| 31385 | .error_set_type => |es| return es.names.len == 0, |
| 31386 | .inferred_error_set_type => |func_index| { |
| 31387 | if (sema.fn_ret_ty_ies) |ies| { |
| 31388 | if (ies.func == func_index) { |
| 31389 | // This is *our* error set; that is, we're currently analyzing the function |
| 31390 | // which owns it. Trying to resolve it now would cause a dependency loop. |
| 31391 | // Instead, accept that we don't know. |
| 31392 | return false; |
| 31393 | } |
| 31394 | } |
| 31395 | try sema.ensureFuncIesResolved(block, src, func_index); |
| 31396 | continue :err_set ip.funcIesResolvedUnordered(func_index); |
| 31397 | }, |
| 31398 | else => unreachable, |
| 31399 | }, |
| 31400 | } |
| 31401 | } |
| 31402 | |
| 31403 | fn analyzeIsNonErr( |
| 31404 | sema: *Sema, |
| 31405 | block: *Block, |
| 31406 | src: LazySrcLoc, |
| 31407 | operand: Air.Inst.Ref, |
| 31408 | ) CompileError!Air.Inst.Ref { |
| 31409 | if (try sema.resolveIsNonErrVal(block, src, operand)) |val| { |
| 31410 | return .fromValue(val); |
| 31411 | } else { |
| 31412 | return block.addUnOp(.is_non_err, operand); |
| 31413 | } |
| 31414 | } |
| 31415 | |
| 31416 | fn analyzePtrIsNonErr( |
| 31417 | sema: *Sema, |
| 31418 | block: *Block, |
| 31419 | src: LazySrcLoc, |
| 31420 | operand: Air.Inst.Ref, |
| 31421 | ) CompileError!Air.Inst.Ref { |
| 31422 | if (try sema.resolvePtrIsNonErrVal(block, src, operand)) |val| { |
| 31423 | return .fromValue(val); |
| 31424 | } else { |
| 31425 | return block.addUnOp(.is_non_err_ptr, operand); |
| 31426 | } |
| 31427 | } |
| 31428 | |
| 31429 | fn analyzeSlice( |
| 31430 | sema: *Sema, |
| 31431 | block: *Block, |
| 31432 | src: LazySrcLoc, |
| 31433 | ptr_ptr: Air.Inst.Ref, |
| 31434 | uncasted_start: Air.Inst.Ref, |
| 31435 | uncasted_end_opt: Air.Inst.Ref, |
| 31436 | sentinel_opt: Air.Inst.Ref, |
| 31437 | sentinel_src: LazySrcLoc, |
| 31438 | ptr_src: LazySrcLoc, |
| 31439 | start_src: LazySrcLoc, |
| 31440 | end_src: LazySrcLoc, |
| 31441 | by_length: bool, |
| 31442 | ) CompileError!Air.Inst.Ref { |
| 31443 | const pt = sema.pt; |
| 31444 | const zcu = pt.zcu; |
| 31445 | // Slice expressions can operate on a variable whose type is an array. This requires |
| 31446 | // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer. |
| 31447 | const ptr_ptr_ty = sema.typeOf(ptr_ptr); |
| 31448 | const ptr_ptr_child_ty = switch (ptr_ptr_ty.zigTypeTag(zcu)) { |
| 31449 | .pointer => ptr_ptr_ty.childType(zcu), |
| 31450 | else => return sema.fail(block, ptr_src, "expected pointer, found '{f}'", .{ptr_ptr_ty.fmt(pt)}), |
| 31451 | }; |
| 31452 | |
| 31453 | var array_ty = ptr_ptr_child_ty; |
| 31454 | var slice_ty = ptr_ptr_ty; |
| 31455 | var ptr_or_slice = ptr_ptr; |
| 31456 | var elem_ty: Type = undefined; |
| 31457 | var ptr_sentinel: ?Value = null; |
| 31458 | switch (ptr_ptr_child_ty.zigTypeTag(zcu)) { |
| 31459 | .array => { |
| 31460 | ptr_sentinel = ptr_ptr_child_ty.sentinel(zcu); |
| 31461 | elem_ty = ptr_ptr_child_ty.childType(zcu); |
| 31462 | }, |
| 31463 | .pointer => switch (ptr_ptr_child_ty.ptrSize(zcu)) { |
| 31464 | .one => { |
| 31465 | const double_child_ty = ptr_ptr_child_ty.childType(zcu); |
| 31466 | ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); |
| 31467 | if (double_child_ty.zigTypeTag(zcu) == .array) { |
| 31468 | ptr_sentinel = double_child_ty.sentinel(zcu); |
| 31469 | slice_ty = ptr_ptr_child_ty; |
| 31470 | array_ty = double_child_ty; |
| 31471 | elem_ty = double_child_ty.childType(zcu); |
| 31472 | } else { |
| 31473 | if (uncasted_end_opt == .none) { |
| 31474 | return sema.fail(block, src, "slice of single-item pointer must be bounded", .{}); |
| 31475 | } |
| 31476 | const start_value = try sema.resolveConstDefinedValue( |
| 31477 | block, |
| 31478 | start_src, |
| 31479 | uncasted_start, |
| 31480 | .{ .simple = .slice_single_item_ptr_bounds }, |
| 31481 | ); |
| 31482 | |
| 31483 | const end_value = try sema.resolveConstDefinedValue( |
| 31484 | block, |
| 31485 | end_src, |
| 31486 | uncasted_end_opt, |
| 31487 | .{ .simple = .slice_single_item_ptr_bounds }, |
| 31488 | ); |
| 31489 | |
| 31490 | const bounds_error_message = "slice of single-item pointer must have bounds [0..0], [0..1], or [1..1]"; |
| 31491 | if (try sema.compareScalar(start_value, .neq, end_value, .comptime_int)) { |
| 31492 | if (try sema.compareScalar(start_value, .neq, Value.zero_comptime_int, .comptime_int)) { |
| 31493 | const msg = msg: { |
| 31494 | const msg = try sema.errMsg(start_src, bounds_error_message, .{}); |
| 31495 | errdefer msg.destroy(sema.gpa); |
| 31496 | try sema.errNote( |
| 31497 | start_src, |
| 31498 | msg, |
| 31499 | "expected '{f}', found '{f}'", |
| 31500 | .{ |
| 31501 | Value.zero_comptime_int.fmtValueSema(pt, sema), |
| 31502 | start_value.fmtValueSema(pt, sema), |
| 31503 | }, |
| 31504 | ); |
| 31505 | break :msg msg; |
| 31506 | }; |
| 31507 | return sema.failWithOwnedErrorMsg(block, msg); |
| 31508 | } else if (try sema.compareScalar(end_value, .neq, Value.one_comptime_int, .comptime_int)) { |
| 31509 | const msg = msg: { |
| 31510 | const msg = try sema.errMsg(end_src, bounds_error_message, .{}); |
| 31511 | errdefer msg.destroy(sema.gpa); |
| 31512 | try sema.errNote( |
| 31513 | end_src, |
| 31514 | msg, |
| 31515 | "expected '{f}', found '{f}'", |
| 31516 | .{ |
| 31517 | Value.one_comptime_int.fmtValueSema(pt, sema), |
| 31518 | end_value.fmtValueSema(pt, sema), |
| 31519 | }, |
| 31520 | ); |
| 31521 | break :msg msg; |
| 31522 | }; |
| 31523 | return sema.failWithOwnedErrorMsg(block, msg); |
| 31524 | } |
| 31525 | } else { |
| 31526 | if (try sema.compareScalar(end_value, .gt, Value.one_comptime_int, .comptime_int)) { |
| 31527 | return sema.fail( |
| 31528 | block, |
| 31529 | end_src, |
| 31530 | "end index {f} out of bounds for slice of single-item pointer", |
| 31531 | .{end_value.fmtValueSema(pt, sema)}, |
| 31532 | ); |
| 31533 | } |
| 31534 | } |
| 31535 | |
| 31536 | array_ty = try pt.arrayType(.{ |
| 31537 | .len = 1, |
| 31538 | .child = double_child_ty.toIntern(), |
| 31539 | }); |
| 31540 | const ptr_info = ptr_ptr_child_ty.ptrInfo(zcu); |
| 31541 | slice_ty = try pt.ptrType(.{ |
| 31542 | .child = array_ty.toIntern(), |
| 31543 | .flags = .{ |
| 31544 | .alignment = ptr_info.flags.alignment, |
| 31545 | .is_const = ptr_info.flags.is_const, |
| 31546 | .is_allowzero = ptr_info.flags.is_allowzero, |
| 31547 | .is_volatile = ptr_info.flags.is_volatile, |
| 31548 | .address_space = ptr_info.flags.address_space, |
| 31549 | }, |
| 31550 | }); |
| 31551 | elem_ty = double_child_ty; |
| 31552 | } |
| 31553 | }, |
| 31554 | .many, .c => { |
| 31555 | ptr_sentinel = ptr_ptr_child_ty.sentinel(zcu); |
| 31556 | ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); |
| 31557 | slice_ty = ptr_ptr_child_ty; |
| 31558 | array_ty = ptr_ptr_child_ty; |
| 31559 | elem_ty = ptr_ptr_child_ty.childType(zcu); |
| 31560 | |
| 31561 | if (ptr_ptr_child_ty.ptrSize(zcu) == .c) { |
| 31562 | if (try sema.resolveDefinedValue(block, ptr_src, ptr_or_slice)) |ptr_val| { |
| 31563 | if (ptr_val.isNull(zcu)) { |
| 31564 | return sema.fail(block, src, "slice of null pointer", .{}); |
| 31565 | } |
| 31566 | } |
| 31567 | } |
| 31568 | }, |
| 31569 | .slice => { |
| 31570 | ptr_sentinel = ptr_ptr_child_ty.sentinel(zcu); |
| 31571 | ptr_or_slice = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src); |
| 31572 | slice_ty = ptr_ptr_child_ty; |
| 31573 | array_ty = ptr_ptr_child_ty; |
| 31574 | elem_ty = ptr_ptr_child_ty.childType(zcu); |
| 31575 | }, |
| 31576 | }, |
| 31577 | else => return sema.fail(block, src, "slice of non-array type '{f}'", .{ptr_ptr_child_ty.fmt(pt)}), |
| 31578 | } |
| 31579 | |
| 31580 | try sema.ensureLayoutResolved(elem_ty, src, .ptr_access); |
| 31581 | try sema.checkSpirvSliceAllowed(block, src, slice_ty.ptrInfo(zcu).flags.address_space); |
| 31582 | |
| 31583 | const ptr = if (slice_ty.isSlice(zcu)) |
| 31584 | try sema.analyzeSlicePtr(block, ptr_src, ptr_or_slice, slice_ty) |
| 31585 | else if (array_ty.zigTypeTag(zcu) == .array) ptr: { |
| 31586 | var manyptr_ty_key = zcu.intern_pool.indexToKey(slice_ty.toIntern()).ptr_type; |
| 31587 | assert(manyptr_ty_key.child == array_ty.toIntern()); |
| 31588 | assert(manyptr_ty_key.flags.size == .one); |
| 31589 | manyptr_ty_key.child = elem_ty.toIntern(); |
| 31590 | manyptr_ty_key.flags.size = .many; |
| 31591 | break :ptr try sema.coerceCompatiblePtrs(block, try pt.ptrType(manyptr_ty_key), ptr_or_slice, ptr_src); |
| 31592 | } else ptr_or_slice; |
| 31593 | |
| 31594 | const start = try sema.coerce(block, .usize, uncasted_start, start_src); |
| 31595 | const new_ptr = try sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, start_src); |
| 31596 | const new_ptr_ty = sema.typeOf(new_ptr); |
| 31597 | |
| 31598 | // true if and only if the end index of the slice, implicitly or explicitly, equals |
| 31599 | // the length of the underlying object being sliced. we might learn the length of the |
| 31600 | // underlying object because it is an array (which has the length in the type), or |
| 31601 | // we might learn of the length because it is a comptime-known slice value. |
| 31602 | var end_is_len = uncasted_end_opt == .none; |
| 31603 | const end = e: { |
| 31604 | if (array_ty.zigTypeTag(zcu) == .array) { |
| 31605 | const len_val = try pt.intValue(.usize, array_ty.arrayLen(zcu)); |
| 31606 | |
| 31607 | if (!end_is_len) { |
| 31608 | const end = if (by_length) end: { |
| 31609 | const len = try sema.coerce(block, .usize, uncasted_end_opt, end_src); |
| 31610 | const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); |
| 31611 | break :end try sema.coerce(block, .usize, uncasted_end, end_src); |
| 31612 | } else try sema.coerce(block, .usize, uncasted_end_opt, end_src); |
| 31613 | if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { |
| 31614 | const len_s_val = try pt.intValue( |
| 31615 | .usize, |
| 31616 | array_ty.arrayLenIncludingSentinel(zcu), |
| 31617 | ); |
| 31618 | if (!(try sema.compareAll(end_val, .lte, len_s_val, .usize))) { |
| 31619 | const sentinel_label: []const u8 = if (array_ty.sentinel(zcu) != null) |
| 31620 | " +1 (sentinel)" |
| 31621 | else |
| 31622 | ""; |
| 31623 | |
| 31624 | return sema.fail( |
| 31625 | block, |
| 31626 | end_src, |
| 31627 | "end index {f} out of bounds for array of length {f}{s}", |
| 31628 | .{ |
| 31629 | end_val.fmtValueSema(pt, sema), |
| 31630 | len_val.fmtValueSema(pt, sema), |
| 31631 | sentinel_label, |
| 31632 | }, |
| 31633 | ); |
| 31634 | } |
| 31635 | |
| 31636 | // end_is_len is only true if we are NOT using the sentinel |
| 31637 | // length. For sentinel-length, we don't want the type to |
| 31638 | // contain the sentinel. |
| 31639 | if (end_val.eql(len_val, .usize, zcu)) { |
| 31640 | end_is_len = true; |
| 31641 | } |
| 31642 | } |
| 31643 | break :e end; |
| 31644 | } |
| 31645 | |
| 31646 | break :e Air.internedToRef(len_val.toIntern()); |
| 31647 | } else if (slice_ty.isSlice(zcu)) { |
| 31648 | if (!end_is_len) { |
| 31649 | const end = if (by_length) end: { |
| 31650 | const len = try sema.coerce(block, .usize, uncasted_end_opt, end_src); |
| 31651 | const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); |
| 31652 | break :end try sema.coerce(block, .usize, uncasted_end, end_src); |
| 31653 | } else try sema.coerce(block, .usize, uncasted_end_opt, end_src); |
| 31654 | if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { |
| 31655 | if (sema.resolveValue(ptr_or_slice)) |slice_val| { |
| 31656 | if (slice_val.isUndef(zcu)) { |
| 31657 | return sema.fail(block, src, "slice of undefined", .{}); |
| 31658 | } |
| 31659 | const has_sentinel = slice_ty.sentinel(zcu) != null; |
| 31660 | const slice_len = slice_val.sliceLen(zcu); |
| 31661 | const len_plus_sent = slice_len + @intFromBool(has_sentinel); |
| 31662 | const slice_len_val_with_sentinel = try pt.intValue(.usize, len_plus_sent); |
| 31663 | if (!(try sema.compareAll(end_val, .lte, slice_len_val_with_sentinel, .usize))) { |
| 31664 | const sentinel_label: []const u8 = if (has_sentinel) |
| 31665 | " +1 (sentinel)" |
| 31666 | else |
| 31667 | ""; |
| 31668 | |
| 31669 | return sema.fail( |
| 31670 | block, |
| 31671 | end_src, |
| 31672 | "end index {f} out of bounds for slice of length {d}{s}", |
| 31673 | .{ |
| 31674 | end_val.fmtValueSema(pt, sema), |
| 31675 | slice_val.sliceLen(zcu), |
| 31676 | sentinel_label, |
| 31677 | }, |
| 31678 | ); |
| 31679 | } |
| 31680 | |
| 31681 | // If the slice has a sentinel, we consider end_is_len |
| 31682 | // is only true if it equals the length WITHOUT the |
| 31683 | // sentinel, so we don't add a sentinel type. |
| 31684 | const slice_len_val = try pt.intValue(.usize, slice_len); |
| 31685 | if (end_val.eql(slice_len_val, .usize, zcu)) { |
| 31686 | end_is_len = true; |
| 31687 | } |
| 31688 | } |
| 31689 | } |
| 31690 | break :e end; |
| 31691 | } |
| 31692 | break :e try sema.analyzeSliceLen(block, src, ptr_or_slice); |
| 31693 | } |
| 31694 | if (!end_is_len) { |
| 31695 | if (by_length) { |
| 31696 | const len = try sema.coerce(block, .usize, uncasted_end_opt, end_src); |
| 31697 | const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); |
| 31698 | break :e try sema.coerce(block, .usize, uncasted_end, end_src); |
| 31699 | } else break :e try sema.coerce(block, .usize, uncasted_end_opt, end_src); |
| 31700 | } |
| 31701 | |
| 31702 | // when slicing a many-item pointer, if a sentinel `S` is provided as in `ptr[a.. :S]`, it |
| 31703 | // must match the sentinel of `@TypeOf(ptr)`. |
| 31704 | sentinel_check: { |
| 31705 | if (sentinel_opt == .none) break :sentinel_check; |
| 31706 | const provided = provided: { |
| 31707 | const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src); |
| 31708 | try checkSentinelType(sema, block, sentinel_src, elem_ty); |
| 31709 | break :provided try sema.resolveConstDefinedValue( |
| 31710 | block, |
| 31711 | sentinel_src, |
| 31712 | casted, |
| 31713 | .{ .simple = .slice_sentinel }, |
| 31714 | ); |
| 31715 | }; |
| 31716 | |
| 31717 | if (ptr_sentinel) |current| { |
| 31718 | if (provided.toIntern() == current.toIntern()) break :sentinel_check; |
| 31719 | } |
| 31720 | |
| 31721 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 31722 | const msg = try sema.errMsg(sentinel_src, "sentinel-terminated slicing of many-item pointer must match existing sentinel", .{}); |
| 31723 | errdefer msg.destroy(sema.gpa); |
| 31724 | if (ptr_sentinel) |current| { |
| 31725 | try sema.errNote(sentinel_src, msg, "expected sentinel '{f}', found '{f}'", .{ current.fmtValue(pt), provided.fmtValue(pt) }); |
| 31726 | } else { |
| 31727 | try sema.errNote(ptr_src, msg, "type '{f}' does not have a sentinel", .{slice_ty.fmt(pt)}); |
| 31728 | } |
| 31729 | try sema.errNote(src, msg, "use @ptrCast to cast pointer sentinel", .{}); |
| 31730 | break :msg msg; |
| 31731 | }); |
| 31732 | } |
| 31733 | return sema.analyzePtrArithmetic(block, src, ptr, start, .ptr_add, start_src); |
| 31734 | }; |
| 31735 | |
| 31736 | const sentinel = s: { |
| 31737 | if (sentinel_opt != .none) { |
| 31738 | const casted = try sema.coerce(block, elem_ty, sentinel_opt, sentinel_src); |
| 31739 | try checkSentinelType(sema, block, sentinel_src, elem_ty); |
| 31740 | break :s try sema.resolveConstDefinedValue(block, sentinel_src, casted, .{ .simple = .slice_sentinel }); |
| 31741 | } |
| 31742 | // If we are slicing to the end of something that is sentinel-terminated |
| 31743 | // then the resulting slice type is also sentinel-terminated. |
| 31744 | if (end_is_len) { |
| 31745 | if (ptr_sentinel) |sent| { |
| 31746 | break :s sent; |
| 31747 | } |
| 31748 | } |
| 31749 | break :s null; |
| 31750 | }; |
| 31751 | const slice_sentinel = if (sentinel_opt != .none) sentinel else null; |
| 31752 | |
| 31753 | var checked_start_lte_end = by_length; |
| 31754 | var runtime_src: ?LazySrcLoc = null; |
| 31755 | |
| 31756 | // requirement: start <= end |
| 31757 | if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| { |
| 31758 | if (try sema.compareAll(start_val, .eq, .zero_usize, .usize)) { |
| 31759 | checked_start_lte_end = true; |
| 31760 | } |
| 31761 | if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { |
| 31762 | if (!checked_start_lte_end and |
| 31763 | !by_length and |
| 31764 | !(try sema.compareAll(start_val, .lte, end_val, .usize))) |
| 31765 | { |
| 31766 | return sema.fail( |
| 31767 | block, |
| 31768 | start_src, |
| 31769 | "start index {f} is larger than end index {f}", |
| 31770 | .{ |
| 31771 | start_val.fmtValueSema(pt, sema), |
| 31772 | end_val.fmtValueSema(pt, sema), |
| 31773 | }, |
| 31774 | ); |
| 31775 | } |
| 31776 | checked_start_lte_end = true; |
| 31777 | if (sema.resolveValue(new_ptr)) |ptr_val| sentinel_check: { |
| 31778 | const expected_sentinel = sentinel orelse break :sentinel_check; |
| 31779 | const start_int = start_val.toUnsignedInt(zcu); |
| 31780 | const end_int = end_val.toUnsignedInt(zcu); |
| 31781 | const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int); |
| 31782 | |
| 31783 | const many_ptr_ty = try pt.manyConstPtrType(elem_ty); |
| 31784 | const many_ptr_val = try pt.getCoerced(ptr_val, many_ptr_ty); |
| 31785 | const elem_ptr = try many_ptr_val.ptrElem(sentinel_index, pt); |
| 31786 | const res = try sema.pointerDerefExtra(block, src, elem_ptr); |
| 31787 | const actual_sentinel = switch (res) { |
| 31788 | .runtime_load => break :sentinel_check, |
| 31789 | .val => |v| v, |
| 31790 | .needed_well_defined => |ty| return sema.fail( |
| 31791 | block, |
| 31792 | src, |
| 31793 | "comptime dereference requires '{f}' to have a well-defined layout", |
| 31794 | .{ty.fmt(pt)}, |
| 31795 | ), |
| 31796 | .out_of_bounds => |ty| return sema.fail( |
| 31797 | block, |
| 31798 | end_src, |
| 31799 | "slice end index {d} exceeds bounds of containing decl of type '{f}'", |
| 31800 | .{ end_int, ty.fmt(pt) }, |
| 31801 | ), |
| 31802 | }; |
| 31803 | |
| 31804 | if (!actual_sentinel.eql(expected_sentinel, elem_ty, zcu)) { |
| 31805 | const msg = msg: { |
| 31806 | const msg = try sema.errMsg(src, "value in memory does not match slice sentinel", .{}); |
| 31807 | errdefer msg.destroy(sema.gpa); |
| 31808 | try sema.errNote(src, msg, "expected '{f}', found '{f}'", .{ |
| 31809 | expected_sentinel.fmtValueSema(pt, sema), |
| 31810 | actual_sentinel.fmtValueSema(pt, sema), |
| 31811 | }); |
| 31812 | |
| 31813 | break :msg msg; |
| 31814 | }; |
| 31815 | return sema.failWithOwnedErrorMsg(block, msg); |
| 31816 | } |
| 31817 | } else { |
| 31818 | runtime_src = ptr_src; |
| 31819 | } |
| 31820 | } else { |
| 31821 | runtime_src = end_src; |
| 31822 | } |
| 31823 | } else { |
| 31824 | runtime_src = start_src; |
| 31825 | } |
| 31826 | |
| 31827 | if (!checked_start_lte_end and block.wantSafety() and !block.isComptime()) { |
| 31828 | // requirement: start <= end |
| 31829 | assert(!block.isComptime()); |
| 31830 | try sema.requireRuntimeBlock(block, src, runtime_src.?); |
| 31831 | const ok = try block.addBinOp(.cmp_lte, start, end); |
| 31832 | try sema.addSafetyCheckCall(block, src, ok, .@"panic.startGreaterThanEnd", &.{ start, end }); |
| 31833 | } |
| 31834 | const new_len = if (by_length) |
| 31835 | try sema.coerce(block, .usize, uncasted_end_opt, end_src) |
| 31836 | else |
| 31837 | try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false); |
| 31838 | const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len); |
| 31839 | |
| 31840 | const new_ptr_ty_info = new_ptr_ty.ptrInfo(zcu); |
| 31841 | const new_allowzero = new_ptr_ty_info.flags.is_allowzero and sema.typeOf(ptr).ptrSize(zcu) != .c; |
| 31842 | |
| 31843 | if (opt_new_len_val) |new_len_val| { |
| 31844 | const new_len_int = new_len_val.toUnsignedInt(zcu); |
| 31845 | |
| 31846 | const return_ty = try pt.ptrType(.{ |
| 31847 | .child = (try pt.arrayType(.{ |
| 31848 | .len = new_len_int, |
| 31849 | .sentinel = if (sentinel) |s| s.toIntern() else .none, |
| 31850 | .child = elem_ty.toIntern(), |
| 31851 | })).toIntern(), |
| 31852 | .flags = .{ |
| 31853 | .alignment = new_ptr_ty_info.flags.alignment, |
| 31854 | .is_const = new_ptr_ty_info.flags.is_const, |
| 31855 | .is_allowzero = new_allowzero, |
| 31856 | .is_volatile = new_ptr_ty_info.flags.is_volatile, |
| 31857 | .address_space = new_ptr_ty_info.flags.address_space, |
| 31858 | }, |
| 31859 | }); |
| 31860 | |
| 31861 | const opt_new_ptr_val = sema.resolveValue(new_ptr); |
| 31862 | const new_ptr_val = opt_new_ptr_val orelse { |
| 31863 | const result = try block.addTyOp(.ptr_cast, return_ty, new_ptr); |
| 31864 | if (block.wantSafety()) { |
| 31865 | // requirement: slicing C ptr is non-null |
| 31866 | if (ptr_ptr_child_ty.isCPtr(zcu)) { |
| 31867 | const is_non_null = try block.addUnOp(.is_non_null, ptr); |
| 31868 | try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); |
| 31869 | } |
| 31870 | |
| 31871 | bounds_check: { |
| 31872 | const actual_len = if (array_ty.zigTypeTag(zcu) == .array) |
| 31873 | try pt.intRef(.usize, array_ty.arrayLenIncludingSentinel(zcu)) |
| 31874 | else if (slice_ty.isSlice(zcu)) l: { |
| 31875 | const slice_len = try sema.analyzeSliceLen(block, src, ptr_or_slice); |
| 31876 | break :l if (slice_ty.sentinel(zcu) == null) |
| 31877 | slice_len |
| 31878 | else |
| 31879 | try sema.analyzeArithmetic(block, .add, slice_len, .one, src, end_src, end_src, true); |
| 31880 | } else break :bounds_check; |
| 31881 | |
| 31882 | const actual_end = if (slice_sentinel != null) |
| 31883 | try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true) |
| 31884 | else |
| 31885 | end; |
| 31886 | |
| 31887 | if (try sema.resolveDefinedValue(block, src, actual_len) == null or |
| 31888 | try sema.resolveDefinedValue(block, src, actual_end) == null) |
| 31889 | try sema.addSafetyCheckIndexOob(block, src, actual_end, actual_len, .cmp_lte); |
| 31890 | } |
| 31891 | |
| 31892 | // requirement: result[new_len] == slice_sentinel |
| 31893 | try sema.addSafetyCheckSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len); |
| 31894 | } |
| 31895 | return result; |
| 31896 | }; |
| 31897 | |
| 31898 | if (!new_ptr_val.isUndef(zcu)) { |
| 31899 | return Air.internedToRef((try pt.getCoerced(new_ptr_val, return_ty)).toIntern()); |
| 31900 | } |
| 31901 | |
| 31902 | // Special case: @as([]i32, undefined)[x..x] |
| 31903 | if (new_len_int == 0) { |
| 31904 | return pt.undefRef(return_ty); |
| 31905 | } |
| 31906 | |
| 31907 | return sema.fail(block, src, "non-zero length slice of undefined pointer", .{}); |
| 31908 | } |
| 31909 | |
| 31910 | const return_ty = try pt.ptrType(.{ |
| 31911 | .child = elem_ty.toIntern(), |
| 31912 | .sentinel = if (sentinel) |s| s.toIntern() else .none, |
| 31913 | .flags = .{ |
| 31914 | .size = .slice, |
| 31915 | .alignment = new_ptr_ty_info.flags.alignment, |
| 31916 | .is_const = new_ptr_ty_info.flags.is_const, |
| 31917 | .is_volatile = new_ptr_ty_info.flags.is_volatile, |
| 31918 | .is_allowzero = new_allowzero, |
| 31919 | .address_space = new_ptr_ty_info.flags.address_space, |
| 31920 | }, |
| 31921 | }); |
| 31922 | |
| 31923 | try sema.requireRuntimeBlock(block, src, runtime_src.?); |
| 31924 | if (block.wantSafety()) { |
| 31925 | // requirement: slicing C ptr is non-null |
| 31926 | if (ptr_ptr_child_ty.isCPtr(zcu)) { |
| 31927 | const is_non_null = try block.addUnOp(.is_non_null, ptr); |
| 31928 | try sema.addSafetyCheck(block, src, is_non_null, .unwrap_null); |
| 31929 | } |
| 31930 | |
| 31931 | // requirement: end <= len |
| 31932 | const opt_len_inst = if (array_ty.zigTypeTag(zcu) == .array) |
| 31933 | try pt.intRef(.usize, array_ty.arrayLenIncludingSentinel(zcu)) |
| 31934 | else if (slice_ty.isSlice(zcu)) blk: { |
| 31935 | if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| { |
| 31936 | // we don't need to add one for sentinels because the |
| 31937 | // underlying value data includes the sentinel |
| 31938 | break :blk try pt.intRef(.usize, slice_val.sliceLen(zcu)); |
| 31939 | } |
| 31940 | |
| 31941 | const slice_len_inst = try block.addTyOp(.slice_len, .usize, ptr_or_slice); |
| 31942 | if (slice_ty.sentinel(zcu) == null) break :blk slice_len_inst; |
| 31943 | |
| 31944 | // we have to add one because slice lengths don't include the sentinel |
| 31945 | break :blk try sema.analyzeArithmetic(block, .add, slice_len_inst, .one, src, end_src, end_src, true); |
| 31946 | } else null; |
| 31947 | if (opt_len_inst) |len_inst| { |
| 31948 | const actual_end = if (slice_sentinel != null) |
| 31949 | try sema.analyzeArithmetic(block, .add, end, .one, src, end_src, end_src, true) |
| 31950 | else |
| 31951 | end; |
| 31952 | try sema.addSafetyCheckIndexOob(block, src, actual_end, len_inst, .cmp_lte); |
| 31953 | } |
| 31954 | } |
| 31955 | const result = try block.addInst(.{ |
| 31956 | .tag = .slice, |
| 31957 | .data = .{ .ty_pl = .{ |
| 31958 | .ty = return_ty, |
| 31959 | .payload = try sema.addExtra(Air.Bin{ |
| 31960 | .lhs = new_ptr, |
| 31961 | .rhs = new_len, |
| 31962 | }), |
| 31963 | } }, |
| 31964 | }); |
| 31965 | if (block.wantSafety()) { |
| 31966 | // requirement: result[new_len] == slice_sentinel |
| 31967 | try sema.addSafetyCheckSentinelMismatch(block, src, slice_sentinel, elem_ty, result, new_len); |
| 31968 | } |
| 31969 | return result; |
| 31970 | } |
| 31971 | |
| 31972 | /// Asserts that lhs and rhs types are both numeric. |
| 31973 | fn cmpNumeric( |
| 31974 | sema: *Sema, |
| 31975 | block: *Block, |
| 31976 | src: LazySrcLoc, |
| 31977 | uncasted_lhs: Air.Inst.Ref, |
| 31978 | uncasted_rhs: Air.Inst.Ref, |
| 31979 | op: std.math.CompareOperator, |
| 31980 | lhs_src: LazySrcLoc, |
| 31981 | rhs_src: LazySrcLoc, |
| 31982 | ) CompileError!Air.Inst.Ref { |
| 31983 | const pt = sema.pt; |
| 31984 | const zcu = pt.zcu; |
| 31985 | const lhs_ty = sema.typeOf(uncasted_lhs); |
| 31986 | const rhs_ty = sema.typeOf(uncasted_rhs); |
| 31987 | |
| 31988 | assert(lhs_ty.isNumeric(zcu)); |
| 31989 | assert(rhs_ty.isNumeric(zcu)); |
| 31990 | |
| 31991 | const lhs_ty_tag = lhs_ty.zigTypeTag(zcu); |
| 31992 | const rhs_ty_tag = rhs_ty.zigTypeTag(zcu); |
| 31993 | const target = zcu.getTarget(); |
| 31994 | |
| 31995 | // One exception to heterogeneous comparison: comptime_float needs to |
| 31996 | // coerce to fixed-width float. |
| 31997 | |
| 31998 | const lhs = if (lhs_ty_tag == .comptime_float and rhs_ty_tag == .float) |
| 31999 | try sema.coerce(block, rhs_ty, uncasted_lhs, lhs_src) |
| 32000 | else |
| 32001 | uncasted_lhs; |
| 32002 | |
| 32003 | const rhs = if (lhs_ty_tag == .float and rhs_ty_tag == .comptime_float) |
| 32004 | try sema.coerce(block, lhs_ty, uncasted_rhs, rhs_src) |
| 32005 | else |
| 32006 | uncasted_rhs; |
| 32007 | |
| 32008 | const maybe_lhs_val = sema.resolveValue(lhs); |
| 32009 | const maybe_rhs_val = sema.resolveValue(rhs); |
| 32010 | |
| 32011 | // If the LHS is const, check if there is a guaranteed result which does not depend on ths RHS value. |
| 32012 | if (maybe_lhs_val) |lhs_val| { |
| 32013 | // Result based on comparison exceeding type bounds |
| 32014 | if (!lhs_val.isUndef(zcu) and (lhs_ty_tag == .int or lhs_ty_tag == .comptime_int) and rhs_ty.isInt(zcu)) { |
| 32015 | if (try sema.compareIntsOnlyPossibleResult(lhs_val, op, rhs_ty)) |res| { |
| 32016 | return if (res) .bool_true else .bool_false; |
| 32017 | } |
| 32018 | } |
| 32019 | // Result based on NaN comparison |
| 32020 | if (lhs_val.isNan(zcu)) { |
| 32021 | return if (op == .neq) .bool_true else .bool_false; |
| 32022 | } |
| 32023 | // Result based on inf comparison to int |
| 32024 | if (lhs_val.isInf(zcu) and rhs_ty_tag == .int) return switch (op) { |
| 32025 | .neq => .bool_true, |
| 32026 | .eq => .bool_false, |
| 32027 | .gt, .gte => if (lhs_val.isNegativeInf(zcu)) .bool_false else .bool_true, |
| 32028 | .lt, .lte => if (lhs_val.isNegativeInf(zcu)) .bool_true else .bool_false, |
| 32029 | }; |
| 32030 | } |
| 32031 | |
| 32032 | // If the RHS is const, check if there is a guaranteed result which does not depend on ths LHS value. |
| 32033 | if (maybe_rhs_val) |rhs_val| { |
| 32034 | // Result based on comparison exceeding type bounds |
| 32035 | if (!rhs_val.isUndef(zcu) and (rhs_ty_tag == .int or rhs_ty_tag == .comptime_int) and lhs_ty.isInt(zcu)) { |
| 32036 | if (try sema.compareIntsOnlyPossibleResult(rhs_val, op.reverse(), lhs_ty)) |res| { |
| 32037 | return if (res) .bool_true else .bool_false; |
| 32038 | } |
| 32039 | } |
| 32040 | // Result based on NaN comparison |
| 32041 | if (rhs_val.isNan(zcu)) { |
| 32042 | return if (op == .neq) .bool_true else .bool_false; |
| 32043 | } |
| 32044 | // Result based on inf comparison to int |
| 32045 | if (rhs_val.isInf(zcu) and lhs_ty_tag == .int) return switch (op) { |
| 32046 | .neq => .bool_true, |
| 32047 | .eq => .bool_false, |
| 32048 | .gt, .gte => if (rhs_val.isNegativeInf(zcu)) .bool_true else .bool_false, |
| 32049 | .lt, .lte => if (rhs_val.isNegativeInf(zcu)) .bool_false else .bool_true, |
| 32050 | }; |
| 32051 | } |
| 32052 | |
| 32053 | // Any other comparison depends on both values, so the result is undef if either is undef. |
| 32054 | if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return .undef_bool; |
| 32055 | if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return .undef_bool; |
| 32056 | |
| 32057 | const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| rs: { |
| 32058 | if (maybe_rhs_val) |rhs_val| { |
| 32059 | return .fromValue(.makeBool(Value.compareHetero(lhs_val, op, rhs_val, zcu))); |
| 32060 | } else break :rs rhs_src; |
| 32061 | } else lhs_src; |
| 32062 | |
| 32063 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 32064 | |
| 32065 | // For floats, emit a float comparison instruction. |
| 32066 | const lhs_is_float = switch (lhs_ty_tag) { |
| 32067 | .float, .comptime_float => true, |
| 32068 | else => false, |
| 32069 | }; |
| 32070 | const rhs_is_float = switch (rhs_ty_tag) { |
| 32071 | .float, .comptime_float => true, |
| 32072 | else => false, |
| 32073 | }; |
| 32074 | |
| 32075 | if (lhs_is_float and rhs_is_float) { |
| 32076 | // Smaller fixed-width floats coerce to larger fixed-width floats. |
| 32077 | // comptime_float coerces to fixed-width float. |
| 32078 | const dest_ty = x: { |
| 32079 | if (lhs_ty_tag == .comptime_float) { |
| 32080 | break :x rhs_ty; |
| 32081 | } else if (rhs_ty_tag == .comptime_float) { |
| 32082 | break :x lhs_ty; |
| 32083 | } |
| 32084 | if (lhs_ty.floatBits(target) >= rhs_ty.floatBits(target)) { |
| 32085 | break :x lhs_ty; |
| 32086 | } else { |
| 32087 | break :x rhs_ty; |
| 32088 | } |
| 32089 | }; |
| 32090 | const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src); |
| 32091 | const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src); |
| 32092 | return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized), casted_lhs, casted_rhs); |
| 32093 | } |
| 32094 | |
| 32095 | // For mixed unsigned integer sizes, implicit cast both operands to the larger integer. |
| 32096 | // For mixed signed and unsigned integers, implicit cast both operands to a signed |
| 32097 | // integer with + 1 bit. |
| 32098 | // For mixed floats and integers, extract the integer part from the float, cast that to |
| 32099 | // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float, |
| 32100 | // add/subtract 1. |
| 32101 | const lhs_is_signed = if (maybe_lhs_val) |lhs_val| |
| 32102 | !lhs_val.compareAllWithZero(.gte, zcu) |
| 32103 | else |
| 32104 | (lhs_ty.isRuntimeFloat() or lhs_ty.isSignedInt(zcu)); |
| 32105 | const rhs_is_signed = if (maybe_rhs_val) |rhs_val| |
| 32106 | !rhs_val.compareAllWithZero(.gte, zcu) |
| 32107 | else |
| 32108 | (rhs_ty.isRuntimeFloat() or rhs_ty.isSignedInt(zcu)); |
| 32109 | const dest_int_is_signed = lhs_is_signed or rhs_is_signed; |
| 32110 | |
| 32111 | var dest_float_type: ?Type = null; |
| 32112 | |
| 32113 | var lhs_bits: usize = undefined; |
| 32114 | if (maybe_lhs_val) |lhs_val| { |
| 32115 | if (!rhs_is_signed) { |
| 32116 | switch (Value.order(lhs_val, .zero_comptime_int, zcu)) { |
| 32117 | .gt => {}, |
| 32118 | .eq => switch (op) { // LHS = 0, RHS is unsigned |
| 32119 | .lte => return .bool_true, |
| 32120 | .gt => return .bool_false, |
| 32121 | else => {}, |
| 32122 | }, |
| 32123 | .lt => switch (op) { // LHS < 0, RHS is unsigned |
| 32124 | .neq, .lt, .lte => return .bool_true, |
| 32125 | .eq, .gt, .gte => return .bool_false, |
| 32126 | }, |
| 32127 | } |
| 32128 | } |
| 32129 | if (lhs_is_float) { |
| 32130 | const float = lhs_val.toFloat(f128, zcu); |
| 32131 | var big_int: std.math.big.int.Mutable = .{ |
| 32132 | .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)), |
| 32133 | .len = undefined, |
| 32134 | .positive = undefined, |
| 32135 | }; |
| 32136 | switch (big_int.setFloat(float, .away)) { |
| 32137 | .inexact => switch (op) { |
| 32138 | .eq => return .bool_false, |
| 32139 | .neq => return .bool_true, |
| 32140 | else => {}, |
| 32141 | }, |
| 32142 | .exact => {}, |
| 32143 | } |
| 32144 | lhs_bits = big_int.toConst().bitCountTwosComp(); |
| 32145 | } else { |
| 32146 | lhs_bits = lhs_val.intBitCountTwosComp(zcu); |
| 32147 | } |
| 32148 | lhs_bits += @intFromBool(!lhs_is_signed and dest_int_is_signed); |
| 32149 | } else if (lhs_is_float) { |
| 32150 | dest_float_type = lhs_ty; |
| 32151 | } else { |
| 32152 | const int_info = lhs_ty.intInfo(zcu); |
| 32153 | lhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed); |
| 32154 | } |
| 32155 | |
| 32156 | var rhs_bits: usize = undefined; |
| 32157 | if (maybe_rhs_val) |rhs_val| { |
| 32158 | if (!lhs_is_signed) { |
| 32159 | switch (Value.order(rhs_val, .zero_comptime_int, zcu)) { |
| 32160 | .gt => {}, |
| 32161 | .eq => switch (op) { // RHS = 0, LHS is unsigned |
| 32162 | .gte => return .bool_true, |
| 32163 | .lt => return .bool_false, |
| 32164 | else => {}, |
| 32165 | }, |
| 32166 | .lt => switch (op) { // RHS < 0, LHS is unsigned |
| 32167 | .neq, .gt, .gte => return .bool_true, |
| 32168 | .eq, .lt, .lte => return .bool_false, |
| 32169 | }, |
| 32170 | } |
| 32171 | } |
| 32172 | if (rhs_is_float) { |
| 32173 | const float = rhs_val.toFloat(f128, zcu); |
| 32174 | var big_int: std.math.big.int.Mutable = .{ |
| 32175 | .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)), |
| 32176 | .len = undefined, |
| 32177 | .positive = undefined, |
| 32178 | }; |
| 32179 | switch (big_int.setFloat(float, .away)) { |
| 32180 | .inexact => switch (op) { |
| 32181 | .eq => return .bool_false, |
| 32182 | .neq => return .bool_true, |
| 32183 | else => {}, |
| 32184 | }, |
| 32185 | .exact => {}, |
| 32186 | } |
| 32187 | rhs_bits = big_int.toConst().bitCountTwosComp(); |
| 32188 | } else { |
| 32189 | rhs_bits = rhs_val.intBitCountTwosComp(zcu); |
| 32190 | } |
| 32191 | rhs_bits += @intFromBool(!rhs_is_signed and dest_int_is_signed); |
| 32192 | } else if (rhs_is_float) { |
| 32193 | dest_float_type = rhs_ty; |
| 32194 | } else { |
| 32195 | const int_info = rhs_ty.intInfo(zcu); |
| 32196 | rhs_bits = int_info.bits + @intFromBool(int_info.signedness == .unsigned and dest_int_is_signed); |
| 32197 | } |
| 32198 | |
| 32199 | const dest_ty = if (dest_float_type) |ft| ft else blk: { |
| 32200 | const max_bits = @max(lhs_bits, rhs_bits); |
| 32201 | const casted_bits = std.math.cast(u16, max_bits) orelse return sema.fail(block, src, "{d} exceeds maximum integer bit count", .{max_bits}); |
| 32202 | const signedness: std.lang.Signedness = if (dest_int_is_signed) .signed else .unsigned; |
| 32203 | break :blk try pt.intType(signedness, casted_bits); |
| 32204 | }; |
| 32205 | const casted_lhs = try sema.coerce(block, dest_ty, lhs, lhs_src); |
| 32206 | const casted_rhs = try sema.coerce(block, dest_ty, rhs, rhs_src); |
| 32207 | |
| 32208 | return block.addBinOp(Air.Inst.Tag.fromCmpOp(op, block.float_mode == .optimized), casted_lhs, casted_rhs); |
| 32209 | } |
| 32210 | |
| 32211 | /// Asserts that LHS value is an int or comptime int and not undefined, and |
| 32212 | /// that RHS type is an int. Given a const LHS and an unknown RHS, attempt to |
| 32213 | /// determine whether `op` has a guaranteed result. |
| 32214 | /// If it cannot be determined, returns null. |
| 32215 | /// Otherwise returns a bool for the guaranteed comparison operation. |
| 32216 | fn compareIntsOnlyPossibleResult( |
| 32217 | sema: *Sema, |
| 32218 | lhs_val: Value, |
| 32219 | op: std.math.CompareOperator, |
| 32220 | rhs_ty: Type, |
| 32221 | ) Allocator.Error!?bool { |
| 32222 | const pt = sema.pt; |
| 32223 | const zcu = pt.zcu; |
| 32224 | |
| 32225 | const min_rhs = try rhs_ty.minInt(pt, rhs_ty); |
| 32226 | const max_rhs = try rhs_ty.maxInt(pt, rhs_ty); |
| 32227 | |
| 32228 | if (min_rhs.toIntern() == max_rhs.toIntern()) { |
| 32229 | // RHS is effectively comptime-known. |
| 32230 | return Value.compareHetero(lhs_val, op, min_rhs, zcu); |
| 32231 | } |
| 32232 | |
| 32233 | const against_min = lhs_val.order(min_rhs, zcu); |
| 32234 | const against_max = lhs_val.order(max_rhs, zcu); |
| 32235 | |
| 32236 | switch (op) { |
| 32237 | .eq => { |
| 32238 | if (against_min.compare(.lt)) return false; |
| 32239 | if (against_max.compare(.gt)) return false; |
| 32240 | }, |
| 32241 | .neq => { |
| 32242 | if (against_min.compare(.lt)) return true; |
| 32243 | if (against_max.compare(.gt)) return true; |
| 32244 | }, |
| 32245 | .lt => { |
| 32246 | if (against_min.compare(.lt)) return true; |
| 32247 | if (against_max.compare(.gte)) return false; |
| 32248 | }, |
| 32249 | .gt => { |
| 32250 | if (against_max.compare(.gt)) return true; |
| 32251 | if (against_min.compare(.lte)) return false; |
| 32252 | }, |
| 32253 | .lte => { |
| 32254 | if (against_min.compare(.lte)) return true; |
| 32255 | if (against_max.compare(.gt)) return false; |
| 32256 | }, |
| 32257 | .gte => { |
| 32258 | if (against_max.compare(.gte)) return true; |
| 32259 | if (against_min.compare(.lt)) return false; |
| 32260 | }, |
| 32261 | } |
| 32262 | |
| 32263 | return null; |
| 32264 | } |
| 32265 | |
| 32266 | /// Asserts that lhs and rhs types are both vectors. |
| 32267 | fn cmpVector( |
| 32268 | sema: *Sema, |
| 32269 | block: *Block, |
| 32270 | src: LazySrcLoc, |
| 32271 | lhs: Air.Inst.Ref, |
| 32272 | rhs: Air.Inst.Ref, |
| 32273 | op: std.math.CompareOperator, |
| 32274 | lhs_src: LazySrcLoc, |
| 32275 | rhs_src: LazySrcLoc, |
| 32276 | ) CompileError!Air.Inst.Ref { |
| 32277 | const pt = sema.pt; |
| 32278 | const zcu = pt.zcu; |
| 32279 | const lhs_ty = sema.typeOf(lhs); |
| 32280 | const rhs_ty = sema.typeOf(rhs); |
| 32281 | assert(lhs_ty.zigTypeTag(zcu) == .vector); |
| 32282 | assert(rhs_ty.zigTypeTag(zcu) == .vector); |
| 32283 | try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src); |
| 32284 | |
| 32285 | const resolved_ty = try sema.resolvePeerTypes(block, src, &.{ lhs, rhs }, .{ .override = &.{ lhs_src, rhs_src } }); |
| 32286 | const casted_lhs = try sema.coerce(block, resolved_ty, lhs, lhs_src); |
| 32287 | const casted_rhs = try sema.coerce(block, resolved_ty, rhs, rhs_src); |
| 32288 | |
| 32289 | const result_ty = try pt.vectorType(.{ |
| 32290 | .len = lhs_ty.vectorLen(zcu), |
| 32291 | .child = .bool_type, |
| 32292 | }); |
| 32293 | |
| 32294 | const maybe_lhs_val = sema.resolveValue(casted_lhs); |
| 32295 | const maybe_rhs_val = sema.resolveValue(casted_rhs); |
| 32296 | if (maybe_lhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty); |
| 32297 | if (maybe_rhs_val) |v| if (v.isUndef(zcu)) return pt.undefRef(result_ty); |
| 32298 | |
| 32299 | const runtime_src: LazySrcLoc = if (maybe_lhs_val) |lhs_val| src: { |
| 32300 | if (maybe_rhs_val) |rhs_val| { |
| 32301 | const cmp_val = try sema.compareVector(lhs_val, op, rhs_val, resolved_ty); |
| 32302 | return Air.internedToRef(cmp_val.toIntern()); |
| 32303 | } else break :src rhs_src; |
| 32304 | } else lhs_src; |
| 32305 | |
| 32306 | try sema.requireRuntimeBlock(block, src, runtime_src); |
| 32307 | return block.addCmpVector(casted_lhs, casted_rhs, op); |
| 32308 | } |
| 32309 | |
| 32310 | fn wrapOptional( |
| 32311 | sema: *Sema, |
| 32312 | block: *Block, |
| 32313 | dest_ty: Type, |
| 32314 | inst: Air.Inst.Ref, |
| 32315 | inst_src: LazySrcLoc, |
| 32316 | ) !Air.Inst.Ref { |
| 32317 | if (sema.resolveValue(inst)) |val| { |
| 32318 | return Air.internedToRef((try sema.pt.intern(.{ .opt = .{ |
| 32319 | .ty = dest_ty.toIntern(), |
| 32320 | .val = val.toIntern(), |
| 32321 | } }))); |
| 32322 | } |
| 32323 | |
| 32324 | try sema.requireRuntimeBlock(block, inst_src, null); |
| 32325 | return block.addTyOp(.wrap_optional, dest_ty, inst); |
| 32326 | } |
| 32327 | |
| 32328 | fn wrapErrorUnionPayload( |
| 32329 | sema: *Sema, |
| 32330 | block: *Block, |
| 32331 | dest_ty: Type, |
| 32332 | inst: Air.Inst.Ref, |
| 32333 | inst_src: LazySrcLoc, |
| 32334 | ) !Air.Inst.Ref { |
| 32335 | const pt = sema.pt; |
| 32336 | const zcu = pt.zcu; |
| 32337 | const dest_payload_ty = dest_ty.errorUnionPayload(zcu); |
| 32338 | const coerced = try sema.coerceExtra(block, dest_payload_ty, inst, inst_src, .{ .report_err = false }); |
| 32339 | if (sema.resolveValue(coerced)) |val| { |
| 32340 | return Air.internedToRef((try pt.intern(.{ .error_union = .{ |
| 32341 | .ty = dest_ty.toIntern(), |
| 32342 | .val = .{ .payload = val.toIntern() }, |
| 32343 | } }))); |
| 32344 | } |
| 32345 | try sema.requireRuntimeBlock(block, inst_src, null); |
| 32346 | return block.addTyOp(.wrap_errunion_payload, dest_ty, coerced); |
| 32347 | } |
| 32348 | |
| 32349 | fn wrapErrorUnionSet( |
| 32350 | sema: *Sema, |
| 32351 | block: *Block, |
| 32352 | dest_ty: Type, |
| 32353 | inst: Air.Inst.Ref, |
| 32354 | inst_src: LazySrcLoc, |
| 32355 | ) !Air.Inst.Ref { |
| 32356 | const pt = sema.pt; |
| 32357 | const zcu = pt.zcu; |
| 32358 | const ip = &zcu.intern_pool; |
| 32359 | const dest_err_set_ty = dest_ty.errorUnionSet(zcu); |
| 32360 | const coerced = try sema.coerceExtra(block, dest_err_set_ty, inst, inst_src, .{ .report_err = false }); |
| 32361 | if (try sema.resolveDefinedValue(block, inst_src, coerced)) |error_val| { |
| 32362 | return .fromIntern(try pt.intern(.{ .error_union = .{ |
| 32363 | .ty = dest_ty.toIntern(), |
| 32364 | .val = .{ .err_name = ip.indexToKey(error_val.toIntern()).err.name }, |
| 32365 | } })); |
| 32366 | } else { |
| 32367 | return block.addTyOp(.wrap_errunion_err, dest_ty, coerced); |
| 32368 | } |
| 32369 | } |
| 32370 | |
| 32371 | /// Returns the enum tag value for the active tag of a tagged union value. |
| 32372 | /// |
| 32373 | /// Asserts that the type of `un` is a tagged union type. |
| 32374 | fn unionToTag(sema: *Sema, block: *Block, un: Air.Inst.Ref) !Air.Inst.Ref { |
| 32375 | const pt = sema.pt; |
| 32376 | const zcu = pt.zcu; |
| 32377 | const ip = &zcu.intern_pool; |
| 32378 | const union_obj = ip.loadUnionType(sema.typeOf(un).toIntern()); |
| 32379 | assert(union_obj.tag_usage == .tagged); |
| 32380 | if (sema.resolveValue(un)) |un_val| { |
| 32381 | return .fromValue(un_val.unionTag(zcu).?); |
| 32382 | } |
| 32383 | const enum_tag_ty: Type = .fromInterned(union_obj.enum_tag_type); |
| 32384 | if (!union_obj.has_runtime_tag) { |
| 32385 | // This means that only one field is possible. |
| 32386 | const field_index = for (union_obj.field_types.get(ip), 0..) |field_ty_ip, field_index| { |
| 32387 | const field_ty: Type = .fromInterned(field_ty_ip); |
| 32388 | if (field_ty.classify(zcu) != .no_possible_value) break field_index; |
| 32389 | } else unreachable; |
| 32390 | return .fromValue(try pt.enumValueFieldIndex(enum_tag_ty, @intCast(field_index))); |
| 32391 | } |
| 32392 | return block.addTyOp(.get_union_tag, enum_tag_ty, un); |
| 32393 | } |
| 32394 | |
| 32395 | const PeerResolveStrategy = enum { |
| 32396 | /// The type is not known. |
| 32397 | /// If refined no further, this is equivalent to `exact`. |
| 32398 | unknown, |
| 32399 | /// The type may be an error set or error union. |
| 32400 | /// If refined no further, it is an error set. |
| 32401 | error_set, |
| 32402 | /// The type must be some error union. |
| 32403 | error_union, |
| 32404 | /// The type may be @TypeOf(null), an optional or a C pointer. |
| 32405 | /// If refined no further, it is @TypeOf(null). |
| 32406 | nullable, |
| 32407 | /// The type must be some optional or a C pointer. |
| 32408 | /// If refined no further, it is an optional. |
| 32409 | optional, |
| 32410 | /// The type must be either an array or a vector. |
| 32411 | /// If refined no further, it is an array. |
| 32412 | array, |
| 32413 | /// The type must be a vector. |
| 32414 | vector, |
| 32415 | /// The type must be a C pointer. |
| 32416 | c_ptr, |
| 32417 | /// The type must be a pointer (C or not). |
| 32418 | /// If refined no further, it is a non-C pointer. |
| 32419 | ptr, |
| 32420 | /// The type must be a function or a pointer to a function. |
| 32421 | /// If refined no further, it is a function. |
| 32422 | func, |
| 32423 | /// The type must be an enum literal, or some specific enum or union. Which one is decided |
| 32424 | /// afterwards based on the types in question. |
| 32425 | enum_or_union, |
| 32426 | /// The type must be some integer or float type. |
| 32427 | /// If refined no further, it is `comptime_int`. |
| 32428 | comptime_int, |
| 32429 | /// The type must be some float type. |
| 32430 | /// If refined no further, it is `comptime_float`. |
| 32431 | comptime_float, |
| 32432 | /// The type must be some float or fixed-width integer type. |
| 32433 | /// If refined no further, it is some fixed-width integer type. |
| 32434 | fixed_int, |
| 32435 | /// The type must be some fixed-width float type. |
| 32436 | fixed_float, |
| 32437 | /// The type must be a tuple. |
| 32438 | tuple, |
| 32439 | /// The peers must all be of the same type. |
| 32440 | exact, |
| 32441 | |
| 32442 | /// Given two strategies, find a strategy that satisfies both, if one exists. If no such |
| 32443 | /// strategy exists, any strategy may be returned; an error will be emitted when the caller |
| 32444 | /// attempts to use the strategy to resolve the type. |
| 32445 | /// Strategy `a` comes from the peer in `reason_peer`, while strategy `b` comes from the peer at |
| 32446 | /// index `b_peer_idx`. `reason_peer` is updated to reflect the reason for the new strategy. |
| 32447 | fn merge(a: PeerResolveStrategy, b: PeerResolveStrategy, reason_peer: *usize, b_peer_idx: usize) PeerResolveStrategy { |
| 32448 | // Our merging should be order-independent. Thus, even though the union order is arbitrary, |
| 32449 | // by sorting the tags and switching first on the smaller, we have half as many cases to |
| 32450 | // worry about (since we avoid the duplicates). |
| 32451 | const s0_is_a = @backingInt(a) <= @backingInt(b); |
| 32452 | const s0 = if (s0_is_a) a else b; |
| 32453 | const s1 = if (s0_is_a) b else a; |
| 32454 | |
| 32455 | const ReasonMethod = enum { |
| 32456 | all_s0, |
| 32457 | all_s1, |
| 32458 | either, |
| 32459 | }; |
| 32460 | |
| 32461 | const reason_method: ReasonMethod, const strat: PeerResolveStrategy = switch (s0) { |
| 32462 | .unknown => .{ .all_s1, s1 }, |
| 32463 | .error_set => switch (s1) { |
| 32464 | .error_set => .{ .either, .error_set }, |
| 32465 | else => .{ .all_s0, .error_union }, |
| 32466 | }, |
| 32467 | .error_union => switch (s1) { |
| 32468 | .error_union => .{ .either, .error_union }, |
| 32469 | else => .{ .all_s0, .error_union }, |
| 32470 | }, |
| 32471 | .nullable => switch (s1) { |
| 32472 | .nullable => .{ .either, .nullable }, |
| 32473 | .c_ptr => .{ .all_s1, .c_ptr }, |
| 32474 | else => .{ .all_s0, .optional }, |
| 32475 | }, |
| 32476 | .optional => switch (s1) { |
| 32477 | .optional => .{ .either, .optional }, |
| 32478 | .c_ptr => .{ .all_s1, .c_ptr }, |
| 32479 | else => .{ .all_s0, .optional }, |
| 32480 | }, |
| 32481 | .array => switch (s1) { |
| 32482 | .array => .{ .either, .array }, |
| 32483 | .vector => .{ .all_s1, .vector }, |
| 32484 | else => .{ .all_s0, .array }, |
| 32485 | }, |
| 32486 | .vector => switch (s1) { |
| 32487 | .vector => .{ .either, .vector }, |
| 32488 | else => .{ .all_s0, .vector }, |
| 32489 | }, |
| 32490 | .c_ptr => switch (s1) { |
| 32491 | .c_ptr => .{ .either, .c_ptr }, |
| 32492 | else => .{ .all_s0, .c_ptr }, |
| 32493 | }, |
| 32494 | .ptr => switch (s1) { |
| 32495 | .ptr => .{ .either, .ptr }, |
| 32496 | else => .{ .all_s0, .ptr }, |
| 32497 | }, |
| 32498 | .func => switch (s1) { |
| 32499 | .func => .{ .either, .func }, |
| 32500 | else => .{ .all_s1, s1 }, // doesn't override anything later |
| 32501 | }, |
| 32502 | .enum_or_union => switch (s1) { |
| 32503 | .enum_or_union => .{ .either, .enum_or_union }, |
| 32504 | else => .{ .all_s0, .enum_or_union }, |
| 32505 | }, |
| 32506 | .comptime_int => switch (s1) { |
| 32507 | .comptime_int => .{ .either, .comptime_int }, |
| 32508 | else => .{ .all_s1, s1 }, // doesn't override anything later |
| 32509 | }, |
| 32510 | .comptime_float => switch (s1) { |
| 32511 | .comptime_float => .{ .either, .comptime_float }, |
| 32512 | else => .{ .all_s1, s1 }, // doesn't override anything later |
| 32513 | }, |
| 32514 | .fixed_int => switch (s1) { |
| 32515 | .fixed_int => .{ .either, .fixed_int }, |
| 32516 | else => .{ .all_s1, s1 }, // doesn't override anything later |
| 32517 | }, |
| 32518 | .fixed_float => switch (s1) { |
| 32519 | .fixed_float => .{ .either, .fixed_float }, |
| 32520 | else => .{ .all_s1, s1 }, // doesn't override anything later |
| 32521 | }, |
| 32522 | .tuple => switch (s1) { |
| 32523 | .exact => .{ .all_s1, .exact }, |
| 32524 | else => .{ .all_s0, .tuple }, |
| 32525 | }, |
| 32526 | .exact => .{ .all_s0, .exact }, |
| 32527 | }; |
| 32528 | |
| 32529 | switch (reason_method) { |
| 32530 | .all_s0 => { |
| 32531 | if (!s0_is_a) { |
| 32532 | reason_peer.* = b_peer_idx; |
| 32533 | } |
| 32534 | }, |
| 32535 | .all_s1 => { |
| 32536 | if (s0_is_a) { |
| 32537 | reason_peer.* = b_peer_idx; |
| 32538 | } |
| 32539 | }, |
| 32540 | .either => { |
| 32541 | // Prefer the earliest peer |
| 32542 | reason_peer.* = @min(reason_peer.*, b_peer_idx); |
| 32543 | }, |
| 32544 | } |
| 32545 | |
| 32546 | return strat; |
| 32547 | } |
| 32548 | |
| 32549 | fn select(ty: Type, zcu: *Zcu) PeerResolveStrategy { |
| 32550 | return switch (ty.zigTypeTag(zcu)) { |
| 32551 | .type, .void, .bool, .@"opaque", .spirv, .frame, .@"anyframe" => .exact, |
| 32552 | .noreturn, .undefined => .unknown, |
| 32553 | .null => .nullable, |
| 32554 | .comptime_int => .comptime_int, |
| 32555 | .int => .fixed_int, |
| 32556 | .comptime_float => .comptime_float, |
| 32557 | .float => .fixed_float, |
| 32558 | .pointer => if (ty.ptrInfo(zcu).flags.size == .c) .c_ptr else .ptr, |
| 32559 | .array => .array, |
| 32560 | .vector => .vector, |
| 32561 | .optional => .optional, |
| 32562 | .error_set => .error_set, |
| 32563 | .error_union => .error_union, |
| 32564 | .enum_literal, .@"enum", .@"union" => .enum_or_union, |
| 32565 | .@"struct" => if (ty.isTuple(zcu)) .tuple else .exact, |
| 32566 | .@"fn" => .func, |
| 32567 | }; |
| 32568 | } |
| 32569 | }; |
| 32570 | |
| 32571 | const PeerTypeCandidateSrc = union(enum) { |
| 32572 | /// Do not print out error notes for candidate sources |
| 32573 | none: void, |
| 32574 | /// When we want to know the the src of candidate i, look up at |
| 32575 | /// index i in this slice |
| 32576 | override: []const ?LazySrcLoc, |
| 32577 | /// resolvePeerTypes originates from a @TypeOf(...) call |
| 32578 | typeof_builtin_call_node_offset: std.zig.Ast.Node.Offset, |
| 32579 | |
| 32580 | pub fn resolve( |
| 32581 | self: PeerTypeCandidateSrc, |
| 32582 | block: *Block, |
| 32583 | candidate_i: usize, |
| 32584 | ) ?LazySrcLoc { |
| 32585 | return switch (self) { |
| 32586 | .none => null, |
| 32587 | .override => |candidate_srcs| if (candidate_i >= candidate_srcs.len) |
| 32588 | null |
| 32589 | else |
| 32590 | candidate_srcs[candidate_i], |
| 32591 | .typeof_builtin_call_node_offset => |node_offset| block.builtinCallArgSrc(node_offset, @intCast(candidate_i)), |
| 32592 | }; |
| 32593 | } |
| 32594 | }; |
| 32595 | |
| 32596 | const PeerResolveResult = union(enum) { |
| 32597 | /// The peer type resolution was successful, and resulted in the given type. |
| 32598 | success: Type, |
| 32599 | /// There was some generic conflict between two peers. |
| 32600 | conflict: struct { |
| 32601 | peer_idx_a: usize, |
| 32602 | peer_idx_b: usize, |
| 32603 | }, |
| 32604 | /// There was an error when resolving the type of a struct or tuple field. |
| 32605 | field_error: struct { |
| 32606 | /// The name of the field which caused the failure. |
| 32607 | field_name: InternPool.NullTerminatedString, |
| 32608 | /// The type of this field in each peer. |
| 32609 | field_types: []Type, |
| 32610 | /// The error from resolving the field type. Guaranteed not to be `success`. |
| 32611 | sub_result: *PeerResolveResult, |
| 32612 | }, |
| 32613 | |
| 32614 | fn report( |
| 32615 | result: PeerResolveResult, |
| 32616 | sema: *Sema, |
| 32617 | block: *Block, |
| 32618 | src: LazySrcLoc, |
| 32619 | instructions: []const Air.Inst.Ref, |
| 32620 | candidate_srcs: PeerTypeCandidateSrc, |
| 32621 | ) !*Zcu.ErrorMsg { |
| 32622 | const pt = sema.pt; |
| 32623 | |
| 32624 | var opt_msg: ?*Zcu.ErrorMsg = null; |
| 32625 | errdefer if (opt_msg) |msg| msg.destroy(sema.gpa); |
| 32626 | |
| 32627 | // If we mention fields we'll want to include field types, so put peer types in a buffer |
| 32628 | var peer_tys = try sema.arena.alloc(Type, instructions.len); |
| 32629 | for (peer_tys, instructions) |*ty, inst| { |
| 32630 | ty.* = sema.typeOf(inst); |
| 32631 | } |
| 32632 | |
| 32633 | var cur = result; |
| 32634 | while (true) { |
| 32635 | var conflict_idx: [2]usize = undefined; |
| 32636 | |
| 32637 | switch (cur) { |
| 32638 | .success => unreachable, |
| 32639 | .conflict => |conflict| { |
| 32640 | // Fall through to two-peer conflict handling below |
| 32641 | conflict_idx = .{ |
| 32642 | conflict.peer_idx_a, |
| 32643 | conflict.peer_idx_b, |
| 32644 | }; |
| 32645 | }, |
| 32646 | .field_error => |field_error| { |
| 32647 | const fmt = "struct field '{f}' has conflicting types"; |
| 32648 | const args = .{field_error.field_name.fmt(&pt.zcu.intern_pool)}; |
| 32649 | if (opt_msg) |msg| { |
| 32650 | try sema.errNote(src, msg, fmt, args); |
| 32651 | } else { |
| 32652 | opt_msg = try sema.errMsg(src, fmt, args); |
| 32653 | } |
| 32654 | |
| 32655 | // Continue on to child error |
| 32656 | cur = field_error.sub_result.*; |
| 32657 | peer_tys = field_error.field_types; |
| 32658 | continue; |
| 32659 | }, |
| 32660 | } |
| 32661 | |
| 32662 | // This is the path for reporting a generic conflict between two peers. |
| 32663 | |
| 32664 | if (conflict_idx[1] < conflict_idx[0]) { |
| 32665 | // b comes first in source, so it's better if it comes first in the error |
| 32666 | std.mem.swap(usize, &conflict_idx[0], &conflict_idx[1]); |
| 32667 | } |
| 32668 | |
| 32669 | const conflict_tys: [2]Type = .{ |
| 32670 | peer_tys[conflict_idx[0]], |
| 32671 | peer_tys[conflict_idx[1]], |
| 32672 | }; |
| 32673 | const conflict_srcs: [2]?LazySrcLoc = .{ |
| 32674 | candidate_srcs.resolve(block, conflict_idx[0]), |
| 32675 | candidate_srcs.resolve(block, conflict_idx[1]), |
| 32676 | }; |
| 32677 | |
| 32678 | const fmt = "incompatible types: '{f}' and '{f}'"; |
| 32679 | const args = .{ |
| 32680 | conflict_tys[0].fmt(pt), |
| 32681 | conflict_tys[1].fmt(pt), |
| 32682 | }; |
| 32683 | const msg = if (opt_msg) |msg| msg: { |
| 32684 | try sema.errNote(src, msg, fmt, args); |
| 32685 | break :msg msg; |
| 32686 | } else msg: { |
| 32687 | const msg = try sema.errMsg(src, fmt, args); |
| 32688 | opt_msg = msg; |
| 32689 | break :msg msg; |
| 32690 | }; |
| 32691 | |
| 32692 | if (conflict_srcs[0]) |src_loc| try sema.errNote(src_loc, msg, "type '{f}' here", .{conflict_tys[0].fmt(pt)}); |
| 32693 | if (conflict_srcs[1]) |src_loc| try sema.errNote(src_loc, msg, "type '{f}' here", .{conflict_tys[1].fmt(pt)}); |
| 32694 | |
| 32695 | // No child error |
| 32696 | break; |
| 32697 | } |
| 32698 | |
| 32699 | return opt_msg.?; |
| 32700 | } |
| 32701 | }; |
| 32702 | |
| 32703 | fn resolvePeerTypes( |
| 32704 | sema: *Sema, |
| 32705 | block: *Block, |
| 32706 | src: LazySrcLoc, |
| 32707 | instructions: []const Air.Inst.Ref, |
| 32708 | candidate_srcs: PeerTypeCandidateSrc, |
| 32709 | ) !Type { |
| 32710 | switch (instructions.len) { |
| 32711 | 0 => return .noreturn, |
| 32712 | 1 => return sema.typeOf(instructions[0]), |
| 32713 | else => {}, |
| 32714 | } |
| 32715 | |
| 32716 | // Fast path: check if everything has the same type to bypass the main PTR logic. |
| 32717 | same_type: { |
| 32718 | const ty = sema.typeOf(instructions[0]); |
| 32719 | for (instructions[1..]) |inst| { |
| 32720 | if (sema.typeOf(inst).toIntern() != ty.toIntern()) { |
| 32721 | break :same_type; |
| 32722 | } |
| 32723 | } |
| 32724 | return ty; |
| 32725 | } |
| 32726 | |
| 32727 | const peer_tys = try sema.arena.alloc(?Type, instructions.len); |
| 32728 | const peer_vals = try sema.arena.alloc(?Value, instructions.len); |
| 32729 | |
| 32730 | for (instructions, peer_tys, peer_vals) |inst, *ty, *val| { |
| 32731 | ty.* = sema.typeOf(inst); |
| 32732 | val.* = sema.resolveValue(inst); |
| 32733 | } |
| 32734 | |
| 32735 | switch (try sema.resolvePeerTypesInner(block, src, peer_tys, peer_vals)) { |
| 32736 | .success => |ty| return ty, |
| 32737 | else => |result| { |
| 32738 | const msg = try result.report(sema, block, src, instructions, candidate_srcs); |
| 32739 | return sema.failWithOwnedErrorMsg(block, msg); |
| 32740 | }, |
| 32741 | } |
| 32742 | } |
| 32743 | |
| 32744 | fn resolvePeerTypesInner( |
| 32745 | sema: *Sema, |
| 32746 | block: *Block, |
| 32747 | src: LazySrcLoc, |
| 32748 | peer_tys: []?Type, |
| 32749 | peer_vals: []?Value, |
| 32750 | ) !PeerResolveResult { |
| 32751 | const pt = sema.pt; |
| 32752 | const zcu = pt.zcu; |
| 32753 | const comp = zcu.comp; |
| 32754 | const gpa = comp.gpa; |
| 32755 | const io = comp.io; |
| 32756 | const ip = &zcu.intern_pool; |
| 32757 | |
| 32758 | var strat_reason: usize = 0; |
| 32759 | var s: PeerResolveStrategy = .unknown; |
| 32760 | for (peer_tys, 0..) |opt_ty, i| { |
| 32761 | const ty = opt_ty orelse continue; |
| 32762 | s = s.merge(PeerResolveStrategy.select(ty, zcu), &strat_reason, i); |
| 32763 | } |
| 32764 | |
| 32765 | if (s == .unknown) { |
| 32766 | // The whole thing was noreturn or undefined - try to do an exact match |
| 32767 | s = .exact; |
| 32768 | } else { |
| 32769 | // There was something other than noreturn and undefined, so we can ignore those peers |
| 32770 | for (peer_tys) |*ty_ptr| { |
| 32771 | const ty = ty_ptr.* orelse continue; |
| 32772 | switch (ty.zigTypeTag(zcu)) { |
| 32773 | .noreturn, .undefined => ty_ptr.* = null, |
| 32774 | else => {}, |
| 32775 | } |
| 32776 | } |
| 32777 | } |
| 32778 | |
| 32779 | const target = zcu.getTarget(); |
| 32780 | |
| 32781 | switch (s) { |
| 32782 | .unknown => unreachable, |
| 32783 | |
| 32784 | .error_set => { |
| 32785 | var final_set: ?Type = null; |
| 32786 | for (peer_tys, 0..) |opt_ty, i| { |
| 32787 | const ty = opt_ty orelse continue; |
| 32788 | if (ty.zigTypeTag(zcu) != .error_set) return .{ .conflict = .{ |
| 32789 | .peer_idx_a = strat_reason, |
| 32790 | .peer_idx_b = i, |
| 32791 | } }; |
| 32792 | if (final_set) |cur_set| { |
| 32793 | final_set = try sema.maybeMergeErrorSets(block, src, cur_set, ty); |
| 32794 | } else { |
| 32795 | final_set = ty; |
| 32796 | } |
| 32797 | } |
| 32798 | return .{ .success = final_set.? }; |
| 32799 | }, |
| 32800 | |
| 32801 | .error_union => { |
| 32802 | var final_set: ?Type = null; |
| 32803 | for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| { |
| 32804 | const ty = ty_ptr.* orelse continue; |
| 32805 | const set_ty = switch (ty.zigTypeTag(zcu)) { |
| 32806 | .error_set => blk: { |
| 32807 | ty_ptr.* = null; // no payload to decide on |
| 32808 | val_ptr.* = null; |
| 32809 | break :blk ty; |
| 32810 | }, |
| 32811 | .error_union => blk: { |
| 32812 | const set_ty = ty.errorUnionSet(zcu); |
| 32813 | ty_ptr.* = ty.errorUnionPayload(zcu); |
| 32814 | if (val_ptr.*) |eu_val| switch (ip.indexToKey(eu_val.toIntern())) { |
| 32815 | .error_union => |eu| switch (eu.val) { |
| 32816 | .payload => |payload_ip| val_ptr.* = Value.fromInterned(payload_ip), |
| 32817 | .err_name => val_ptr.* = null, |
| 32818 | }, |
| 32819 | .undef => val_ptr.* = Value.fromInterned(try pt.intern(.{ .undef = ty_ptr.*.?.toIntern() })), |
| 32820 | else => unreachable, |
| 32821 | }; |
| 32822 | break :blk set_ty; |
| 32823 | }, |
| 32824 | else => continue, // whole type is the payload |
| 32825 | }; |
| 32826 | if (final_set) |cur_set| { |
| 32827 | final_set = try sema.maybeMergeErrorSets(block, src, cur_set, set_ty); |
| 32828 | } else { |
| 32829 | final_set = set_ty; |
| 32830 | } |
| 32831 | } |
| 32832 | assert(final_set != null); |
| 32833 | const final_payload = switch (try sema.resolvePeerTypesInner( |
| 32834 | block, |
| 32835 | src, |
| 32836 | peer_tys, |
| 32837 | peer_vals, |
| 32838 | )) { |
| 32839 | .success => |ty| ty, |
| 32840 | else => |result| return result, |
| 32841 | }; |
| 32842 | return .{ .success = try pt.errorUnionType(final_set.?, final_payload) }; |
| 32843 | }, |
| 32844 | |
| 32845 | .nullable => { |
| 32846 | for (peer_tys, 0..) |opt_ty, i| { |
| 32847 | const ty = opt_ty orelse continue; |
| 32848 | if (!ty.eql(.null)) return .{ .conflict = .{ |
| 32849 | .peer_idx_a = strat_reason, |
| 32850 | .peer_idx_b = i, |
| 32851 | } }; |
| 32852 | } |
| 32853 | return .{ .success = .null }; |
| 32854 | }, |
| 32855 | |
| 32856 | .optional => { |
| 32857 | for (peer_tys, peer_vals) |*ty_ptr, *val_ptr| { |
| 32858 | const ty = ty_ptr.* orelse continue; |
| 32859 | switch (ty.zigTypeTag(zcu)) { |
| 32860 | .null => { |
| 32861 | ty_ptr.* = null; |
| 32862 | val_ptr.* = null; |
| 32863 | }, |
| 32864 | .optional => { |
| 32865 | ty_ptr.* = ty.optionalChild(zcu); |
| 32866 | if (val_ptr.*) |opt_val| val_ptr.* = if (!opt_val.isUndef(zcu)) opt_val.optionalValue(zcu) else null; |
| 32867 | }, |
| 32868 | else => {}, |
| 32869 | } |
| 32870 | } |
| 32871 | const child_ty = switch (try sema.resolvePeerTypesInner( |
| 32872 | block, |
| 32873 | src, |
| 32874 | peer_tys, |
| 32875 | peer_vals, |
| 32876 | )) { |
| 32877 | .success => |ty| ty, |
| 32878 | else => |result| return result, |
| 32879 | }; |
| 32880 | return .{ .success = try pt.optionalType(child_ty.toIntern()) }; |
| 32881 | }, |
| 32882 | |
| 32883 | .array => { |
| 32884 | // Index of the first non-null peer |
| 32885 | var opt_first_idx: ?usize = null; |
| 32886 | // Index of the first array or vector peer (i.e. not a tuple) |
| 32887 | var opt_first_arr_idx: ?usize = null; |
| 32888 | // Set to non-null once we see any peer, even a tuple |
| 32889 | var len: u64 = undefined; |
| 32890 | var sentinel: ?Value = undefined; |
| 32891 | // Only set once we see a non-tuple peer |
| 32892 | var elem_ty: Type = undefined; |
| 32893 | |
| 32894 | for (peer_tys, 0..) |*ty_ptr, i| { |
| 32895 | const ty = ty_ptr.* orelse continue; |
| 32896 | |
| 32897 | if (!ty.isArrayOrVector(zcu)) { |
| 32898 | // We allow tuples of the correct length. We won't validate their elem type, since the elements can be coerced. |
| 32899 | const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{ |
| 32900 | .peer_idx_a = strat_reason, |
| 32901 | .peer_idx_b = i, |
| 32902 | } }; |
| 32903 | |
| 32904 | if (opt_first_idx) |first_idx| { |
| 32905 | if (arr_like.len != len) return .{ .conflict = .{ |
| 32906 | .peer_idx_a = first_idx, |
| 32907 | .peer_idx_b = i, |
| 32908 | } }; |
| 32909 | } else { |
| 32910 | opt_first_idx = i; |
| 32911 | len = arr_like.len; |
| 32912 | } |
| 32913 | |
| 32914 | sentinel = null; |
| 32915 | |
| 32916 | continue; |
| 32917 | } |
| 32918 | |
| 32919 | const first_arr_idx = opt_first_arr_idx orelse { |
| 32920 | if (opt_first_idx == null) { |
| 32921 | opt_first_idx = i; |
| 32922 | len = ty.arrayLen(zcu); |
| 32923 | sentinel = ty.sentinel(zcu); |
| 32924 | } |
| 32925 | opt_first_arr_idx = i; |
| 32926 | elem_ty = ty.childType(zcu); |
| 32927 | continue; |
| 32928 | }; |
| 32929 | |
| 32930 | if (ty.arrayLen(zcu) != len) return .{ .conflict = .{ |
| 32931 | .peer_idx_a = first_arr_idx, |
| 32932 | .peer_idx_b = i, |
| 32933 | } }; |
| 32934 | |
| 32935 | const peer_elem_ty = ty.childType(zcu); |
| 32936 | if (!peer_elem_ty.eql(elem_ty)) coerce: { |
| 32937 | const peer_elem_coerces_to_elem = |
| 32938 | try sema.coerceInMemoryAllowed(block, elem_ty, peer_elem_ty, false, zcu.getTarget(), src, src, null); |
| 32939 | if (peer_elem_coerces_to_elem == .ok) { |
| 32940 | break :coerce; |
| 32941 | } |
| 32942 | |
| 32943 | const elem_coerces_to_peer_elem = |
| 32944 | try sema.coerceInMemoryAllowed(block, peer_elem_ty, elem_ty, false, zcu.getTarget(), src, src, null); |
| 32945 | if (elem_coerces_to_peer_elem == .ok) { |
| 32946 | elem_ty = peer_elem_ty; |
| 32947 | break :coerce; |
| 32948 | } |
| 32949 | |
| 32950 | return .{ .conflict = .{ |
| 32951 | .peer_idx_a = first_arr_idx, |
| 32952 | .peer_idx_b = i, |
| 32953 | } }; |
| 32954 | } |
| 32955 | |
| 32956 | if (sentinel) |cur_sent| { |
| 32957 | if (ty.sentinel(zcu)) |peer_sent| { |
| 32958 | if (!peer_sent.eql(cur_sent, elem_ty, zcu)) sentinel = null; |
| 32959 | } else { |
| 32960 | sentinel = null; |
| 32961 | } |
| 32962 | } |
| 32963 | } |
| 32964 | |
| 32965 | // There should always be at least one array or vector peer |
| 32966 | assert(opt_first_arr_idx != null); |
| 32967 | |
| 32968 | return .{ .success = try pt.arrayType(.{ |
| 32969 | .len = len, |
| 32970 | .child = elem_ty.toIntern(), |
| 32971 | .sentinel = if (sentinel) |sent_val| sent_val.toIntern() else .none, |
| 32972 | }) }; |
| 32973 | }, |
| 32974 | |
| 32975 | .vector => { |
| 32976 | var len: ?u64 = null; |
| 32977 | var first_idx: usize = undefined; |
| 32978 | for (peer_tys, peer_vals, 0..) |*ty_ptr, *val_ptr, i| { |
| 32979 | const ty = ty_ptr.* orelse continue; |
| 32980 | |
| 32981 | if (!ty.isArrayOrVector(zcu)) { |
| 32982 | // Allow tuples of the correct length |
| 32983 | const arr_like = sema.typeIsArrayLike(ty) orelse return .{ .conflict = .{ |
| 32984 | .peer_idx_a = strat_reason, |
| 32985 | .peer_idx_b = i, |
| 32986 | } }; |
| 32987 | |
| 32988 | if (len) |expect_len| { |
| 32989 | if (arr_like.len != expect_len) return .{ .conflict = .{ |
| 32990 | .peer_idx_a = first_idx, |
| 32991 | .peer_idx_b = i, |
| 32992 | } }; |
| 32993 | } else { |
| 32994 | len = arr_like.len; |
| 32995 | first_idx = i; |
| 32996 | } |
| 32997 | |
| 32998 | // Tuples won't participate in the child type resolution. We'll resolve without |
| 32999 | // them, and if the tuples have a bad type, we'll get a coercion error later. |
| 33000 | ty_ptr.* = null; |
| 33001 | val_ptr.* = null; |
| 33002 | |
| 33003 | continue; |
| 33004 | } |
| 33005 | |
| 33006 | if (len) |expect_len| { |
| 33007 | if (ty.arrayLen(zcu) != expect_len) return .{ .conflict = .{ |
| 33008 | .peer_idx_a = first_idx, |
| 33009 | .peer_idx_b = i, |
| 33010 | } }; |
| 33011 | } else { |
| 33012 | len = ty.arrayLen(zcu); |
| 33013 | first_idx = i; |
| 33014 | } |
| 33015 | |
| 33016 | ty_ptr.* = ty.childType(zcu); |
| 33017 | val_ptr.* = null; // multiple child vals, so we can't easily use them in PTR |
| 33018 | } |
| 33019 | |
| 33020 | const child_ty = switch (try sema.resolvePeerTypesInner( |
| 33021 | block, |
| 33022 | src, |
| 33023 | peer_tys, |
| 33024 | peer_vals, |
| 33025 | )) { |
| 33026 | .success => |ty| ty, |
| 33027 | else => |result| return result, |
| 33028 | }; |
| 33029 | |
| 33030 | return .{ .success = try pt.vectorType(.{ |
| 33031 | .len = @intCast(len.?), |
| 33032 | .child = child_ty.toIntern(), |
| 33033 | }) }; |
| 33034 | }, |
| 33035 | |
| 33036 | .c_ptr => { |
| 33037 | var opt_ptr_info: ?InternPool.Key.PtrType = null; |
| 33038 | var first_idx: usize = undefined; |
| 33039 | for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { |
| 33040 | const ty = opt_ty orelse continue; |
| 33041 | switch (ty.zigTypeTag(zcu)) { |
| 33042 | .comptime_int => continue, // comptime-known integers can always coerce to C pointers |
| 33043 | .int => { |
| 33044 | if (opt_val != null) { |
| 33045 | // Always allow the coercion for comptime-known ints |
| 33046 | continue; |
| 33047 | } else { |
| 33048 | // Runtime-known, so check if the type is no bigger than a usize |
| 33049 | const ptr_bits = target.ptrBitWidth(); |
| 33050 | const bits = ty.intInfo(zcu).bits; |
| 33051 | if (bits <= ptr_bits) continue; |
| 33052 | } |
| 33053 | }, |
| 33054 | .null => continue, |
| 33055 | else => {}, |
| 33056 | } |
| 33057 | |
| 33058 | if (!ty.isPtrAtRuntime(zcu)) return .{ .conflict = .{ |
| 33059 | .peer_idx_a = strat_reason, |
| 33060 | .peer_idx_b = i, |
| 33061 | } }; |
| 33062 | |
| 33063 | // Goes through optionals |
| 33064 | const peer_info = ty.ptrInfo(zcu); |
| 33065 | |
| 33066 | var ptr_info = opt_ptr_info orelse { |
| 33067 | opt_ptr_info = peer_info; |
| 33068 | opt_ptr_info.?.flags.size = .c; |
| 33069 | first_idx = i; |
| 33070 | continue; |
| 33071 | }; |
| 33072 | |
| 33073 | // Try peer -> cur, then cur -> peer |
| 33074 | ptr_info.child = ((try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) orelse { |
| 33075 | return .{ .conflict = .{ |
| 33076 | .peer_idx_a = first_idx, |
| 33077 | .peer_idx_b = i, |
| 33078 | } }; |
| 33079 | }).toIntern(); |
| 33080 | |
| 33081 | if (ptr_info.sentinel != .none and peer_info.sentinel != .none) { |
| 33082 | const peer_sent = try ip.getCoerced(gpa, io, pt.tid, ptr_info.sentinel, ptr_info.child); |
| 33083 | const ptr_sent = try ip.getCoerced(gpa, io, pt.tid, peer_info.sentinel, ptr_info.child); |
| 33084 | if (ptr_sent == peer_sent) { |
| 33085 | ptr_info.sentinel = ptr_sent; |
| 33086 | } else { |
| 33087 | ptr_info.sentinel = .none; |
| 33088 | } |
| 33089 | } else { |
| 33090 | ptr_info.sentinel = .none; |
| 33091 | } |
| 33092 | |
| 33093 | ptr_info.flags.alignment = a: { |
| 33094 | // If both alignments are implicit, the result alignment is implicit. |
| 33095 | // e.g. '[*c]u32' + '[*c]c_uint' -> '[*c]u32' |
| 33096 | if (ptr_info.flags.alignment == .none and peer_info.flags.alignment == .none) { |
| 33097 | break :a .none; |
| 33098 | } |
| 33099 | // Otherwise (if either alignment is explicit), the result alignment is explicit. |
| 33100 | // e.g. '[*c]u32' + '[*c]align(4) c_uint' -> '[*c]align(4) u32' |
| 33101 | const cur_align = switch (ptr_info.flags.alignment) { |
| 33102 | .none => Type.fromInterned(ptr_info.child).abiAlignment(zcu), |
| 33103 | else => ptr_info.flags.alignment, |
| 33104 | }; |
| 33105 | const new_align = switch (peer_info.flags.alignment) { |
| 33106 | .none => Type.fromInterned(peer_info.child).abiAlignment(zcu), |
| 33107 | else => peer_info.flags.alignment, |
| 33108 | }; |
| 33109 | break :a .minStrict(cur_align, new_align); |
| 33110 | }; |
| 33111 | if (ptr_info.flags.address_space != peer_info.flags.address_space) { |
| 33112 | return .{ .conflict = .{ |
| 33113 | .peer_idx_a = first_idx, |
| 33114 | .peer_idx_b = i, |
| 33115 | } }; |
| 33116 | } |
| 33117 | |
| 33118 | if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or |
| 33119 | ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size) |
| 33120 | { |
| 33121 | return .{ .conflict = .{ |
| 33122 | .peer_idx_a = first_idx, |
| 33123 | .peer_idx_b = i, |
| 33124 | } }; |
| 33125 | } |
| 33126 | |
| 33127 | ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const; |
| 33128 | ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile; |
| 33129 | |
| 33130 | opt_ptr_info = ptr_info; |
| 33131 | } |
| 33132 | return .{ .success = try pt.ptrType(opt_ptr_info.?) }; |
| 33133 | }, |
| 33134 | |
| 33135 | .ptr => { |
| 33136 | // If we've resolved to a `[]T` but then see a `[*]T`, we can resolve to a `[*]T` only |
| 33137 | // if there were no actual slices. Else, we want the slice index to report a conflict. |
| 33138 | var opt_slice_idx: ?usize = null; |
| 33139 | |
| 33140 | var opt_ptr_info: ?InternPool.Key.PtrType = null; |
| 33141 | var first_idx: usize = undefined; |
| 33142 | var other_idx: usize = undefined; // We sometimes need a second peer index to report a generic error |
| 33143 | |
| 33144 | for (peer_tys, 0..) |opt_ty, i| { |
| 33145 | const ty = opt_ty orelse continue; |
| 33146 | const peer_info: InternPool.Key.PtrType = switch (ty.zigTypeTag(zcu)) { |
| 33147 | .pointer => ty.ptrInfo(zcu), |
| 33148 | .@"fn" => .{ |
| 33149 | .child = ty.toIntern(), |
| 33150 | .flags = .{ |
| 33151 | .address_space = target_util.defaultAddressSpace(target, .global_constant), |
| 33152 | }, |
| 33153 | }, |
| 33154 | else => return .{ .conflict = .{ |
| 33155 | .peer_idx_a = strat_reason, |
| 33156 | .peer_idx_b = i, |
| 33157 | } }, |
| 33158 | }; |
| 33159 | |
| 33160 | switch (peer_info.flags.size) { |
| 33161 | .one, .many => {}, |
| 33162 | .slice => opt_slice_idx = i, |
| 33163 | .c => return .{ .conflict = .{ |
| 33164 | .peer_idx_a = strat_reason, |
| 33165 | .peer_idx_b = i, |
| 33166 | } }, |
| 33167 | } |
| 33168 | |
| 33169 | var ptr_info = opt_ptr_info orelse { |
| 33170 | opt_ptr_info = peer_info; |
| 33171 | first_idx = i; |
| 33172 | continue; |
| 33173 | }; |
| 33174 | |
| 33175 | other_idx = i; |
| 33176 | |
| 33177 | // We want to return this in a lot of cases, so alias it here for convenience |
| 33178 | const generic_err: PeerResolveResult = .{ .conflict = .{ |
| 33179 | .peer_idx_a = first_idx, |
| 33180 | .peer_idx_b = i, |
| 33181 | } }; |
| 33182 | |
| 33183 | ptr_info.flags.alignment = a: { |
| 33184 | // If both alignments are implicit, the result alignment is implicit. |
| 33185 | // e.g. '*u32' + '*c_uint' -> '*u32' |
| 33186 | if (ptr_info.flags.alignment == .none and peer_info.flags.alignment == .none) { |
| 33187 | break :a .none; |
| 33188 | } |
| 33189 | // Otherwise (if either alignment is explicit), the result alignment is explicit. |
| 33190 | // e.g. '*u32' + '*align(4) c_uint' -> '*align(4) u32' |
| 33191 | const cur_align = switch (ptr_info.flags.alignment) { |
| 33192 | .none => Type.fromInterned(ptr_info.child).abiAlignment(zcu), |
| 33193 | else => ptr_info.flags.alignment, |
| 33194 | }; |
| 33195 | const new_align = switch (peer_info.flags.alignment) { |
| 33196 | .none => Type.fromInterned(peer_info.child).abiAlignment(zcu), |
| 33197 | else => peer_info.flags.alignment, |
| 33198 | }; |
| 33199 | break :a .minStrict(cur_align, new_align); |
| 33200 | }; |
| 33201 | |
| 33202 | if (ptr_info.flags.address_space != peer_info.flags.address_space) { |
| 33203 | return generic_err; |
| 33204 | } |
| 33205 | |
| 33206 | if (ptr_info.packed_offset.bit_offset != peer_info.packed_offset.bit_offset or |
| 33207 | ptr_info.packed_offset.host_size != peer_info.packed_offset.host_size) |
| 33208 | { |
| 33209 | return generic_err; |
| 33210 | } |
| 33211 | |
| 33212 | ptr_info.flags.is_const = ptr_info.flags.is_const or peer_info.flags.is_const; |
| 33213 | ptr_info.flags.is_volatile = ptr_info.flags.is_volatile or peer_info.flags.is_volatile; |
| 33214 | ptr_info.flags.is_allowzero = ptr_info.flags.is_allowzero or peer_info.flags.is_allowzero; |
| 33215 | |
| 33216 | const peer_sentinel: InternPool.Index = switch (peer_info.flags.size) { |
| 33217 | .one => switch (ip.indexToKey(peer_info.child)) { |
| 33218 | .array_type => |array_type| array_type.sentinel, |
| 33219 | else => .none, |
| 33220 | }, |
| 33221 | .many, .slice => peer_info.sentinel, |
| 33222 | .c => unreachable, |
| 33223 | }; |
| 33224 | |
| 33225 | const cur_sentinel: InternPool.Index = switch (ptr_info.flags.size) { |
| 33226 | .one => switch (ip.indexToKey(ptr_info.child)) { |
| 33227 | .array_type => |array_type| array_type.sentinel, |
| 33228 | else => .none, |
| 33229 | }, |
| 33230 | .many, .slice => ptr_info.sentinel, |
| 33231 | .c => unreachable, |
| 33232 | }; |
| 33233 | |
| 33234 | // We abstract array handling slightly so that tuple pointers can work like array pointers |
| 33235 | const peer_pointee_array = sema.typeIsArrayLike(.fromInterned(peer_info.child)); |
| 33236 | const cur_pointee_array = sema.typeIsArrayLike(.fromInterned(ptr_info.child)); |
| 33237 | |
| 33238 | // This switch is just responsible for deciding the size and pointee (not including |
| 33239 | // single-pointer array sentinel). |
| 33240 | good: { |
| 33241 | switch (peer_info.flags.size) { |
| 33242 | .one => switch (ptr_info.flags.size) { |
| 33243 | .one => { |
| 33244 | if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| { |
| 33245 | ptr_info.child = pointee.toIntern(); |
| 33246 | break :good; |
| 33247 | } |
| 33248 | |
| 33249 | const cur_arr = cur_pointee_array orelse return generic_err; |
| 33250 | const peer_arr = peer_pointee_array orelse return generic_err; |
| 33251 | |
| 33252 | if (try sema.resolvePairInMemoryCoercible(block, src, cur_arr.elem_ty, peer_arr.elem_ty)) |elem_ty| { |
| 33253 | // *[n:x]T + *[n:y]T = *[n]T |
| 33254 | if (cur_arr.len == peer_arr.len) { |
| 33255 | ptr_info.child = (try pt.arrayType(.{ |
| 33256 | .len = cur_arr.len, |
| 33257 | .child = elem_ty.toIntern(), |
| 33258 | })).toIntern(); |
| 33259 | break :good; |
| 33260 | } |
| 33261 | // *[a]T + *[b]T = []T |
| 33262 | ptr_info.flags.size = .slice; |
| 33263 | ptr_info.child = elem_ty.toIntern(); |
| 33264 | break :good; |
| 33265 | } |
| 33266 | |
| 33267 | if (peer_arr.elem_ty.toIntern() == .noreturn_type) { |
| 33268 | // *struct{} + *[a]T = []T |
| 33269 | ptr_info.flags.size = .slice; |
| 33270 | ptr_info.child = cur_arr.elem_ty.toIntern(); |
| 33271 | break :good; |
| 33272 | } |
| 33273 | |
| 33274 | if (cur_arr.elem_ty.toIntern() == .noreturn_type) { |
| 33275 | // *[a]T + *struct{} = []T |
| 33276 | ptr_info.flags.size = .slice; |
| 33277 | ptr_info.child = peer_arr.elem_ty.toIntern(); |
| 33278 | break :good; |
| 33279 | } |
| 33280 | |
| 33281 | return generic_err; |
| 33282 | }, |
| 33283 | .many => { |
| 33284 | // Only works for *[n]T + [*]T -> [*]T |
| 33285 | const arr = peer_pointee_array orelse return generic_err; |
| 33286 | if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), arr.elem_ty)) |pointee| { |
| 33287 | ptr_info.child = pointee.toIntern(); |
| 33288 | break :good; |
| 33289 | } |
| 33290 | if (arr.elem_ty.toIntern() == .noreturn_type) { |
| 33291 | // *struct{} + [*]T -> [*]T |
| 33292 | break :good; |
| 33293 | } |
| 33294 | return generic_err; |
| 33295 | }, |
| 33296 | .slice => { |
| 33297 | // Only works for *[n]T + []T -> []T |
| 33298 | const arr = peer_pointee_array orelse return generic_err; |
| 33299 | if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), arr.elem_ty)) |pointee| { |
| 33300 | ptr_info.child = pointee.toIntern(); |
| 33301 | break :good; |
| 33302 | } |
| 33303 | if (arr.elem_ty.toIntern() == .noreturn_type) { |
| 33304 | // *struct{} + []T -> []T |
| 33305 | break :good; |
| 33306 | } |
| 33307 | return generic_err; |
| 33308 | }, |
| 33309 | .c => unreachable, |
| 33310 | }, |
| 33311 | .many => switch (ptr_info.flags.size) { |
| 33312 | .one => { |
| 33313 | // Only works for [*]T + *[n]T -> [*]T |
| 33314 | const arr = cur_pointee_array orelse return generic_err; |
| 33315 | if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, .fromInterned(peer_info.child))) |pointee| { |
| 33316 | ptr_info.flags.size = .many; |
| 33317 | ptr_info.child = pointee.toIntern(); |
| 33318 | break :good; |
| 33319 | } |
| 33320 | if (arr.elem_ty.toIntern() == .noreturn_type) { |
| 33321 | // [*]T + *struct{} -> [*]T |
| 33322 | ptr_info.flags.size = .many; |
| 33323 | ptr_info.child = peer_info.child; |
| 33324 | break :good; |
| 33325 | } |
| 33326 | return generic_err; |
| 33327 | }, |
| 33328 | .many => { |
| 33329 | if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| { |
| 33330 | ptr_info.child = pointee.toIntern(); |
| 33331 | break :good; |
| 33332 | } |
| 33333 | return generic_err; |
| 33334 | }, |
| 33335 | .slice => { |
| 33336 | // Only works if no peers are actually slices |
| 33337 | if (opt_slice_idx) |slice_idx| { |
| 33338 | return .{ .conflict = .{ |
| 33339 | .peer_idx_a = slice_idx, |
| 33340 | .peer_idx_b = i, |
| 33341 | } }; |
| 33342 | } |
| 33343 | // Okay, then works for [*]T + "[]T" -> [*]T |
| 33344 | if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| { |
| 33345 | ptr_info.flags.size = .many; |
| 33346 | ptr_info.child = pointee.toIntern(); |
| 33347 | break :good; |
| 33348 | } |
| 33349 | return generic_err; |
| 33350 | }, |
| 33351 | .c => unreachable, |
| 33352 | }, |
| 33353 | .slice => switch (ptr_info.flags.size) { |
| 33354 | .one => { |
| 33355 | // Only works for []T + *[n]T -> []T |
| 33356 | const arr = cur_pointee_array orelse return generic_err; |
| 33357 | if (try sema.resolvePairInMemoryCoercible(block, src, arr.elem_ty, .fromInterned(peer_info.child))) |pointee| { |
| 33358 | ptr_info.flags.size = .slice; |
| 33359 | ptr_info.child = pointee.toIntern(); |
| 33360 | break :good; |
| 33361 | } |
| 33362 | if (arr.elem_ty.toIntern() == .noreturn_type) { |
| 33363 | // []T + *struct{} -> []T |
| 33364 | ptr_info.flags.size = .slice; |
| 33365 | ptr_info.child = peer_info.child; |
| 33366 | break :good; |
| 33367 | } |
| 33368 | return generic_err; |
| 33369 | }, |
| 33370 | .many => { |
| 33371 | // Impossible! (current peer is an actual slice) |
| 33372 | return generic_err; |
| 33373 | }, |
| 33374 | .slice => { |
| 33375 | if (try sema.resolvePairInMemoryCoercible(block, src, .fromInterned(ptr_info.child), .fromInterned(peer_info.child))) |pointee| { |
| 33376 | ptr_info.child = pointee.toIntern(); |
| 33377 | break :good; |
| 33378 | } |
| 33379 | return generic_err; |
| 33380 | }, |
| 33381 | .c => unreachable, |
| 33382 | }, |
| 33383 | .c => unreachable, |
| 33384 | } |
| 33385 | } |
| 33386 | |
| 33387 | const sentinel_ty = switch (ptr_info.flags.size) { |
| 33388 | .one => switch (ip.indexToKey(ptr_info.child)) { |
| 33389 | .array_type => |array_type| array_type.child, |
| 33390 | else => ptr_info.child, |
| 33391 | }, |
| 33392 | .many, .slice, .c => ptr_info.child, |
| 33393 | }; |
| 33394 | |
| 33395 | sentinel: { |
| 33396 | no_sentinel: { |
| 33397 | if (peer_sentinel == .none) break :no_sentinel; |
| 33398 | if (cur_sentinel == .none) break :no_sentinel; |
| 33399 | const peer_sent_coerced = try ip.getCoerced(gpa, io, pt.tid, peer_sentinel, sentinel_ty); |
| 33400 | const cur_sent_coerced = try ip.getCoerced(gpa, io, pt.tid, cur_sentinel, sentinel_ty); |
| 33401 | if (peer_sent_coerced != cur_sent_coerced) break :no_sentinel; |
| 33402 | // Sentinels match |
| 33403 | if (ptr_info.flags.size == .one) switch (ip.indexToKey(ptr_info.child)) { |
| 33404 | .array_type => |array_type| ptr_info.child = (try pt.arrayType(.{ |
| 33405 | .len = array_type.len, |
| 33406 | .child = array_type.child, |
| 33407 | .sentinel = cur_sent_coerced, |
| 33408 | })).toIntern(), |
| 33409 | else => unreachable, |
| 33410 | } else { |
| 33411 | ptr_info.sentinel = cur_sent_coerced; |
| 33412 | } |
| 33413 | break :sentinel; |
| 33414 | } |
| 33415 | // Clear existing sentinel |
| 33416 | ptr_info.sentinel = .none; |
| 33417 | if (ptr_info.flags.size == .one) switch (ip.indexToKey(ptr_info.child)) { |
| 33418 | .array_type => |array_type| ptr_info.child = (try pt.arrayType(.{ |
| 33419 | .len = array_type.len, |
| 33420 | .child = array_type.child, |
| 33421 | .sentinel = .none, |
| 33422 | })).toIntern(), |
| 33423 | else => {}, |
| 33424 | }; |
| 33425 | } |
| 33426 | |
| 33427 | opt_ptr_info = ptr_info; |
| 33428 | } |
| 33429 | |
| 33430 | // Before we succeed, check the pointee type. If we tried to apply PTR to (for instance) |
| 33431 | // &.{} and &.{}, we'll currently have a pointer type of `*[0]noreturn` - we wanted to |
| 33432 | // coerce the empty struct to a specific type, but no peer provided one. We need to |
| 33433 | // detect this case and emit an error. |
| 33434 | const pointee = opt_ptr_info.?.child; |
| 33435 | switch (pointee) { |
| 33436 | .noreturn_type => return .{ .conflict = .{ |
| 33437 | .peer_idx_a = first_idx, |
| 33438 | .peer_idx_b = other_idx, |
| 33439 | } }, |
| 33440 | else => switch (ip.indexToKey(pointee)) { |
| 33441 | .array_type => |array_type| if (array_type.child == .noreturn_type) return .{ .conflict = .{ |
| 33442 | .peer_idx_a = first_idx, |
| 33443 | .peer_idx_b = other_idx, |
| 33444 | } }, |
| 33445 | else => {}, |
| 33446 | }, |
| 33447 | } |
| 33448 | |
| 33449 | return .{ .success = try pt.ptrType(opt_ptr_info.?) }; |
| 33450 | }, |
| 33451 | |
| 33452 | .func => { |
| 33453 | var opt_cur_ty: ?Type = null; |
| 33454 | var first_idx: usize = undefined; |
| 33455 | for (peer_tys, 0..) |opt_ty, i| { |
| 33456 | const ty = opt_ty orelse continue; |
| 33457 | const cur_ty = opt_cur_ty orelse { |
| 33458 | opt_cur_ty = ty; |
| 33459 | first_idx = i; |
| 33460 | continue; |
| 33461 | }; |
| 33462 | if (ty.zigTypeTag(zcu) != .@"fn") return .{ .conflict = .{ |
| 33463 | .peer_idx_a = strat_reason, |
| 33464 | .peer_idx_b = i, |
| 33465 | } }; |
| 33466 | // ty -> cur_ty |
| 33467 | if (.ok == try sema.coerceInMemoryAllowedFns(block, cur_ty, ty, false, target, src, src)) { |
| 33468 | continue; |
| 33469 | } |
| 33470 | // cur_ty -> ty |
| 33471 | if (.ok == try sema.coerceInMemoryAllowedFns(block, ty, cur_ty, false, target, src, src)) { |
| 33472 | opt_cur_ty = ty; |
| 33473 | continue; |
| 33474 | } |
| 33475 | return .{ .conflict = .{ |
| 33476 | .peer_idx_a = first_idx, |
| 33477 | .peer_idx_b = i, |
| 33478 | } }; |
| 33479 | } |
| 33480 | return .{ .success = opt_cur_ty.? }; |
| 33481 | }, |
| 33482 | |
| 33483 | .enum_or_union => { |
| 33484 | var opt_cur_ty: ?Type = null; |
| 33485 | // The peer index which gave the current type |
| 33486 | var cur_ty_idx: usize = undefined; |
| 33487 | |
| 33488 | for (peer_tys, 0..) |opt_ty, i| { |
| 33489 | const ty = opt_ty orelse continue; |
| 33490 | switch (ty.zigTypeTag(zcu)) { |
| 33491 | .enum_literal, .@"enum", .@"union" => {}, |
| 33492 | else => return .{ .conflict = .{ |
| 33493 | .peer_idx_a = strat_reason, |
| 33494 | .peer_idx_b = i, |
| 33495 | } }, |
| 33496 | } |
| 33497 | const cur_ty = opt_cur_ty orelse { |
| 33498 | opt_cur_ty = ty; |
| 33499 | cur_ty_idx = i; |
| 33500 | continue; |
| 33501 | }; |
| 33502 | |
| 33503 | // We want to return this in a lot of cases, so alias it here for convenience |
| 33504 | const generic_err: PeerResolveResult = .{ .conflict = .{ |
| 33505 | .peer_idx_a = cur_ty_idx, |
| 33506 | .peer_idx_b = i, |
| 33507 | } }; |
| 33508 | |
| 33509 | switch (cur_ty.zigTypeTag(zcu)) { |
| 33510 | .enum_literal => { |
| 33511 | opt_cur_ty = ty; |
| 33512 | cur_ty_idx = i; |
| 33513 | }, |
| 33514 | .@"enum" => switch (ty.zigTypeTag(zcu)) { |
| 33515 | .enum_literal => {}, |
| 33516 | .@"enum" => { |
| 33517 | if (!ty.eql(cur_ty)) return generic_err; |
| 33518 | }, |
| 33519 | .@"union" => { |
| 33520 | const tag_ty = ty.unionTagTypeHypothetical(zcu); |
| 33521 | if (!tag_ty.eql(cur_ty)) return generic_err; |
| 33522 | opt_cur_ty = ty; |
| 33523 | cur_ty_idx = i; |
| 33524 | }, |
| 33525 | else => unreachable, |
| 33526 | }, |
| 33527 | .@"union" => switch (ty.zigTypeTag(zcu)) { |
| 33528 | .enum_literal => {}, |
| 33529 | .@"enum" => { |
| 33530 | const cur_tag_ty = cur_ty.unionTagTypeHypothetical(zcu); |
| 33531 | if (!ty.eql(cur_tag_ty)) return generic_err; |
| 33532 | }, |
| 33533 | .@"union" => { |
| 33534 | if (!ty.eql(cur_ty)) return generic_err; |
| 33535 | }, |
| 33536 | else => unreachable, |
| 33537 | }, |
| 33538 | else => unreachable, |
| 33539 | } |
| 33540 | } |
| 33541 | return .{ .success = opt_cur_ty.? }; |
| 33542 | }, |
| 33543 | |
| 33544 | .comptime_int => { |
| 33545 | for (peer_tys, 0..) |opt_ty, i| { |
| 33546 | const ty = opt_ty orelse continue; |
| 33547 | switch (ty.zigTypeTag(zcu)) { |
| 33548 | .comptime_int => {}, |
| 33549 | else => return .{ .conflict = .{ |
| 33550 | .peer_idx_a = strat_reason, |
| 33551 | .peer_idx_b = i, |
| 33552 | } }, |
| 33553 | } |
| 33554 | } |
| 33555 | return .{ .success = .comptime_int }; |
| 33556 | }, |
| 33557 | |
| 33558 | .comptime_float => { |
| 33559 | for (peer_tys, 0..) |opt_ty, i| { |
| 33560 | const ty = opt_ty orelse continue; |
| 33561 | switch (ty.zigTypeTag(zcu)) { |
| 33562 | .comptime_int, .comptime_float => {}, |
| 33563 | else => return .{ .conflict = .{ |
| 33564 | .peer_idx_a = strat_reason, |
| 33565 | .peer_idx_b = i, |
| 33566 | } }, |
| 33567 | } |
| 33568 | } |
| 33569 | return .{ .success = .comptime_float }; |
| 33570 | }, |
| 33571 | |
| 33572 | .fixed_int => { |
| 33573 | var idx_unsigned: ?usize = null; |
| 33574 | var idx_signed: ?usize = null; |
| 33575 | |
| 33576 | // TODO: this is for compatibility with legacy behavior. See beneath the loop. |
| 33577 | var any_comptime_known = false; |
| 33578 | |
| 33579 | for (peer_tys, peer_vals, 0..) |opt_ty, *ptr_opt_val, i| { |
| 33580 | const ty = opt_ty orelse continue; |
| 33581 | const opt_val = ptr_opt_val.*; |
| 33582 | |
| 33583 | const peer_tag = ty.zigTypeTag(zcu); |
| 33584 | switch (peer_tag) { |
| 33585 | .comptime_int => { |
| 33586 | // If the value is undefined, we can't refine to a fixed-width int |
| 33587 | if (opt_val == null or opt_val.?.isUndef(zcu)) return .{ .conflict = .{ |
| 33588 | .peer_idx_a = strat_reason, |
| 33589 | .peer_idx_b = i, |
| 33590 | } }; |
| 33591 | any_comptime_known = true; |
| 33592 | ptr_opt_val.* = opt_val.?; |
| 33593 | continue; |
| 33594 | }, |
| 33595 | .int => {}, |
| 33596 | else => return .{ .conflict = .{ |
| 33597 | .peer_idx_a = strat_reason, |
| 33598 | .peer_idx_b = i, |
| 33599 | } }, |
| 33600 | } |
| 33601 | |
| 33602 | if (opt_val != null) any_comptime_known = true; |
| 33603 | |
| 33604 | const info = ty.intInfo(zcu); |
| 33605 | |
| 33606 | const idx_ptr = switch (info.signedness) { |
| 33607 | .unsigned => &idx_unsigned, |
| 33608 | .signed => &idx_signed, |
| 33609 | }; |
| 33610 | |
| 33611 | const largest_idx = idx_ptr.* orelse { |
| 33612 | idx_ptr.* = i; |
| 33613 | continue; |
| 33614 | }; |
| 33615 | |
| 33616 | const cur_info = peer_tys[largest_idx].?.intInfo(zcu); |
| 33617 | if (info.bits > cur_info.bits) { |
| 33618 | idx_ptr.* = i; |
| 33619 | } |
| 33620 | } |
| 33621 | |
| 33622 | if (idx_signed == null) { |
| 33623 | return .{ .success = peer_tys[idx_unsigned.?].? }; |
| 33624 | } |
| 33625 | |
| 33626 | if (idx_unsigned == null) { |
| 33627 | return .{ .success = peer_tys[idx_signed.?].? }; |
| 33628 | } |
| 33629 | |
| 33630 | const unsigned_info = peer_tys[idx_unsigned.?].?.intInfo(zcu); |
| 33631 | const signed_info = peer_tys[idx_signed.?].?.intInfo(zcu); |
| 33632 | if (signed_info.bits > unsigned_info.bits) { |
| 33633 | return .{ .success = peer_tys[idx_signed.?].? }; |
| 33634 | } |
| 33635 | |
| 33636 | // TODO: this is for compatibility with legacy behavior. Before this version of PTR was |
| 33637 | // implemented, the algorithm very often returned false positives, with the expectation |
| 33638 | // that you'd just hit a coercion error later. One of these was that for integers, the |
| 33639 | // largest type would always be returned, even if it couldn't fit everything. This had |
| 33640 | // an unintentional consequence to semantics, which is that if values were known at |
| 33641 | // comptime, they would be coerced down to the smallest type where possible. This |
| 33642 | // behavior is unintuitive and order-dependent, so in my opinion should be eliminated, |
| 33643 | // but for now we'll retain compatibility. |
| 33644 | if (any_comptime_known) { |
| 33645 | if (unsigned_info.bits > signed_info.bits) { |
| 33646 | return .{ .success = peer_tys[idx_unsigned.?].? }; |
| 33647 | } |
| 33648 | const idx = @min(idx_unsigned.?, idx_signed.?); |
| 33649 | return .{ .success = peer_tys[idx].? }; |
| 33650 | } |
| 33651 | |
| 33652 | return .{ .conflict = .{ |
| 33653 | .peer_idx_a = idx_unsigned.?, |
| 33654 | .peer_idx_b = idx_signed.?, |
| 33655 | } }; |
| 33656 | }, |
| 33657 | |
| 33658 | .fixed_float => { |
| 33659 | var opt_cur_ty: ?Type = null; |
| 33660 | |
| 33661 | for (peer_tys, 0..) |opt_ty, i| { |
| 33662 | const ty = opt_ty orelse continue; |
| 33663 | switch (ty.zigTypeTag(zcu)) { |
| 33664 | .comptime_float, .comptime_int, .int => {}, |
| 33665 | .float => { |
| 33666 | if (opt_cur_ty) |cur_ty| { |
| 33667 | if (cur_ty.eql(ty)) continue; |
| 33668 | // Recreate the type so we eliminate any c_longdouble |
| 33669 | const bits = @max(cur_ty.floatBits(target), ty.floatBits(target)); |
| 33670 | opt_cur_ty = switch (bits) { |
| 33671 | 16 => .f16, |
| 33672 | 32 => .f32, |
| 33673 | 64 => .f64, |
| 33674 | 80 => .f80, |
| 33675 | 128 => .f128, |
| 33676 | else => unreachable, |
| 33677 | }; |
| 33678 | } else { |
| 33679 | opt_cur_ty = ty; |
| 33680 | } |
| 33681 | }, |
| 33682 | else => return .{ .conflict = .{ |
| 33683 | .peer_idx_a = strat_reason, |
| 33684 | .peer_idx_b = i, |
| 33685 | } }, |
| 33686 | } |
| 33687 | } |
| 33688 | |
| 33689 | // Note that fixed_float is only chosen if there is at least one fixed-width float peer, |
| 33690 | // so opt_cur_ty must be non-null. |
| 33691 | const cur_ty = opt_cur_ty.?; |
| 33692 | |
| 33693 | // Ensure that any integer peers can coerce safely to the resulting float. |
| 33694 | for (peer_tys, peer_vals, 0..) |opt_ty, opt_val, i| { |
| 33695 | const ty = opt_ty orelse continue; |
| 33696 | switch (ty.zigTypeTag(zcu)) { |
| 33697 | .comptime_float, .comptime_int, .float => {}, |
| 33698 | .int => { |
| 33699 | if (opt_val != null) continue; |
| 33700 | const int_info = ty.intInfo(zcu); |
| 33701 | const int_precision = int_info.bits - @intFromBool(int_info.signedness == .signed); |
| 33702 | if (int_precision > cur_ty.floatSignificandBits(target)) |
| 33703 | return .{ .conflict = .{ |
| 33704 | .peer_idx_a = strat_reason, |
| 33705 | .peer_idx_b = i, |
| 33706 | } }; |
| 33707 | }, |
| 33708 | else => unreachable, // Previous pass returned on this branch. |
| 33709 | } |
| 33710 | } |
| 33711 | |
| 33712 | return .{ .success = cur_ty }; |
| 33713 | }, |
| 33714 | |
| 33715 | .tuple => { |
| 33716 | // First, check that every peer has the same approximate structure (field count) |
| 33717 | |
| 33718 | var opt_first_idx: ?usize = null; |
| 33719 | var is_tuple: bool = undefined; |
| 33720 | var field_count: usize = undefined; |
| 33721 | |
| 33722 | for (peer_tys, 0..) |opt_ty, i| { |
| 33723 | const ty = opt_ty orelse continue; |
| 33724 | |
| 33725 | if (!ty.isTuple(zcu)) { |
| 33726 | return .{ .conflict = .{ |
| 33727 | .peer_idx_a = strat_reason, |
| 33728 | .peer_idx_b = i, |
| 33729 | } }; |
| 33730 | } |
| 33731 | |
| 33732 | const first_idx = opt_first_idx orelse { |
| 33733 | opt_first_idx = i; |
| 33734 | is_tuple = ty.isTuple(zcu); |
| 33735 | field_count = ty.structFieldCount(zcu); |
| 33736 | continue; |
| 33737 | }; |
| 33738 | |
| 33739 | if (ty.structFieldCount(zcu) != field_count) { |
| 33740 | return .{ .conflict = .{ |
| 33741 | .peer_idx_a = first_idx, |
| 33742 | .peer_idx_b = i, |
| 33743 | } }; |
| 33744 | } |
| 33745 | } |
| 33746 | |
| 33747 | assert(opt_first_idx != null); |
| 33748 | |
| 33749 | // Now, we'll recursively resolve the field types |
| 33750 | const field_types = try sema.arena.alloc(InternPool.Index, field_count); |
| 33751 | // Values for `comptime` fields - `.none` used for non-comptime fields |
| 33752 | const field_vals = try sema.arena.alloc(InternPool.Index, field_count); |
| 33753 | const sub_peer_tys = try sema.arena.alloc(?Type, peer_tys.len); |
| 33754 | const sub_peer_vals = try sema.arena.alloc(?Value, peer_vals.len); |
| 33755 | |
| 33756 | for (field_types, field_vals, 0..) |*field_ty, *field_val, field_index| { |
| 33757 | // Fill buffers with types and values of the field |
| 33758 | for (peer_tys, peer_vals, sub_peer_tys, sub_peer_vals) |opt_ty, opt_val, *peer_field_ty, *peer_field_val| { |
| 33759 | const ty = opt_ty orelse { |
| 33760 | peer_field_ty.* = null; |
| 33761 | peer_field_val.* = null; |
| 33762 | continue; |
| 33763 | }; |
| 33764 | peer_field_ty.* = ty.fieldType(field_index, zcu); |
| 33765 | peer_field_val.* = if (opt_val) |val| try val.fieldValue(pt, field_index) else null; |
| 33766 | } |
| 33767 | |
| 33768 | // Resolve field type recursively |
| 33769 | field_ty.* = switch (try sema.resolvePeerTypesInner(block, src, sub_peer_tys, sub_peer_vals)) { |
| 33770 | .success => |ty| ty.toIntern(), |
| 33771 | else => |result| { |
| 33772 | const result_buf = try sema.arena.create(PeerResolveResult); |
| 33773 | result_buf.* = result; |
| 33774 | const field_name = try ip.getOrPutStringFmt(gpa, io, pt.tid, "{d}", .{field_index}, .no_embedded_nulls); |
| 33775 | |
| 33776 | // The error info needs the field types, but we can't reuse sub_peer_tys |
| 33777 | // since the recursive call may have clobbered it. |
| 33778 | const peer_field_tys = try sema.arena.alloc(Type, peer_tys.len); |
| 33779 | for (peer_tys, peer_field_tys) |opt_ty, *peer_field_ty| { |
| 33780 | // Already-resolved types won't be referenced by the error so it's fine |
| 33781 | // to leave them undefined. |
| 33782 | const ty = opt_ty orelse continue; |
| 33783 | peer_field_ty.* = ty.fieldType(field_index, zcu); |
| 33784 | } |
| 33785 | |
| 33786 | return .{ .field_error = .{ |
| 33787 | .field_name = field_name, |
| 33788 | .field_types = peer_field_tys, |
| 33789 | .sub_result = result_buf, |
| 33790 | } }; |
| 33791 | }, |
| 33792 | }; |
| 33793 | |
| 33794 | // Decide if this is a comptime field. If it is comptime in all peers, and the |
| 33795 | // coerced comptime values are all the same, we say it is comptime, else not. |
| 33796 | |
| 33797 | var comptime_val: ?Value = null; |
| 33798 | for (peer_tys) |opt_ty| { |
| 33799 | const struct_ty = opt_ty orelse continue; |
| 33800 | |
| 33801 | const uncoerced_field_val = try struct_ty.structFieldValueComptime(pt, field_index) orelse { |
| 33802 | comptime_val = null; |
| 33803 | break; |
| 33804 | }; |
| 33805 | const uncoerced_field = Air.internedToRef(uncoerced_field_val.toIntern()); |
| 33806 | const coerced_inst = sema.coerceExtra(block, .fromInterned(field_ty.*), uncoerced_field, src, .{ .report_err = false }) catch |err| switch (err) { |
| 33807 | // It's possible for PTR to give false positives. Just give up on making this a comptime field, we'll get an error later anyway |
| 33808 | error.NotCoercible => { |
| 33809 | comptime_val = null; |
| 33810 | break; |
| 33811 | }, |
| 33812 | else => |e| return e, |
| 33813 | }; |
| 33814 | const coerced_val = sema.resolveValue(coerced_inst) orelse continue; |
| 33815 | const existing = comptime_val orelse { |
| 33816 | comptime_val = coerced_val; |
| 33817 | continue; |
| 33818 | }; |
| 33819 | if (!coerced_val.eql(existing, .fromInterned(field_ty.*), zcu)) { |
| 33820 | comptime_val = null; |
| 33821 | break; |
| 33822 | } |
| 33823 | } |
| 33824 | |
| 33825 | field_val.* = if (comptime_val) |v| v.toIntern() else .none; |
| 33826 | } |
| 33827 | |
| 33828 | const final_ty = try ip.getTupleType(gpa, io, pt.tid, .{ |
| 33829 | .types = field_types, |
| 33830 | .values = field_vals, |
| 33831 | }); |
| 33832 | |
| 33833 | return .{ .success = .fromInterned(final_ty) }; |
| 33834 | }, |
| 33835 | |
| 33836 | .exact => { |
| 33837 | var expect_ty: ?Type = null; |
| 33838 | var first_idx: usize = undefined; |
| 33839 | for (peer_tys, 0..) |opt_ty, i| { |
| 33840 | const ty = opt_ty orelse continue; |
| 33841 | if (expect_ty) |expect| { |
| 33842 | if (!ty.eql(expect)) return .{ .conflict = .{ |
| 33843 | .peer_idx_a = first_idx, |
| 33844 | .peer_idx_b = i, |
| 33845 | } }; |
| 33846 | } else { |
| 33847 | expect_ty = ty; |
| 33848 | first_idx = i; |
| 33849 | } |
| 33850 | } |
| 33851 | return .{ .success = expect_ty.? }; |
| 33852 | }, |
| 33853 | } |
| 33854 | } |
| 33855 | |
| 33856 | fn maybeMergeErrorSets(sema: *Sema, block: *Block, src: LazySrcLoc, e0: Type, e1: Type) !Type { |
| 33857 | // e0 -> e1 |
| 33858 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e1, e0, src, src)) { |
| 33859 | return e1; |
| 33860 | } |
| 33861 | |
| 33862 | // e1 -> e0 |
| 33863 | if (.ok == try sema.coerceInMemoryAllowedErrorSets(block, e0, e1, src, src)) { |
| 33864 | return e0; |
| 33865 | } |
| 33866 | |
| 33867 | return sema.errorSetMerge(e0, e1); |
| 33868 | } |
| 33869 | |
| 33870 | fn resolvePairInMemoryCoercible(sema: *Sema, block: *Block, src: LazySrcLoc, ty_a: Type, ty_b: Type) !?Type { |
| 33871 | const target = sema.pt.zcu.getTarget(); |
| 33872 | |
| 33873 | // ty_b -> ty_a |
| 33874 | if (.ok == try sema.coerceInMemoryAllowed(block, ty_a, ty_b, false, target, src, src, null)) { |
| 33875 | return ty_a; |
| 33876 | } |
| 33877 | |
| 33878 | // ty_a -> ty_b |
| 33879 | if (.ok == try sema.coerceInMemoryAllowed(block, ty_b, ty_a, false, target, src, src, null)) { |
| 33880 | return ty_b; |
| 33881 | } |
| 33882 | |
| 33883 | return null; |
| 33884 | } |
| 33885 | |
| 33886 | const ArrayLike = struct { |
| 33887 | len: u64, |
| 33888 | /// `noreturn` indicates that this type is `struct{}` so can coerce to anything |
| 33889 | elem_ty: Type, |
| 33890 | }; |
| 33891 | fn typeIsArrayLike(sema: *Sema, ty: Type) ?ArrayLike { |
| 33892 | const pt = sema.pt; |
| 33893 | const zcu = pt.zcu; |
| 33894 | return switch (ty.zigTypeTag(zcu)) { |
| 33895 | .array => .{ |
| 33896 | .len = ty.arrayLen(zcu), |
| 33897 | .elem_ty = ty.childType(zcu), |
| 33898 | }, |
| 33899 | .@"struct" => { |
| 33900 | if (!ty.isTuple(zcu)) return null; |
| 33901 | const field_count = ty.structFieldCount(zcu); |
| 33902 | if (field_count == 0) return .{ |
| 33903 | .len = 0, |
| 33904 | .elem_ty = .noreturn, |
| 33905 | }; |
| 33906 | const elem_ty = ty.fieldType(0, zcu); |
| 33907 | for (1..field_count) |i| { |
| 33908 | if (!ty.fieldType(i, zcu).eql(elem_ty)) { |
| 33909 | return null; |
| 33910 | } |
| 33911 | } |
| 33912 | return .{ |
| 33913 | .len = field_count, |
| 33914 | .elem_ty = elem_ty, |
| 33915 | }; |
| 33916 | }, |
| 33917 | else => null, |
| 33918 | }; |
| 33919 | } |
| 33920 | |
| 33921 | fn checkIndexable(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { |
| 33922 | const pt = sema.pt; |
| 33923 | if (!ty.isIndexable(pt.zcu)) { |
| 33924 | const msg = msg: { |
| 33925 | const msg = try sema.errMsg(src, "type '{f}' does not support indexing", .{ty.fmt(pt)}); |
| 33926 | errdefer msg.destroy(sema.gpa); |
| 33927 | try sema.errNote(src, msg, "operand must be an array, slice, tuple, or vector", .{}); |
| 33928 | try sema.addDeclaredHereNote(msg, ty); |
| 33929 | break :msg msg; |
| 33930 | }; |
| 33931 | return sema.failWithOwnedErrorMsg(block, msg); |
| 33932 | } |
| 33933 | } |
| 33934 | |
| 33935 | fn checkMemOperand(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { |
| 33936 | const pt = sema.pt; |
| 33937 | const zcu = pt.zcu; |
| 33938 | if (ty.zigTypeTag(zcu) == .pointer) { |
| 33939 | switch (ty.ptrSize(zcu)) { |
| 33940 | .slice, .many, .c => return, |
| 33941 | .one => { |
| 33942 | const elem_ty = ty.childType(zcu); |
| 33943 | if (elem_ty.zigTypeTag(zcu) == .array) return; |
| 33944 | // TODO https://github.com/ziglang/zig/issues/15479 |
| 33945 | // if (elem_ty.isTuple()) return; |
| 33946 | }, |
| 33947 | } |
| 33948 | } |
| 33949 | const msg = msg: { |
| 33950 | const msg = try sema.errMsg(src, "type '{f}' is not an indexable pointer", .{ty.fmt(pt)}); |
| 33951 | errdefer msg.destroy(sema.gpa); |
| 33952 | try sema.errNote(src, msg, "operand must be a slice, a many pointer or a pointer to an array", .{}); |
| 33953 | break :msg msg; |
| 33954 | }; |
| 33955 | return sema.failWithOwnedErrorMsg(block, msg); |
| 33956 | } |
| 33957 | |
| 33958 | /// Resolves the inferred error set of the given function, so that the corresponding concrete error |
| 33959 | /// set is available by calling `InternPool.funcIesResolvedUnordered` on `func_index`. |
| 33960 | /// |
| 33961 | /// Asserts that `func_index` is a function. Also asserts that it is not a coerced function, because |
| 33962 | /// coerced functions do not own inferred error sets. |
| 33963 | fn ensureFuncIesResolved( |
| 33964 | sema: *Sema, |
| 33965 | block: *Block, |
| 33966 | src: LazySrcLoc, |
| 33967 | func_index: InternPool.Index, |
| 33968 | ) CompileError!void { |
| 33969 | const pt = sema.pt; |
| 33970 | const zcu = pt.zcu; |
| 33971 | const ip = &zcu.intern_pool; |
| 33972 | |
| 33973 | assert(ip.unwrapCoercedFunc(func_index) == func_index); |
| 33974 | |
| 33975 | try sema.declareDependency(.{ .func_ies = func_index }); |
| 33976 | try sema.addReferenceEntry(block, src, .wrap(.{ .func = func_index })); |
| 33977 | |
| 33978 | const reason: Zcu.DependencyReason = .{ .src = src, .type_layout_reason = undefined }; |
| 33979 | |
| 33980 | if (zcu.analysis_in_progress.contains(.wrap(.{ .func = func_index }))) { |
| 33981 | return sema.failWithDependencyLoop(.wrap(.{ .func = func_index }), &reason); |
| 33982 | } |
| 33983 | |
| 33984 | pt.ensureFuncBodyUpToDate(func_index, &reason) catch |err| switch (err) { |
| 33985 | error.AnalysisFail => return sema.failTransitive(.{ .failed_unit = .wrap(.{ .func = func_index }) }), |
| 33986 | else => |e| return e, |
| 33987 | }; |
| 33988 | } |
| 33989 | |
| 33990 | pub fn resolveInferredErrorSetPtr( |
| 33991 | sema: *Sema, |
| 33992 | block: *Block, |
| 33993 | src: LazySrcLoc, |
| 33994 | ies: *InferredErrorSet, |
| 33995 | ) CompileError!void { |
| 33996 | const pt = sema.pt; |
| 33997 | const ip = &pt.zcu.intern_pool; |
| 33998 | |
| 33999 | if (ies.resolved != .none) return; |
| 34000 | |
| 34001 | const ies_index = ip.errorUnionSet(sema.fn_ret_ty.toIntern()); |
| 34002 | |
| 34003 | for (ies.inferred_error_sets.keys()) |other_ies_index| { |
| 34004 | if (ies_index == other_ies_index) continue; |
| 34005 | const other_func_index = ip.iesFuncIndex(other_ies_index); |
| 34006 | try sema.ensureFuncIesResolved(block, src, other_func_index); |
| 34007 | switch (ip.funcIesResolvedUnordered(other_func_index)) { |
| 34008 | .anyerror_type => { |
| 34009 | ies.resolved = .anyerror_type; |
| 34010 | return; |
| 34011 | }, |
| 34012 | else => |error_set_ty_index| { |
| 34013 | const names = ip.indexToKey(error_set_ty_index).error_set_type.names; |
| 34014 | for (names.get(ip)) |name| { |
| 34015 | try ies.errors.put(sema.arena, name, {}); |
| 34016 | } |
| 34017 | }, |
| 34018 | } |
| 34019 | } |
| 34020 | |
| 34021 | const resolved_error_set_ty = try pt.errorSetFromUnsortedNames(ies.errors.keys()); |
| 34022 | ies.resolved = resolved_error_set_ty.toIntern(); |
| 34023 | } |
| 34024 | |
| 34025 | fn resolveAdHocInferredErrorSet( |
| 34026 | sema: *Sema, |
| 34027 | block: *Block, |
| 34028 | src: LazySrcLoc, |
| 34029 | value: InternPool.Index, |
| 34030 | ) CompileError!InternPool.Index { |
| 34031 | const pt = sema.pt; |
| 34032 | const zcu = pt.zcu; |
| 34033 | const comp = zcu.comp; |
| 34034 | const gpa = comp.gpa; |
| 34035 | const io = comp.io; |
| 34036 | const ip = &zcu.intern_pool; |
| 34037 | |
| 34038 | const new_ty = try resolveAdHocInferredErrorSetTy(sema, block, src, ip.typeOf(value)); |
| 34039 | if (new_ty == .none) return value; |
| 34040 | return ip.getCoerced(gpa, io, pt.tid, value, new_ty); |
| 34041 | } |
| 34042 | |
| 34043 | fn resolveAdHocInferredErrorSetTy( |
| 34044 | sema: *Sema, |
| 34045 | block: *Block, |
| 34046 | src: LazySrcLoc, |
| 34047 | ty: InternPool.Index, |
| 34048 | ) CompileError!InternPool.Index { |
| 34049 | const ies = sema.fn_ret_ty_ies orelse return .none; |
| 34050 | const pt = sema.pt; |
| 34051 | const zcu = pt.zcu; |
| 34052 | const ip = &zcu.intern_pool; |
| 34053 | const error_union_info = switch (ip.indexToKey(ty)) { |
| 34054 | .error_union_type => |x| x, |
| 34055 | else => return .none, |
| 34056 | }; |
| 34057 | if (error_union_info.error_set_type != .adhoc_inferred_error_set_type) |
| 34058 | return .none; |
| 34059 | |
| 34060 | try sema.resolveInferredErrorSetPtr(block, src, ies); |
| 34061 | const new_ty = try pt.intern(.{ .error_union_type = .{ |
| 34062 | .error_set_type = ies.resolved, |
| 34063 | .payload_type = error_union_info.payload_type, |
| 34064 | } }); |
| 34065 | return new_ty; |
| 34066 | } |
| 34067 | |
| 34068 | fn resolveInferredErrorSetTy( |
| 34069 | sema: *Sema, |
| 34070 | block: *Block, |
| 34071 | src: LazySrcLoc, |
| 34072 | ty: InternPool.Index, |
| 34073 | ) CompileError!InternPool.Index { |
| 34074 | const pt = sema.pt; |
| 34075 | const zcu = pt.zcu; |
| 34076 | const ip = &zcu.intern_pool; |
| 34077 | if (ty == .anyerror_type) return ty; |
| 34078 | switch (ip.indexToKey(ty)) { |
| 34079 | .error_set_type => return ty, |
| 34080 | .inferred_error_set_type => |func_index| { |
| 34081 | try sema.ensureFuncIesResolved(block, src, func_index); |
| 34082 | return ip.funcIesResolvedUnordered(func_index); |
| 34083 | }, |
| 34084 | else => unreachable, |
| 34085 | } |
| 34086 | } |
| 34087 | |
| 34088 | /// Returns the type of the AIR instruction. |
| 34089 | fn typeOf(sema: *Sema, inst: Air.Inst.Ref) Type { |
| 34090 | return sema.getTmpAir().typeOf(inst, &sema.pt.zcu.intern_pool); |
| 34091 | } |
| 34092 | |
| 34093 | pub fn getTmpAir(sema: Sema) Air { |
| 34094 | return .{ |
| 34095 | .instructions = sema.air_instructions.slice(), |
| 34096 | .extra = sema.air_extra, |
| 34097 | }; |
| 34098 | } |
| 34099 | |
| 34100 | pub fn addExtra(sema: *Sema, extra: anytype) Allocator.Error!u32 { |
| 34101 | const field_count = @typeInfo(@TypeOf(extra)).@"struct".field_names.len; |
| 34102 | try sema.air_extra.ensureUnusedCapacity(sema.gpa, field_count); |
| 34103 | return sema.addExtraAssumeCapacity(extra); |
| 34104 | } |
| 34105 | |
| 34106 | pub fn addExtraAssumeCapacity(sema: *Sema, extra: anytype) u32 { |
| 34107 | const result: u32 = @intCast(sema.air_extra.items.len); |
| 34108 | sema.air_extra.appendSliceAssumeCapacity(&payloadToExtraItems(extra)); |
| 34109 | return result; |
| 34110 | } |
| 34111 | |
| 34112 | fn payloadToExtraItems(data: anytype) [@typeInfo(@TypeOf(data)).@"struct".field_names.len]u32 { |
| 34113 | const info = @typeInfo(@TypeOf(data)).@"struct"; |
| 34114 | var result: [info.field_names.len]u32 = undefined; |
| 34115 | inline for (&result, info.field_names, info.field_types) |*val, field_name, field_type| { |
| 34116 | val.* = switch (field_type) { |
| 34117 | u32 => @field(data, field_name), |
| 34118 | i32, Air.CondBr.BranchHints, Air.Asm.Flags => @bitCast(@field(data, field_name)), |
| 34119 | Air.Inst.Ref, InternPool.Index => @backingInt(@field(data, field_name)), |
| 34120 | else => @compileError("bad field type: " ++ @typeName(field_type)), |
| 34121 | }; |
| 34122 | } |
| 34123 | return result; |
| 34124 | } |
| 34125 | |
| 34126 | fn appendRefsAssumeCapacity(sema: *Sema, refs: []const Air.Inst.Ref) void { |
| 34127 | sema.air_extra.appendSliceAssumeCapacity(@ptrCast(refs)); |
| 34128 | } |
| 34129 | |
| 34130 | fn getBreakBlock(sema: *Sema, inst_index: Air.Inst.Index) ?Air.Inst.Index { |
| 34131 | const air_datas = sema.air_instructions.items(.data); |
| 34132 | const air_tags = sema.air_instructions.items(.tag); |
| 34133 | switch (air_tags[@backingInt(inst_index)]) { |
| 34134 | .br => return air_datas[@backingInt(inst_index)].br.block_inst, |
| 34135 | else => return null, |
| 34136 | } |
| 34137 | } |
| 34138 | |
| 34139 | fn isComptimeKnown( |
| 34140 | sema: *Sema, |
| 34141 | inst: Air.Inst.Ref, |
| 34142 | ) !bool { |
| 34143 | return sema.resolveValue(inst) != null; |
| 34144 | } |
| 34145 | |
| 34146 | /// Asserts that the layout of `var_type` has already been resolved. |
| 34147 | fn analyzeComptimeAlloc( |
| 34148 | sema: *Sema, |
| 34149 | block: *Block, |
| 34150 | src: LazySrcLoc, |
| 34151 | var_type: Type, |
| 34152 | alignment: Alignment, |
| 34153 | ) CompileError!Air.Inst.Ref { |
| 34154 | const pt = sema.pt; |
| 34155 | const zcu = pt.zcu; |
| 34156 | |
| 34157 | var_type.assertHasLayout(zcu); |
| 34158 | |
| 34159 | const ptr_type = try pt.ptrType(.{ |
| 34160 | .child = var_type.toIntern(), |
| 34161 | .flags = .{ |
| 34162 | .alignment = alignment, |
| 34163 | .address_space = target_util.defaultAddressSpace(zcu.getTarget(), .global_constant), |
| 34164 | }, |
| 34165 | }); |
| 34166 | |
| 34167 | if (try var_type.onePossibleValue(pt)) |opv| { |
| 34168 | return .fromIntern(try pt.intern(.{ .ptr = .{ |
| 34169 | .ty = ptr_type.toIntern(), |
| 34170 | .base_addr = .{ .uav = .{ |
| 34171 | .val = opv.toIntern(), |
| 34172 | .orig_ty = ptr_type.toIntern(), |
| 34173 | } }, |
| 34174 | .byte_offset = 0, |
| 34175 | } })); |
| 34176 | } else { |
| 34177 | const alloc = try sema.newComptimeAlloc(block, src, var_type, alignment); |
| 34178 | return .fromIntern(try pt.intern(.{ .ptr = .{ |
| 34179 | .ty = ptr_type.toIntern(), |
| 34180 | .base_addr = .{ .comptime_alloc = alloc }, |
| 34181 | .byte_offset = 0, |
| 34182 | } })); |
| 34183 | } |
| 34184 | } |
| 34185 | |
| 34186 | fn resolveAddressSpace( |
| 34187 | sema: *Sema, |
| 34188 | block: *Block, |
| 34189 | src: LazySrcLoc, |
| 34190 | zir_ref: Zir.Inst.Ref, |
| 34191 | ctx: std.Target.AddressSpaceContext, |
| 34192 | ) !std.lang.AddressSpace { |
| 34193 | const air_ref = sema.resolveInst(zir_ref); |
| 34194 | return sema.analyzeAsAddressSpace(block, src, air_ref, ctx); |
| 34195 | } |
| 34196 | |
| 34197 | pub fn analyzeAsAddressSpace( |
| 34198 | sema: *Sema, |
| 34199 | block: *Block, |
| 34200 | src: LazySrcLoc, |
| 34201 | air_ref: Air.Inst.Ref, |
| 34202 | ctx: std.Target.AddressSpaceContext, |
| 34203 | ) !std.lang.AddressSpace { |
| 34204 | const pt = sema.pt; |
| 34205 | const addrspace_ty = try sema.getStdLangType(src, .AddressSpace); |
| 34206 | const coerced = try sema.coerce(block, addrspace_ty, air_ref, src); |
| 34207 | const addrspace_val = try sema.resolveConstDefinedValue(block, src, coerced, .{ .simple = .@"addrspace" }); |
| 34208 | const address_space = try sema.interpretStdLangType(block, src, addrspace_val, std.lang.AddressSpace); |
| 34209 | const target = pt.zcu.getTarget(); |
| 34210 | |
| 34211 | if (!target.supportsAddressSpace(address_space, ctx)) { |
| 34212 | // TODO error messages could be made more elaborate here |
| 34213 | const entity = switch (ctx) { |
| 34214 | .function => "functions", |
| 34215 | .variable => "mutable values", |
| 34216 | .constant => "constant values", |
| 34217 | .pointer => "pointers", |
| 34218 | }; |
| 34219 | return sema.fail( |
| 34220 | block, |
| 34221 | src, |
| 34222 | "{s} with address space '{s}' are not supported on {s}", |
| 34223 | .{ entity, @tagName(address_space), @tagName(target.cpu.arch.family()) }, |
| 34224 | ); |
| 34225 | } |
| 34226 | |
| 34227 | return address_space; |
| 34228 | } |
| 34229 | |
| 34230 | /// Asserts the value is a pointer and dereferences it. |
| 34231 | /// Returns `null` if the pointer contents cannot be loaded at comptime. |
| 34232 | fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value { |
| 34233 | // TODO: audit use sites to eliminate this coercion |
| 34234 | const pt = sema.pt; |
| 34235 | const coerced_ptr_val = try pt.getCoerced(ptr_val, ptr_ty); |
| 34236 | switch (try sema.pointerDerefExtra(block, src, coerced_ptr_val)) { |
| 34237 | .runtime_load => return null, |
| 34238 | .val => |v| return v, |
| 34239 | .needed_well_defined => |ty| return sema.fail( |
| 34240 | block, |
| 34241 | src, |
| 34242 | "comptime dereference requires '{f}' to have a well-defined layout", |
| 34243 | .{ty.fmt(pt)}, |
| 34244 | ), |
| 34245 | .out_of_bounds => |ty| return sema.fail( |
| 34246 | block, |
| 34247 | src, |
| 34248 | "dereference of '{f}' exceeds bounds of containing decl of type '{f}'", |
| 34249 | .{ ptr_ty.fmt(pt), ty.fmt(pt) }, |
| 34250 | ), |
| 34251 | } |
| 34252 | } |
| 34253 | |
| 34254 | const DerefResult = union(enum) { |
| 34255 | runtime_load, |
| 34256 | val: Value, |
| 34257 | needed_well_defined: Type, |
| 34258 | out_of_bounds: Type, |
| 34259 | }; |
| 34260 | |
| 34261 | fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value) CompileError!DerefResult { |
| 34262 | const pt = sema.pt; |
| 34263 | const ip = &pt.zcu.intern_pool; |
| 34264 | switch (try sema.loadComptimePtr(block, src, ptr_val)) { |
| 34265 | .success => |mv| return .{ .val = try mv.intern(pt, sema.arena) }, |
| 34266 | .runtime_load => return .runtime_load, |
| 34267 | .undef => return sema.failWithUseOfUndef(block, src, null), |
| 34268 | .err_payload => |err_name| return sema.fail(block, src, "attempt to unwrap error: {f}", .{err_name.fmt(ip)}), |
| 34269 | .null_payload => return sema.fail(block, src, "attempt to use null value", .{}), |
| 34270 | .inactive_union_field => return sema.fail(block, src, "access of inactive union field", .{}), |
| 34271 | .needed_well_defined => |ty| return .{ .needed_well_defined = ty }, |
| 34272 | .out_of_bounds => |ty| return .{ .out_of_bounds = ty }, |
| 34273 | .exceeds_host_size => return sema.fail(block, src, "bit-pointer target exceeds host size", .{}), |
| 34274 | } |
| 34275 | } |
| 34276 | |
| 34277 | /// Used to convert a u64 value to a usize value, emitting a compile error if the number |
| 34278 | /// is too big to fit. |
| 34279 | fn usizeCast(sema: *Sema, block: *Block, src: LazySrcLoc, int: u64) CompileError!usize { |
| 34280 | if (@bitSizeOf(u64) <= @bitSizeOf(usize)) return int; |
| 34281 | return std.math.cast(usize, int) orelse return sema.fail(block, src, "expression produces integer value '{d}' which is too big for this compiler implementation to handle", .{int}); |
| 34282 | } |
| 34283 | |
| 34284 | /// Asserts that the layout of `union_ty` is already resolved. |
| 34285 | fn unionFieldIndex( |
| 34286 | sema: *Sema, |
| 34287 | block: *Block, |
| 34288 | union_ty: Type, |
| 34289 | field_name: InternPool.NullTerminatedString, |
| 34290 | field_src: LazySrcLoc, |
| 34291 | ) !u32 { |
| 34292 | const pt = sema.pt; |
| 34293 | const zcu = pt.zcu; |
| 34294 | const ip = &zcu.intern_pool; |
| 34295 | const union_obj = zcu.typeToUnion(union_ty).?; |
| 34296 | const enum_obj = ip.loadEnumType(union_obj.enum_tag_type); |
| 34297 | const field_index = enum_obj.nameIndex(ip, field_name) orelse |
| 34298 | return sema.failWithBadUnionFieldAccess(block, union_ty, union_obj, field_src, field_name); |
| 34299 | return @intCast(field_index); |
| 34300 | } |
| 34301 | |
| 34302 | /// Asserts that the layout of `struct_ty` is already resolved. |
| 34303 | fn structFieldIndex( |
| 34304 | sema: *Sema, |
| 34305 | block: *Block, |
| 34306 | struct_ty: Type, |
| 34307 | field_name: InternPool.NullTerminatedString, |
| 34308 | field_src: LazySrcLoc, |
| 34309 | ) !u32 { |
| 34310 | const pt = sema.pt; |
| 34311 | const zcu = pt.zcu; |
| 34312 | const ip = &zcu.intern_pool; |
| 34313 | const struct_type = zcu.typeToStruct(struct_ty).?; |
| 34314 | return struct_type.nameIndex(ip, field_name) orelse |
| 34315 | return sema.failWithBadStructFieldAccess(block, struct_ty, struct_type, field_src, field_name); |
| 34316 | } |
| 34317 | |
| 34318 | const IntFromFloatMode = enum { exact, truncate, round, floor, ceil }; |
| 34319 | |
| 34320 | fn intFromFloat( |
| 34321 | sema: *Sema, |
| 34322 | block: *Block, |
| 34323 | src: LazySrcLoc, |
| 34324 | val: Value, |
| 34325 | float_ty: Type, |
| 34326 | int_ty: Type, |
| 34327 | mode: IntFromFloatMode, |
| 34328 | ) CompileError!Value { |
| 34329 | const pt = sema.pt; |
| 34330 | const zcu = pt.zcu; |
| 34331 | if (float_ty.zigTypeTag(zcu) == .vector) { |
| 34332 | const result_data = try sema.arena.alloc(InternPool.Index, float_ty.vectorLen(zcu)); |
| 34333 | for (result_data, 0..) |*scalar, elem_idx| { |
| 34334 | const elem_val = try val.elemValue(pt, elem_idx); |
| 34335 | scalar.* = (try sema.intFromFloatScalar(block, src, elem_val, int_ty.scalarType(zcu), mode, elem_idx)).toIntern(); |
| 34336 | } |
| 34337 | return pt.aggregateValue(int_ty, result_data); |
| 34338 | } |
| 34339 | return sema.intFromFloatScalar(block, src, val, int_ty, mode, null); |
| 34340 | } |
| 34341 | |
| 34342 | fn intFromFloatScalar( |
| 34343 | sema: *Sema, |
| 34344 | block: *Block, |
| 34345 | src: LazySrcLoc, |
| 34346 | val: Value, |
| 34347 | int_ty: Type, |
| 34348 | mode: IntFromFloatMode, |
| 34349 | vec_idx: ?usize, |
| 34350 | ) CompileError!Value { |
| 34351 | const pt = sema.pt; |
| 34352 | const zcu = pt.zcu; |
| 34353 | |
| 34354 | if (val.isUndef(zcu)) return sema.failWithUseOfUndef(block, src, vec_idx); |
| 34355 | |
| 34356 | var float = val.toFloat(f128, zcu); |
| 34357 | switch (mode) { |
| 34358 | .round => float = @round(float), |
| 34359 | .floor => float = @floor(float), |
| 34360 | .ceil => float = @ceil(float), |
| 34361 | .truncate, .exact => {}, |
| 34362 | } |
| 34363 | |
| 34364 | if (std.math.isNan(float)) { |
| 34365 | return sema.fail(block, src, "float value NaN cannot be stored in integer type '{f}'", .{ |
| 34366 | int_ty.fmt(pt), |
| 34367 | }); |
| 34368 | } |
| 34369 | if (std.math.isInf(float)) { |
| 34370 | return sema.fail(block, src, "float value Inf cannot be stored in integer type '{f}'", .{ |
| 34371 | int_ty.fmt(pt), |
| 34372 | }); |
| 34373 | } |
| 34374 | |
| 34375 | var big_int: std.math.big.int.Mutable = .{ |
| 34376 | .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(float)), |
| 34377 | .len = undefined, |
| 34378 | .positive = undefined, |
| 34379 | }; |
| 34380 | switch (big_int.setFloat(float, .trunc)) { |
| 34381 | .inexact => switch (mode) { |
| 34382 | .exact => return sema.fail( |
| 34383 | block, |
| 34384 | src, |
| 34385 | "fractional component prevents float value '{f}' from coercion to type '{f}'", |
| 34386 | .{ val.fmtValueSema(pt, sema), int_ty.fmt(pt) }, |
| 34387 | ), |
| 34388 | .truncate, .round, .floor, .ceil => {}, |
| 34389 | }, |
| 34390 | .exact => {}, |
| 34391 | } |
| 34392 | const cti_result = try pt.intValue_big(.comptime_int, big_int.toConst()); |
| 34393 | if (int_ty.toIntern() == .comptime_int_type) return cti_result; |
| 34394 | |
| 34395 | const int_info = int_ty.intInfo(zcu); |
| 34396 | if (!big_int.toConst().fitsInTwosComp(int_info.signedness, int_info.bits)) { |
| 34397 | return sema.fail(block, src, "float value '{f}' cannot be stored in integer type '{f}'", .{ |
| 34398 | val.fmtValueSema(pt, sema), int_ty.fmt(pt), |
| 34399 | }); |
| 34400 | } |
| 34401 | return pt.getCoerced(cti_result, int_ty); |
| 34402 | } |
| 34403 | |
| 34404 | /// Asserts the type is an exhaustive enum. |
| 34405 | fn enumHasInt(sema: *Sema, ty: Type, int: Value) CompileError!bool { |
| 34406 | const pt = sema.pt; |
| 34407 | const zcu = pt.zcu; |
| 34408 | const enum_type = zcu.intern_pool.loadEnumType(ty.toIntern()); |
| 34409 | assert(!enum_type.nonexhaustive); |
| 34410 | // The `tagValueIndex` function call below relies on the type being the integer tag type. |
| 34411 | // `getCoerced` assumes the value will fit the new type. |
| 34412 | const int_tag_ty: Type = .fromInterned(enum_type.int_tag_type); |
| 34413 | if (int_tag_ty.classify(zcu) == .no_possible_value) return false; |
| 34414 | if (!int.intFitsInType(int_tag_ty, null, zcu)) return false; |
| 34415 | const int_coerced = try pt.getCoerced(int, int_tag_ty); |
| 34416 | return enum_type.tagValueIndex(&zcu.intern_pool, int_coerced.toIntern()) != null; |
| 34417 | } |
| 34418 | |
| 34419 | /// Asserts the values are comparable. Both operands have type `ty`. |
| 34420 | /// For vectors, returns true if the comparison is true for ALL elements. |
| 34421 | /// |
| 34422 | /// Note that `!compareAll(.eq, ...) != compareAll(.neq, ...)` |
| 34423 | fn compareAll( |
| 34424 | sema: *Sema, |
| 34425 | lhs: Value, |
| 34426 | op: std.math.CompareOperator, |
| 34427 | rhs: Value, |
| 34428 | ty: Type, |
| 34429 | ) CompileError!bool { |
| 34430 | const pt = sema.pt; |
| 34431 | const zcu = pt.zcu; |
| 34432 | if (ty.zigTypeTag(zcu) == .vector) { |
| 34433 | var i: usize = 0; |
| 34434 | while (i < ty.vectorLen(zcu)) : (i += 1) { |
| 34435 | const lhs_elem = try lhs.elemValue(pt, i); |
| 34436 | const rhs_elem = try rhs.elemValue(pt, i); |
| 34437 | if (!(try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(zcu)))) { |
| 34438 | return false; |
| 34439 | } |
| 34440 | } |
| 34441 | return true; |
| 34442 | } |
| 34443 | return sema.compareScalar(lhs, op, rhs, ty); |
| 34444 | } |
| 34445 | |
| 34446 | /// Asserts the values are comparable. Both operands have type `ty`. |
| 34447 | fn compareScalar( |
| 34448 | sema: *Sema, |
| 34449 | lhs: Value, |
| 34450 | op: std.math.CompareOperator, |
| 34451 | rhs: Value, |
| 34452 | ty: Type, |
| 34453 | ) CompileError!bool { |
| 34454 | const pt = sema.pt; |
| 34455 | const zcu = pt.zcu; |
| 34456 | |
| 34457 | const coerced_lhs = try pt.getCoerced(lhs, ty); |
| 34458 | const coerced_rhs = try pt.getCoerced(rhs, ty); |
| 34459 | |
| 34460 | // Equality comparisons of signed zero and NaN need to use floating point semantics |
| 34461 | if (coerced_lhs.isFloat(zcu) or coerced_rhs.isFloat(zcu)) |
| 34462 | return Value.compareHetero(coerced_lhs, op, coerced_rhs, zcu); |
| 34463 | |
| 34464 | switch (op) { |
| 34465 | .eq => return Value.eql(coerced_lhs, coerced_rhs, ty, zcu), |
| 34466 | .neq => return !Value.eql(coerced_lhs, coerced_rhs, ty, zcu), |
| 34467 | else => return Value.compareHetero(coerced_lhs, op, coerced_rhs, zcu), |
| 34468 | } |
| 34469 | } |
| 34470 | |
| 34471 | fn valuesEqual( |
| 34472 | sema: *Sema, |
| 34473 | lhs: Value, |
| 34474 | rhs: Value, |
| 34475 | ty: Type, |
| 34476 | ) CompileError!bool { |
| 34477 | return lhs.eql(rhs, ty, sema.pt.zcu); |
| 34478 | } |
| 34479 | |
| 34480 | /// Asserts the values are comparable vectors of type `ty`. |
| 34481 | fn compareVector( |
| 34482 | sema: *Sema, |
| 34483 | lhs: Value, |
| 34484 | op: std.math.CompareOperator, |
| 34485 | rhs: Value, |
| 34486 | ty: Type, |
| 34487 | ) !Value { |
| 34488 | const pt = sema.pt; |
| 34489 | const zcu = pt.zcu; |
| 34490 | assert(ty.zigTypeTag(zcu) == .vector); |
| 34491 | const result_data = try sema.arena.alloc(InternPool.Index, ty.vectorLen(zcu)); |
| 34492 | for (result_data, 0..) |*scalar, i| { |
| 34493 | const lhs_elem = try lhs.elemValue(pt, i); |
| 34494 | const rhs_elem = try rhs.elemValue(pt, i); |
| 34495 | if (lhs_elem.isUndef(zcu) or rhs_elem.isUndef(zcu)) { |
| 34496 | scalar.* = .undef_bool; |
| 34497 | } else { |
| 34498 | const res_bool = try sema.compareScalar(lhs_elem, op, rhs_elem, ty.scalarType(zcu)); |
| 34499 | scalar.* = Value.makeBool(res_bool).toIntern(); |
| 34500 | } |
| 34501 | } |
| 34502 | return pt.aggregateValue(try pt.vectorType(.{ |
| 34503 | .len = ty.vectorLen(zcu), |
| 34504 | .child = .bool_type, |
| 34505 | }), result_data); |
| 34506 | } |
| 34507 | |
| 34508 | /// Merge lhs with rhs. |
| 34509 | /// Asserts that lhs and rhs are both error sets and are resolved. |
| 34510 | fn errorSetMerge(sema: *Sema, lhs: Type, rhs: Type) !Type { |
| 34511 | const pt = sema.pt; |
| 34512 | const ip = &pt.zcu.intern_pool; |
| 34513 | const arena = sema.arena; |
| 34514 | const lhs_names = lhs.errorSetNames(pt.zcu); |
| 34515 | const rhs_names = rhs.errorSetNames(pt.zcu); |
| 34516 | var names: InferredErrorSet.NameMap = .{}; |
| 34517 | try names.ensureUnusedCapacity(arena, lhs_names.len); |
| 34518 | |
| 34519 | for (0..lhs_names.len) |lhs_index| { |
| 34520 | names.putAssumeCapacityNoClobber(lhs_names.get(ip)[lhs_index], {}); |
| 34521 | } |
| 34522 | for (0..rhs_names.len) |rhs_index| { |
| 34523 | try names.put(arena, rhs_names.get(ip)[rhs_index], {}); |
| 34524 | } |
| 34525 | |
| 34526 | return pt.errorSetFromUnsortedNames(names.keys()); |
| 34527 | } |
| 34528 | |
| 34529 | pub fn declareDependency(sema: *Sema, dependee: InternPool.Dependee) !void { |
| 34530 | const pt = sema.pt; |
| 34531 | if (!pt.zcu.comp.config.incremental) return; |
| 34532 | |
| 34533 | const gop = try sema.dependencies.getOrPut(sema.gpa, dependee); |
| 34534 | if (gop.found_existing) return; |
| 34535 | |
| 34536 | try pt.addDependency(sema.owner, dependee); |
| 34537 | } |
| 34538 | |
| 34539 | fn isComptimeMutablePtr(sema: *Sema, val: Value) bool { |
| 34540 | return switch (sema.pt.zcu.intern_pool.indexToKey(val.toIntern())) { |
| 34541 | .slice => |slice| sema.isComptimeMutablePtr(Value.fromInterned(slice.ptr)), |
| 34542 | .ptr => |ptr| switch (ptr.base_addr) { |
| 34543 | .uav, .nav, .int => false, |
| 34544 | .comptime_field => true, |
| 34545 | .comptime_alloc => |alloc_index| !sema.getComptimeAlloc(alloc_index).is_const, |
| 34546 | .eu_payload, .opt_payload => |base| sema.isComptimeMutablePtr(Value.fromInterned(base)), |
| 34547 | .arr_elem, .field => |bi| sema.isComptimeMutablePtr(Value.fromInterned(bi.base)), |
| 34548 | }, |
| 34549 | else => false, |
| 34550 | }; |
| 34551 | } |
| 34552 | |
| 34553 | fn checkRuntimeValue(sema: *Sema, ptr: Air.Inst.Ref) bool { |
| 34554 | const val = ptr.toInterned() orelse return true; |
| 34555 | return !Value.fromInterned(val).canMutateComptimeVarState(sema.pt.zcu); |
| 34556 | } |
| 34557 | |
| 34558 | fn validateRuntimeValue(sema: *Sema, block: *Block, val_src: LazySrcLoc, val: Air.Inst.Ref) CompileError!void { |
| 34559 | if (sema.checkRuntimeValue(val)) return; |
| 34560 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 34561 | const pt = sema.pt; |
| 34562 | const zcu = pt.zcu; |
| 34563 | const comp = zcu.comp; |
| 34564 | const gpa = comp.gpa; |
| 34565 | const io = comp.io; |
| 34566 | |
| 34567 | const msg = try sema.errMsg(val_src, "runtime value contains reference to comptime var", .{}); |
| 34568 | errdefer msg.destroy(gpa); |
| 34569 | try sema.errNote(val_src, msg, "comptime var pointers are not available at runtime", .{}); |
| 34570 | const val_str = try pt.zcu.intern_pool.getOrPutString(gpa, io, pt.tid, "runtime_value", .no_embedded_nulls); |
| 34571 | try sema.explainWhyValueContainsReferenceToComptimeVar(msg, val_src, val_str, .fromInterned(val.toInterned().?)); |
| 34572 | break :msg msg; |
| 34573 | }); |
| 34574 | } |
| 34575 | |
| 34576 | pub fn failWithContainsReferenceToComptimeVar(sema: *Sema, block: *Block, src: LazySrcLoc, value_name: InternPool.NullTerminatedString, kind_of_value: []const u8, val: ?Value) CompileError { |
| 34577 | return sema.failWithOwnedErrorMsg(block, msg: { |
| 34578 | const msg = try sema.errMsg(src, "{s} contains reference to comptime var", .{kind_of_value}); |
| 34579 | errdefer msg.destroy(sema.gpa); |
| 34580 | if (val) |v| try sema.explainWhyValueContainsReferenceToComptimeVar(msg, src, value_name, v); |
| 34581 | break :msg msg; |
| 34582 | }); |
| 34583 | } |
| 34584 | |
| 34585 | fn explainWhyValueContainsReferenceToComptimeVar(sema: *Sema, msg: *Zcu.ErrorMsg, src: LazySrcLoc, value_name: InternPool.NullTerminatedString, val: Value) Allocator.Error!void { |
| 34586 | // Our goal is something like this: |
| 34587 | // note: '(value.? catch unreachable)[0]' points to 'v0.?.foo' |
| 34588 | // note: '(v0.?.bar catch unreachable)' points to 'v1' |
| 34589 | // note: 'v1.?' points to a comptime var |
| 34590 | |
| 34591 | var intermediate_value_count: u32 = 0; |
| 34592 | var cur_val: Value = val; |
| 34593 | while (true) { |
| 34594 | switch (try sema.notePathToComptimeAllocPtr(msg, src, cur_val, intermediate_value_count, value_name)) { |
| 34595 | .done => return, |
| 34596 | .new_val => |new_val| { |
| 34597 | intermediate_value_count += 1; |
| 34598 | cur_val = new_val; |
| 34599 | }, |
| 34600 | } |
| 34601 | } |
| 34602 | } |
| 34603 | |
| 34604 | fn notePathToComptimeAllocPtr( |
| 34605 | sema: *Sema, |
| 34606 | msg: *Zcu.ErrorMsg, |
| 34607 | src: LazySrcLoc, |
| 34608 | val: Value, |
| 34609 | intermediate_value_count: u32, |
| 34610 | start_value_name: InternPool.NullTerminatedString, |
| 34611 | ) Allocator.Error!union(enum) { |
| 34612 | done, |
| 34613 | new_val: Value, |
| 34614 | } { |
| 34615 | const arena = sema.arena; |
| 34616 | const pt = sema.pt; |
| 34617 | const zcu = pt.zcu; |
| 34618 | const ip = &zcu.intern_pool; |
| 34619 | |
| 34620 | var first_path: std.ArrayList(u8) = .empty; |
| 34621 | if (intermediate_value_count == 0) { |
| 34622 | try first_path.print(arena, "{f}", .{start_value_name.fmt(ip)}); |
| 34623 | } else { |
| 34624 | try first_path.print(arena, "v{d}", .{intermediate_value_count - 1}); |
| 34625 | } |
| 34626 | |
| 34627 | const comptime_ptr = try sema.notePathToComptimeAllocPtrInner(val, &first_path); |
| 34628 | |
| 34629 | switch (ip.indexToKey(comptime_ptr.toIntern()).ptr.base_addr) { |
| 34630 | .comptime_field => { |
| 34631 | try sema.errNote(src, msg, "'{s}' points to comptime field", .{first_path.items}); |
| 34632 | return .done; |
| 34633 | }, |
| 34634 | .comptime_alloc => |idx| { |
| 34635 | const cta = sema.getComptimeAlloc(idx); |
| 34636 | if (!cta.is_const) { |
| 34637 | try sema.errNote(cta.src, msg, "'{s}' points to comptime var declared here", .{first_path.items}); |
| 34638 | return .done; |
| 34639 | } |
| 34640 | }, |
| 34641 | else => {}, // there will be another stage |
| 34642 | } |
| 34643 | |
| 34644 | const derivation = try comptime_ptr.pointerDerivation(arena, pt, sema); |
| 34645 | |
| 34646 | var second_path_aw: std.Io.Writer.Allocating = .init(arena); |
| 34647 | defer second_path_aw.deinit(); |
| 34648 | const inter_name = try std.fmt.allocPrint(arena, "v{d}", .{intermediate_value_count}); |
| 34649 | const deriv_start = @import("print_value.zig").printPtrDerivation( |
| 34650 | derivation, |
| 34651 | &second_path_aw.writer, |
| 34652 | pt, |
| 34653 | .lvalue, |
| 34654 | .{ .str = inter_name }, |
| 34655 | 20, |
| 34656 | ) catch return error.OutOfMemory; |
| 34657 | |
| 34658 | switch (deriv_start) { |
| 34659 | .int, .nav_ptr => unreachable, |
| 34660 | .uav_ptr => |uav| { |
| 34661 | try sema.errNote(src, msg, "'{s}' points to '{s}', where", .{ first_path.items, second_path_aw.written() }); |
| 34662 | return .{ .new_val = .fromInterned(uav.val) }; |
| 34663 | }, |
| 34664 | .comptime_alloc_ptr => |cta_info| { |
| 34665 | try sema.errNote(src, msg, "'{s}' points to '{s}', where", .{ first_path.items, second_path_aw.written() }); |
| 34666 | const cta = sema.getComptimeAlloc(cta_info.idx); |
| 34667 | if (cta.is_const) { |
| 34668 | return .{ .new_val = cta_info.val }; |
| 34669 | } else { |
| 34670 | try sema.errNote(cta.src, msg, "'{s}' is a comptime var declared here", .{inter_name}); |
| 34671 | return .done; |
| 34672 | } |
| 34673 | }, |
| 34674 | .comptime_field_ptr => { |
| 34675 | try sema.errNote(src, msg, "'{s}' points to '{s}', where", .{ first_path.items, second_path_aw.written() }); |
| 34676 | try sema.errNote(src, msg, "'{s}' is a comptime field", .{inter_name}); |
| 34677 | return .done; |
| 34678 | }, |
| 34679 | .eu_payload_ptr, |
| 34680 | .opt_payload_ptr, |
| 34681 | .field_ptr, |
| 34682 | .elem_ptr, |
| 34683 | .offset_and_cast, |
| 34684 | => unreachable, |
| 34685 | } |
| 34686 | } |
| 34687 | |
| 34688 | fn notePathToComptimeAllocPtrInner(sema: *Sema, val: Value, path: *std.ArrayList(u8)) Allocator.Error!Value { |
| 34689 | const pt = sema.pt; |
| 34690 | const zcu = pt.zcu; |
| 34691 | const ip = &zcu.intern_pool; |
| 34692 | const arena = sema.arena; |
| 34693 | assert(val.canMutateComptimeVarState(zcu)); |
| 34694 | switch (ip.indexToKey(val.toIntern())) { |
| 34695 | .ptr => return val, |
| 34696 | .error_union => |eu| { |
| 34697 | try path.insert(arena, 0, '('); |
| 34698 | try path.appendSlice(arena, " catch unreachable)"); |
| 34699 | return sema.notePathToComptimeAllocPtrInner(.fromInterned(eu.val.payload), path); |
| 34700 | }, |
| 34701 | .slice => |slice| { |
| 34702 | try path.appendSlice(arena, ".ptr"); |
| 34703 | return sema.notePathToComptimeAllocPtrInner(.fromInterned(slice.ptr), path); |
| 34704 | }, |
| 34705 | .opt => |opt| { |
| 34706 | try path.appendSlice(arena, ".?"); |
| 34707 | return sema.notePathToComptimeAllocPtrInner(.fromInterned(opt.val), path); |
| 34708 | }, |
| 34709 | .un => |un| { |
| 34710 | assert(un.tag != .none); |
| 34711 | const union_ty: Type = .fromInterned(un.ty); |
| 34712 | const backing_enum = union_ty.unionTagTypeHypothetical(zcu); |
| 34713 | const field_idx = backing_enum.enumTagFieldIndex(.fromInterned(un.tag), zcu).?; |
| 34714 | const field_name = backing_enum.enumFieldName(field_idx, zcu); |
| 34715 | try path.print(arena, ".{f}", .{field_name.fmt(ip)}); |
| 34716 | return sema.notePathToComptimeAllocPtrInner(.fromInterned(un.val), path); |
| 34717 | }, |
| 34718 | .aggregate => |agg| { |
| 34719 | const elem: InternPool.Index, const elem_idx: usize = switch (agg.storage) { |
| 34720 | .bytes => unreachable, |
| 34721 | .repeated_elem => |elem| .{ elem, 0 }, |
| 34722 | .elems => |elems| for (elems, 0..) |elem, elem_idx| { |
| 34723 | if (Value.fromInterned(elem).canMutateComptimeVarState(zcu)) { |
| 34724 | break .{ elem, elem_idx }; |
| 34725 | } |
| 34726 | } else unreachable, |
| 34727 | }; |
| 34728 | const agg_ty: Type = .fromInterned(agg.ty); |
| 34729 | switch (agg_ty.zigTypeTag(zcu)) { |
| 34730 | .array, .vector => try path.print(arena, "[{d}]", .{elem_idx}), |
| 34731 | .pointer => switch (elem_idx) { |
| 34732 | Value.slice_ptr_index => try path.appendSlice(arena, ".ptr"), |
| 34733 | Value.slice_len_index => try path.appendSlice(arena, ".len"), |
| 34734 | else => unreachable, |
| 34735 | }, |
| 34736 | .@"struct" => if (agg_ty.isTuple(zcu)) { |
| 34737 | try path.print(arena, "[{d}]", .{elem_idx}); |
| 34738 | } else { |
| 34739 | const name = agg_ty.structFieldName(elem_idx, zcu).unwrap().?; |
| 34740 | try path.print(arena, ".{f}", .{name.fmt(ip)}); |
| 34741 | }, |
| 34742 | else => unreachable, |
| 34743 | } |
| 34744 | return sema.notePathToComptimeAllocPtrInner(.fromInterned(elem), path); |
| 34745 | }, |
| 34746 | else => unreachable, |
| 34747 | } |
| 34748 | } |
| 34749 | |
| 34750 | /// Returns true if any value contained in `val` is undefined. |
| 34751 | fn anyUndef(sema: *Sema, block: *Block, src: LazySrcLoc, val: Value) !bool { |
| 34752 | const pt = sema.pt; |
| 34753 | const zcu = pt.zcu; |
| 34754 | return switch (zcu.intern_pool.indexToKey(val.toIntern())) { |
| 34755 | .undef => true, |
| 34756 | .slice => { |
| 34757 | // If the slice contents are runtime-known, reification will fail later on with a |
| 34758 | // specific error message. |
| 34759 | const arr = try sema.maybeDerefSliceAsArray(block, src, val) orelse return false; |
| 34760 | return sema.anyUndef(block, src, arr); |
| 34761 | }, |
| 34762 | .aggregate => |aggregate| for (0..aggregate.storage.values().len) |i| { |
| 34763 | const elem = zcu.intern_pool.indexToKey(val.toIntern()).aggregate.storage.values()[i]; |
| 34764 | if (try sema.anyUndef(block, src, Value.fromInterned(elem))) break true; |
| 34765 | } else false, |
| 34766 | else => false, |
| 34767 | }; |
| 34768 | } |
| 34769 | |
| 34770 | /// Asserts that `slice_val` is a slice of `u8`. |
| 34771 | fn sliceToIpString( |
| 34772 | sema: *Sema, |
| 34773 | block: *Block, |
| 34774 | src: LazySrcLoc, |
| 34775 | slice_val: Value, |
| 34776 | reason: ComptimeReason, |
| 34777 | ) CompileError!InternPool.NullTerminatedString { |
| 34778 | const pt = sema.pt; |
| 34779 | const zcu = pt.zcu; |
| 34780 | const slice_ty = slice_val.typeOf(zcu); |
| 34781 | assert(slice_ty.isSlice(zcu)); |
| 34782 | assert(slice_ty.childType(zcu).toIntern() == .u8_type); |
| 34783 | const array_val = try sema.derefSliceAsArray(block, src, slice_val, reason); |
| 34784 | const array_ty = array_val.typeOf(zcu); |
| 34785 | return array_val.toIpString(array_ty, pt); |
| 34786 | } |
| 34787 | |
| 34788 | /// Given a slice value, attempts to dereference it into a comptime-known array. |
| 34789 | /// Emits a compile error if the contents of the slice are not comptime-known. |
| 34790 | /// Asserts that `slice_val` is a slice or a pointer to an array. |
| 34791 | fn derefSliceAsArray( |
| 34792 | sema: *Sema, |
| 34793 | block: *Block, |
| 34794 | src: LazySrcLoc, |
| 34795 | slice_val: Value, |
| 34796 | /// `null` may be passed only if `block.isComptime()`. It indicates that the reason for the value |
| 34797 | /// being comptime-resolved is that the block is being comptime-evaluated. |
| 34798 | reason: ?ComptimeReason, |
| 34799 | ) CompileError!Value { |
| 34800 | return try sema.maybeDerefSliceAsArray(block, src, slice_val) orelse { |
| 34801 | return sema.failWithNeededComptime(block, src, reason); |
| 34802 | }; |
| 34803 | } |
| 34804 | |
| 34805 | /// Given a slice value, attempts to dereference it into a comptime-known array. |
| 34806 | /// Returns `null` if the contents of the slice are not comptime-known. |
| 34807 | /// Asserts that `slice_val` is a slice or a pointer to an array. |
| 34808 | fn maybeDerefSliceAsArray( |
| 34809 | sema: *Sema, |
| 34810 | block: *Block, |
| 34811 | src: LazySrcLoc, |
| 34812 | slice_val: Value, |
| 34813 | ) CompileError!?Value { |
| 34814 | const pt = sema.pt; |
| 34815 | const zcu = pt.zcu; |
| 34816 | const slice_ty = slice_val.typeOf(zcu); |
| 34817 | assert(slice_ty.zigTypeTag(zcu) == .pointer); |
| 34818 | switch (slice_ty.ptrInfo(zcu).flags.size) { |
| 34819 | .slice => {}, |
| 34820 | .one => return sema.pointerDeref(block, src, slice_val, slice_ty), |
| 34821 | .many, .c => unreachable, |
| 34822 | } |
| 34823 | const slice = switch (zcu.intern_pool.indexToKey(slice_val.toIntern())) { |
| 34824 | .undef => return sema.failWithUseOfUndef(block, src, null), |
| 34825 | .slice => |slice| slice, |
| 34826 | else => unreachable, |
| 34827 | }; |
| 34828 | if (slice.len == .undef_usize) return sema.failWithUndefSliceLen(block, src); |
| 34829 | const casted_ptr = try pt.sliceToArrayPtr(slice); |
| 34830 | return sema.pointerDeref(block, src, casted_ptr, casted_ptr.typeOf(zcu)); |
| 34831 | } |
| 34832 | |
| 34833 | fn analyzeUnreachable(sema: *Sema, block: *Block, src: LazySrcLoc, safety_check: bool) !void { |
| 34834 | if (safety_check and block.wantSafety()) { |
| 34835 | // We only apply the first hint in a branch. |
| 34836 | // This allows user-provided hints to override implicit cold hints. |
| 34837 | if (sema.branch_hint == null) { |
| 34838 | sema.branch_hint = .cold; |
| 34839 | } |
| 34840 | |
| 34841 | try sema.safetyPanic(block, src, .reached_unreachable); |
| 34842 | } else { |
| 34843 | _ = try block.addNoOp(.unreach); |
| 34844 | } |
| 34845 | } |
| 34846 | |
| 34847 | /// This should be called exactly once, at the end of a `Sema`'s lifetime. |
| 34848 | /// It takes the exports stored in `sema.export` and flushes them to the `Zcu` |
| 34849 | /// to be processed by the linker after the update. |
| 34850 | pub fn flushExports(sema: *Sema) !void { |
| 34851 | if (sema.exports.items.len == 0) return; |
| 34852 | |
| 34853 | const zcu = sema.pt.zcu; |
| 34854 | const gpa = zcu.gpa; |
| 34855 | |
| 34856 | assert(!zcu.single_exports.contains(sema.owner)); |
| 34857 | assert(!zcu.multi_exports.contains(sema.owner)); |
| 34858 | |
| 34859 | if (sema.exports.items.len == 1) { |
| 34860 | try zcu.single_exports.ensureUnusedCapacity(gpa, 1); |
| 34861 | const export_idx: Zcu.Export.Index = zcu.free_exports.pop() orelse idx: { |
| 34862 | _ = try zcu.all_exports.addOne(gpa); |
| 34863 | break :idx @fromBackingInt(@intCast(zcu.all_exports.items.len - 1)); |
| 34864 | }; |
| 34865 | export_idx.ptr(zcu).* = sema.exports.items[0]; |
| 34866 | zcu.single_exports.putAssumeCapacityNoClobber(sema.owner, export_idx); |
| 34867 | } else { |
| 34868 | try zcu.multi_exports.ensureUnusedCapacity(gpa, 1); |
| 34869 | const exports_base = zcu.all_exports.items.len; |
| 34870 | try zcu.all_exports.appendSlice(gpa, sema.exports.items); |
| 34871 | zcu.multi_exports.putAssumeCapacityNoClobber(sema.owner, .{ |
| 34872 | .index = @intCast(exports_base), |
| 34873 | .len = @intCast(sema.exports.items.len), |
| 34874 | }); |
| 34875 | } |
| 34876 | } |
| 34877 | |
| 34878 | pub const castMemory = @import("Sema/reinterpret.zig").castMemory; |
| 34879 | pub const spliceMemory = @import("Sema/reinterpret.zig").spliceMemory; |
| 34880 | |
| 34881 | const loadComptimePtr = @import("Sema/comptime_ptr_access.zig").loadComptimePtr; |
| 34882 | const ComptimeLoadResult = @import("Sema/comptime_ptr_access.zig").ComptimeLoadResult; |
| 34883 | const storeComptimePtr = @import("Sema/comptime_ptr_access.zig").storeComptimePtr; |
| 34884 | const ComptimeStoreResult = @import("Sema/comptime_ptr_access.zig").ComptimeStoreResult; |
| 34885 | |
| 34886 | pub const type_resolution = @import("Sema/type_resolution.zig"); |
| 34887 | pub const ensureLayoutResolved = type_resolution.ensureLayoutResolved; |
| 34888 | pub const ensureStructDefaultsResolved = type_resolution.ensureStructDefaultsResolved; |
| 34889 | |
| 34890 | pub fn getStdLangType(sema: *Sema, src: LazySrcLoc, decl: Zcu.StdLangDecl) SemaError!Type { |
| 34891 | assert(decl.kind() == .type); |
| 34892 | try sema.ensureMemoizedStateResolved(src, decl.stage()); |
| 34893 | return .fromInterned(sema.pt.zcu.std_lang_decl_values.get(decl)); |
| 34894 | } |
| 34895 | pub fn getStdLangValue(sema: *Sema, src: LazySrcLoc, decl: Zcu.StdLangDecl) SemaError!InternPool.Index { |
| 34896 | assert(decl.kind() != .type); |
| 34897 | try sema.ensureMemoizedStateResolved(src, decl.stage()); |
| 34898 | return sema.pt.zcu.std_lang_decl_values.get(decl); |
| 34899 | } |
| 34900 | |
| 34901 | pub const NavPtrModifiers = struct { |
| 34902 | @"align": Alignment, |
| 34903 | @"linksection": InternPool.OptionalNullTerminatedString, |
| 34904 | @"addrspace": std.lang.AddressSpace, |
| 34905 | }; |
| 34906 | |
| 34907 | pub fn resolveNavPtrModifiers( |
| 34908 | sema: *Sema, |
| 34909 | block: *Block, |
| 34910 | zir_decl: Zir.Inst.Declaration.Unwrapped, |
| 34911 | decl_inst: Zir.Inst.Index, |
| 34912 | nav_ty: Type, |
| 34913 | ) CompileError!NavPtrModifiers { |
| 34914 | const pt = sema.pt; |
| 34915 | const zcu = pt.zcu; |
| 34916 | const comp = zcu.comp; |
| 34917 | const gpa = comp.gpa; |
| 34918 | const io = comp.io; |
| 34919 | const ip = &zcu.intern_pool; |
| 34920 | |
| 34921 | const align_src = block.src(.{ .node_offset_var_decl_align = .zero }); |
| 34922 | const section_src = block.src(.{ .node_offset_var_decl_section = .zero }); |
| 34923 | const addrspace_src = block.src(.{ .node_offset_var_decl_addrspace = .zero }); |
| 34924 | |
| 34925 | const @"align": InternPool.Alignment = a: { |
| 34926 | const align_body = zir_decl.align_body orelse break :a .none; |
| 34927 | const align_ref = try sema.resolveInlineBody(block, align_body, decl_inst); |
| 34928 | break :a try sema.analyzeAsAlign(block, align_src, align_ref); |
| 34929 | }; |
| 34930 | |
| 34931 | const @"linksection": InternPool.OptionalNullTerminatedString = ls: { |
| 34932 | const linksection_body = zir_decl.linksection_body orelse break :ls .none; |
| 34933 | const linksection_ref = try sema.resolveInlineBody(block, linksection_body, decl_inst); |
| 34934 | const bytes = try sema.toConstString(block, section_src, linksection_ref, .{ .simple = .@"linksection" }); |
| 34935 | if (std.mem.findScalar(u8, bytes, 0) != null) { |
| 34936 | return sema.fail(block, section_src, "linksection cannot contain null bytes", .{}); |
| 34937 | } else if (bytes.len == 0) { |
| 34938 | return sema.fail(block, section_src, "linksection cannot be empty", .{}); |
| 34939 | } |
| 34940 | break :ls try ip.getOrPutStringOpt(gpa, io, pt.tid, bytes, .no_embedded_nulls); |
| 34941 | }; |
| 34942 | |
| 34943 | const @"addrspace": std.lang.AddressSpace = as: { |
| 34944 | const addrspace_ctx: std.Target.AddressSpaceContext = switch (zir_decl.kind) { |
| 34945 | .@"var" => .variable, |
| 34946 | else => switch (nav_ty.zigTypeTag(zcu)) { |
| 34947 | .@"fn" => .function, |
| 34948 | else => .constant, |
| 34949 | }, |
| 34950 | }; |
| 34951 | const target = zcu.getTarget(); |
| 34952 | const addrspace_body = zir_decl.addrspace_body orelse { |
| 34953 | if (zir_decl.linkage == .@"extern" and |
| 34954 | target.cpu.arch.isSpirV() and |
| 34955 | nav_ty.zigTypeTag(zcu) != .@"fn") |
| 34956 | { |
| 34957 | return sema.fail( |
| 34958 | block, |
| 34959 | block.src(.{ .node_offset_var_decl_ty = .zero }), |
| 34960 | "SPIR-V extern variables require an explicit address space", |
| 34961 | .{}, |
| 34962 | ); |
| 34963 | } |
| 34964 | break :as switch (addrspace_ctx) { |
| 34965 | .function => target_util.defaultAddressSpace(target, .function), |
| 34966 | .variable => target_util.defaultAddressSpace(target, .global_mutable), |
| 34967 | .constant => target_util.defaultAddressSpace(target, .global_constant), |
| 34968 | else => unreachable, |
| 34969 | }; |
| 34970 | }; |
| 34971 | const addrspace_ref = try sema.resolveInlineBody(block, addrspace_body, decl_inst); |
| 34972 | break :as try sema.analyzeAsAddressSpace(block, addrspace_src, addrspace_ref, addrspace_ctx); |
| 34973 | }; |
| 34974 | |
| 34975 | return .{ |
| 34976 | .@"align" = @"align", |
| 34977 | .@"linksection" = @"linksection", |
| 34978 | .@"addrspace" = @"addrspace", |
| 34979 | }; |
| 34980 | } |
| 34981 | |
| 34982 | pub fn analyzeMemoizedState(sema: *Sema, stage: InternPool.MemoizedStateStage) CompileError!bool { |
| 34983 | const pt = sema.pt; |
| 34984 | const zcu = pt.zcu; |
| 34985 | const comp = zcu.comp; |
| 34986 | const gpa = comp.gpa; |
| 34987 | const io = comp.io; |
| 34988 | const ip = &zcu.intern_pool; |
| 34989 | |
| 34990 | // This `Block` acts kind of like it's evaluating a `comptime` declaration in the root source |
| 34991 | // file of the standard library. In particular, its namespace is the root std namespace. |
| 34992 | var block: Block = block: { |
| 34993 | // Get the main struct type of the root source file of `std`. No need for a reference entry |
| 34994 | // because `std` is always an analysis root. |
| 34995 | const std_file_index = zcu.module_roots.get(zcu.std_mod).?.unwrap().?; |
| 34996 | try sema.declareDependency(.{ .source_file = std_file_index }); |
| 34997 | try pt.ensureFilePopulated(std_file_index); |
| 34998 | const std_type: Type = .fromInterned(zcu.fileRootType(std_file_index)); |
| 34999 | break :block .{ |
| 35000 | .parent = null, |
| 35001 | .sema = sema, |
| 35002 | .namespace = std_type.getNamespaceIndex(zcu), |
| 35003 | .instructions = .empty, |
| 35004 | .inlining = null, |
| 35005 | .comptime_reason = null, |
| 35006 | .src_base_inst = std_type.typeDeclInst(zcu).?, |
| 35007 | .type_name_ctx = .empty, |
| 35008 | .type_fqn_ctx = .empty, |
| 35009 | }; |
| 35010 | }; |
| 35011 | defer block.instructions.deinit(gpa); |
| 35012 | |
| 35013 | const std_lang_ty: Type = ty: { |
| 35014 | const std_src = block.nodeOffset(.zero); |
| 35015 | const decl_name = try ip.getOrPutString(gpa, io, pt.tid, "lang", .no_embedded_nulls); |
| 35016 | const nav = try sema.namespaceLookup(&block, std_src, block.namespace, decl_name) orelse { |
| 35017 | return sema.fail(&block, std_src, "'std' missing 'lang'", .{}); |
| 35018 | }; |
| 35019 | const uncoerced_val = try sema.analyzeNavVal(&block, std_src, nav); |
| 35020 | const decl_src: LazySrcLoc = .{ |
| 35021 | .base_node_inst = ip.getNav(nav).srcInst(ip), |
| 35022 | .offset = .nodeOffset(.zero), |
| 35023 | }; |
| 35024 | break :ty try sema.analyzeAsType(&block, decl_src, .std_lang_decl, uncoerced_val); |
| 35025 | }; |
| 35026 | |
| 35027 | var any_changed = false; |
| 35028 | |
| 35029 | inline for (comptime std.enums.values(Zcu.StdLangDecl)) |std_lang_decl| { |
| 35030 | if (stage == comptime std_lang_decl.stage()) { |
| 35031 | const parent_ns_ty: Type, const parent_name: []const u8, const name: []const u8 = switch (comptime std_lang_decl.access()) { |
| 35032 | .direct => |name| .{ std_lang_ty, "std.lang", name }, |
| 35033 | .nested => |nested| access: { |
| 35034 | const parent_decl, const name = nested; |
| 35035 | const parent_ty: Type = .fromInterned(zcu.std_lang_decl_values.get(parent_decl)); |
| 35036 | break :access .{ parent_ty, "std.lang." ++ @tagName(parent_decl), name }; |
| 35037 | }, |
| 35038 | }; |
| 35039 | |
| 35040 | const parent_ns = parent_ns_ty.getNamespace(zcu).unwrap() orelse { |
| 35041 | return sema.fail(&block, block.nodeOffset(.zero), "'{s}' is not a container type", .{parent_name}); |
| 35042 | }; |
| 35043 | const parent_ty_src = parent_ns_ty.srcLoc(zcu); |
| 35044 | const name_nts = try ip.getOrPutString(gpa, io, pt.tid, name, .no_embedded_nulls); |
| 35045 | const nav = try sema.namespaceLookup(&block, parent_ty_src, parent_ns, name_nts) orelse { |
| 35046 | return sema.fail(&block, parent_ty_src, "'{s}' missing '{s}'", .{ parent_name, name }); |
| 35047 | }; |
| 35048 | const uncoerced_val = try sema.analyzeNavVal(&block, parent_ty_src, nav); |
| 35049 | |
| 35050 | const decl_src: LazySrcLoc = .{ |
| 35051 | .base_node_inst = ip.getNav(nav).srcInst(ip), |
| 35052 | .offset = .nodeOffset(.zero), |
| 35053 | }; |
| 35054 | |
| 35055 | const val: Value = switch (std_lang_decl.kind()) { |
| 35056 | .type => val: { |
| 35057 | const ty = try sema.analyzeAsType(&block, decl_src, .std_lang_decl, uncoerced_val); |
| 35058 | try sema.ensureLayoutResolved(ty, decl_src, .std_lang_type); |
| 35059 | break :val ty.toValue(); |
| 35060 | }, |
| 35061 | .func => val: { |
| 35062 | const func_ty = try sema.getExpectedBuiltinFnType(std_lang_decl); |
| 35063 | const coerced = try sema.coerce(&block, func_ty, uncoerced_val, decl_src); |
| 35064 | break :val try sema.resolveConstDefinedValue(&block, decl_src, coerced, .{ .simple = .std_lang_decl }); |
| 35065 | }, |
| 35066 | .string => val: { |
| 35067 | const coerced = try sema.coerce(&block, .slice_const_u8, uncoerced_val, decl_src); |
| 35068 | break :val try sema.resolveConstDefinedValue(&block, decl_src, coerced, .{ .simple = .std_lang_decl }); |
| 35069 | }, |
| 35070 | }; |
| 35071 | |
| 35072 | if (zcu.std_lang_decl_values.get(std_lang_decl) != val.toIntern()) { |
| 35073 | zcu.std_lang_decl_values.set(std_lang_decl, val.toIntern()); |
| 35074 | any_changed = true; |
| 35075 | } |
| 35076 | } |
| 35077 | } |
| 35078 | |
| 35079 | return any_changed; |
| 35080 | } |
| 35081 | |
| 35082 | /// Given that `decl.kind() == .func`, get the type expected of the function. |
| 35083 | fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.StdLangDecl) CompileError!Type { |
| 35084 | const pt = sema.pt; |
| 35085 | return switch (decl) { |
| 35086 | // `noinline fn () void` |
| 35087 | .returnError => try pt.funcType(.{ |
| 35088 | .param_types = &.{}, |
| 35089 | .return_type = .void_type, |
| 35090 | .is_noinline = true, |
| 35091 | }), |
| 35092 | |
| 35093 | // `fn ([]const u8, ?usize) noreturn` |
| 35094 | .@"panic.call" => try pt.funcType(.{ |
| 35095 | .param_types = &.{ |
| 35096 | .slice_const_u8_type, |
| 35097 | (try pt.optionalType(.usize_type)).toIntern(), |
| 35098 | }, |
| 35099 | .return_type = .noreturn_type, |
| 35100 | }), |
| 35101 | |
| 35102 | // `fn (anytype, anytype) noreturn` |
| 35103 | .@"panic.sentinelMismatch", |
| 35104 | .@"panic.inactiveUnionField", |
| 35105 | => try pt.funcType(.{ |
| 35106 | .param_types = &.{ .generic_poison_type, .generic_poison_type }, |
| 35107 | .return_type = .noreturn_type, |
| 35108 | }), |
| 35109 | |
| 35110 | // `fn (anyerror) noreturn` |
| 35111 | .@"panic.unwrapError", |
| 35112 | .@"panic.unexpectedErrorCode", |
| 35113 | => try pt.funcType(.{ |
| 35114 | .param_types = &.{.anyerror_type}, |
| 35115 | .return_type = .noreturn_type, |
| 35116 | }), |
| 35117 | |
| 35118 | // `fn (usize) noreturn` |
| 35119 | .@"panic.sliceCastLenRemainder" => try pt.funcType(.{ |
| 35120 | .param_types = &.{.usize_type}, |
| 35121 | .return_type = .noreturn_type, |
| 35122 | }), |
| 35123 | |
| 35124 | // `fn (usize, usize) noreturn` |
| 35125 | .@"panic.outOfBounds", |
| 35126 | .@"panic.startGreaterThanEnd", |
| 35127 | => try pt.funcType(.{ |
| 35128 | .param_types = &.{ .usize_type, .usize_type }, |
| 35129 | .return_type = .noreturn_type, |
| 35130 | }), |
| 35131 | |
| 35132 | // `fn () noreturn` |
| 35133 | .@"panic.reachedUnreachable", |
| 35134 | .@"panic.unwrapNull", |
| 35135 | .@"panic.castToNull", |
| 35136 | .@"panic.incorrectAlignment", |
| 35137 | .@"panic.invalidErrorCode", |
| 35138 | .@"panic.integerOutOfBounds", |
| 35139 | .@"panic.integerOverflow", |
| 35140 | .@"panic.shlOverflow", |
| 35141 | .@"panic.shrOverflow", |
| 35142 | .@"panic.divideByZero", |
| 35143 | .@"panic.exactDivisionRemainder", |
| 35144 | .@"panic.integerPartOutOfBounds", |
| 35145 | .@"panic.corruptSwitch", |
| 35146 | .@"panic.shiftRhsTooBig", |
| 35147 | .@"panic.invalidEnumValue", |
| 35148 | .@"panic.forLenMismatch", |
| 35149 | .@"panic.copyLenMismatch", |
| 35150 | .@"panic.memcpyAlias", |
| 35151 | .@"panic.noreturnReturned", |
| 35152 | .@"panic.loadUninstantiableType", |
| 35153 | => try pt.funcType(.{ |
| 35154 | .param_types = &.{}, |
| 35155 | .return_type = .noreturn_type, |
| 35156 | }), |
| 35157 | |
| 35158 | .StackTrace, |
| 35159 | .CallingConvention, |
| 35160 | .SourceLocation, |
| 35161 | .Signedness, |
| 35162 | .AddressSpace, |
| 35163 | .VaList, |
| 35164 | .CallModifier, |
| 35165 | .AtomicOrder, |
| 35166 | .AtomicRmwOp, |
| 35167 | .ReduceOp, |
| 35168 | .FloatMode, |
| 35169 | .PrefetchOptions, |
| 35170 | .ExportOptions, |
| 35171 | .ExternOptions, |
| 35172 | .BranchHint, |
| 35173 | .assembly, |
| 35174 | .@"assembly.Clobbers", |
| 35175 | .Type, |
| 35176 | .@"Type.Fn", |
| 35177 | .@"Type.Fn.ParamAttributes", |
| 35178 | .@"Type.Fn.Attributes", |
| 35179 | .@"Type.Int", |
| 35180 | .@"Type.Float", |
| 35181 | .@"Type.Pointer", |
| 35182 | .@"Type.Pointer.Size", |
| 35183 | .@"Type.Pointer.Attributes", |
| 35184 | .@"Type.Array", |
| 35185 | .@"Type.Vector", |
| 35186 | .@"Type.Optional", |
| 35187 | .@"Type.ErrorUnion", |
| 35188 | .@"Type.ErrorSet", |
| 35189 | .@"Type.Enum", |
| 35190 | .@"Type.Enum.Mode", |
| 35191 | .@"Type.Union", |
| 35192 | .@"Type.Union.FieldAttributes", |
| 35193 | .@"Type.Struct", |
| 35194 | .@"Type.Struct.FieldAttributes", |
| 35195 | .@"Type.ContainerLayout", |
| 35196 | .@"Type.Opaque", |
| 35197 | .@"Type.Spirv", |
| 35198 | .@"Type.Spirv.Image", |
| 35199 | .@"Type.Spirv.Image.Usage", |
| 35200 | .@"Type.Spirv.Image.Format", |
| 35201 | .@"Type.Spirv.Image.Dimensionality", |
| 35202 | .@"Type.Spirv.Image.Depth", |
| 35203 | .@"Type.Spirv.Image.Access", |
| 35204 | .panic, |
| 35205 | => unreachable, // not a function (`decl.kind() != .func`) |
| 35206 | }; |
| 35207 | } |
| 35208 | |
| 35209 | pub fn setTypeName( |
| 35210 | sema: *Sema, |
| 35211 | block: *Block, |
| 35212 | wip: *const InternPool.WipContainerType, |
| 35213 | name_strategy: Zir.Inst.NameStrategy, |
| 35214 | anon_prefix: []const u8, |
| 35215 | inst: Zir.Inst.Index, |
| 35216 | ) CompileError!void { |
| 35217 | const pt = sema.pt; |
| 35218 | const zcu = pt.zcu; |
| 35219 | const comp = zcu.comp; |
| 35220 | const gpa = comp.gpa; |
| 35221 | const io = comp.io; |
| 35222 | const ip = &zcu.intern_pool; |
| 35223 | |
| 35224 | strat: switch (name_strategy) { |
| 35225 | .anon => { |
| 35226 | // It would be neat to have "struct:line:column" but this name has |
| 35227 | // to survive incremental updates, where it may have been shifted down |
| 35228 | // or up to a different line, but unchanged, and thus not unnecessarily |
| 35229 | // semantically analyzed. |
| 35230 | // TODO: that would be possible, by detecting line number changes and renaming |
| 35231 | // types appropriately. However, `@typeName` becomes a problem then. If we remove |
| 35232 | // that builtin from the language, we can consider this. |
| 35233 | wip.setName(ip, try ip.getOrPutStringFmt( |
| 35234 | gpa, |
| 35235 | io, |
| 35236 | pt.tid, |
| 35237 | "{f}__{s}_{d}", |
| 35238 | .{ block.type_name_ctx.fmt(ip), anon_prefix, zcu.anon_name_counter }, |
| 35239 | .no_embedded_nulls, |
| 35240 | ), try ip.getOrPutStringFmt( |
| 35241 | gpa, |
| 35242 | io, |
| 35243 | pt.tid, |
| 35244 | "{f}__{s}_{d}", |
| 35245 | .{ block.type_fqn_ctx.fmt(ip), anon_prefix, zcu.anon_name_counter }, |
| 35246 | .no_embedded_nulls, |
| 35247 | ), .none); |
| 35248 | zcu.anon_name_counter += 1; |
| 35249 | }, |
| 35250 | .parent => wip.setName(ip, block.type_name_ctx, block.type_fqn_ctx, sema.owner.unwrap().nav_val.toOptional()), |
| 35251 | .func => { |
| 35252 | const fn_info = sema.code.getFnInfo(ip.funcZirBodyInst(sema.func_index).resolve(ip) orelse { |
| 35253 | return sema.failTransitive(.{ .lost_tracking = ip.funcZirBodyInst(sema.func_index) }); |
| 35254 | }); |
| 35255 | const zir_tags = sema.code.instructions.items(.tag); |
| 35256 | |
| 35257 | var aw: std.Io.Writer.Allocating = .init(gpa); |
| 35258 | defer aw.deinit(); |
| 35259 | const w = &aw.writer; |
| 35260 | w.writeByte('(') catch return error.OutOfMemory; |
| 35261 | |
| 35262 | var arg_i: usize = 0; |
| 35263 | for (fn_info.param_body) |zir_inst| switch (zir_tags[@backingInt(zir_inst)]) { |
| 35264 | .param, .param_comptime, .param_anytype, .param_anytype_comptime => { |
| 35265 | const arg = sema.inst_map.get(zir_inst).?; |
| 35266 | // If this is being called in a generic function then analyzeCall will |
| 35267 | // have already resolved the args and this will work. |
| 35268 | // If not then this is a struct type being returned from a non-generic |
| 35269 | // function and the name doesn't matter since it will later |
| 35270 | // result in a compile error. |
| 35271 | const arg_val = sema.resolveValue(arg) orelse { |
| 35272 | continue :strat .anon; |
| 35273 | }; |
| 35274 | |
| 35275 | if (arg_i != 0) w.writeByte(',') catch return error.OutOfMemory; |
| 35276 | |
| 35277 | // Limiting the depth here helps avoid type names getting too long, which |
| 35278 | // in turn helps to avoid unreasonably long symbol names for namespaced |
| 35279 | // symbols. Such names should ideally be human-readable, and additionally, |
| 35280 | // some tooling may not support very long symbol names. |
| 35281 | w.print("{f}", .{Value.fmtValueSemaFull(.{ |
| 35282 | .val = arg_val, |
| 35283 | .pt = pt, |
| 35284 | .opt_sema = sema, |
| 35285 | .depth = 1, |
| 35286 | })}) catch return error.OutOfMemory; |
| 35287 | |
| 35288 | arg_i += 1; |
| 35289 | continue; |
| 35290 | }, |
| 35291 | else => continue, |
| 35292 | }; |
| 35293 | |
| 35294 | w.writeByte(')') catch return error.OutOfMemory; |
| 35295 | wip.setName(ip, try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}{s}", .{ |
| 35296 | block.type_name_ctx.fmt(ip), |
| 35297 | aw.written(), |
| 35298 | }, .no_embedded_nulls), try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}{s}", .{ |
| 35299 | block.type_fqn_ctx.fmt(ip), |
| 35300 | aw.written(), |
| 35301 | }, .no_embedded_nulls), .none); |
| 35302 | }, |
| 35303 | .dbg_var => { |
| 35304 | // TODO: this logic is questionable. We ideally should be traversing the `Block` rather than relying on the order of AstGen instructions. |
| 35305 | const ref = inst.toRef(); |
| 35306 | const zir_tags = sema.code.instructions.items(.tag); |
| 35307 | const zir_data = sema.code.instructions.items(.data); |
| 35308 | const var_name = for (@backingInt(inst)..zir_tags.len) |i| switch (zir_tags[i]) { |
| 35309 | .dbg_var_ptr, .dbg_var_val => if (zir_data[i].str_op.operand == ref) { |
| 35310 | break zir_data[i].str_op.getStr(sema.code); |
| 35311 | }, |
| 35312 | else => {}, |
| 35313 | } else { |
| 35314 | continue :strat .anon; |
| 35315 | }; |
| 35316 | wip.setName(ip, try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}.{s}", .{ |
| 35317 | // this "{f}." should be elided, but there's currently no way to get the parent function |
| 35318 | block.type_name_ctx.fmt(ip), var_name, |
| 35319 | }, .no_embedded_nulls), try ip.getOrPutStringFmt(gpa, io, pt.tid, "{f}.{s}", .{ |
| 35320 | block.type_fqn_ctx.fmt(ip), var_name, |
| 35321 | }, .no_embedded_nulls), .none); |
| 35322 | }, |
| 35323 | } |
| 35324 | } |
| 35325 | |
| 35326 | fn zirStructDecl( |
| 35327 | sema: *Sema, |
| 35328 | block: *Block, |
| 35329 | inst: Zir.Inst.Index, |
| 35330 | ) CompileError!Air.Inst.Ref { |
| 35331 | const pt = sema.pt; |
| 35332 | const zcu = pt.zcu; |
| 35333 | const comp = zcu.comp; |
| 35334 | const gpa = comp.gpa; |
| 35335 | const io = comp.io; |
| 35336 | const ip = &zcu.intern_pool; |
| 35337 | |
| 35338 | const tracked_inst = try block.trackZir(inst); |
| 35339 | |
| 35340 | const src: LazySrcLoc = .{ |
| 35341 | .base_node_inst = tracked_inst, |
| 35342 | .offset = .nodeOffset(.zero), |
| 35343 | }; |
| 35344 | |
| 35345 | const struct_decl = sema.code.getStructDecl(inst); |
| 35346 | |
| 35347 | const captures = try sema.getCaptures(block, src, struct_decl.captures, struct_decl.capture_names); |
| 35348 | |
| 35349 | const ty: Type = switch (try ip.getDeclaredStructType(gpa, io, pt.tid, .{ |
| 35350 | .zir_index = tracked_inst, |
| 35351 | .captures = captures, |
| 35352 | .fields_len = @intCast(struct_decl.field_names.len), |
| 35353 | .layout = struct_decl.layout, |
| 35354 | .any_comptime_fields = struct_decl.field_comptime_bits != null, |
| 35355 | .any_field_defaults = struct_decl.field_default_body_lens != null, |
| 35356 | .any_field_aligns = struct_decl.field_align_body_lens != null, |
| 35357 | .packed_backing_mode = if (struct_decl.backing_int_type_body != null) .explicit else .auto, |
| 35358 | })) { |
| 35359 | .existing => |ty| .fromInterned(ty), |
| 35360 | .wip => |wip| ty: { |
| 35361 | errdefer wip.cancel(ip, pt.tid); |
| 35362 | try sema.setTypeName(block, &wip, struct_decl.name_strategy, "struct", inst); |
| 35363 | const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ |
| 35364 | .parent = block.namespace.toOptional(), |
| 35365 | .owner_type = wip.index, |
| 35366 | .file_scope = block.getFileScopeIndex(zcu), |
| 35367 | .generation = zcu.generation, |
| 35368 | }); |
| 35369 | errdefer pt.destroyNamespace(new_namespace_index); |
| 35370 | try pt.scanNamespace(new_namespace_index, struct_decl.decls); |
| 35371 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); |
| 35372 | break :ty .fromInterned(wip.finish(ip, new_namespace_index)); |
| 35373 | }, |
| 35374 | }; |
| 35375 | |
| 35376 | try sema.addTypeReferenceEntry(src, ty); |
| 35377 | pt.ensureNamespaceUpToDate(ty.getNamespaceIndex(zcu)) catch |err| switch (err) { |
| 35378 | error.LostZirContainerDecl => unreachable, // we literally just tracked it |
| 35379 | else => |e| return e, |
| 35380 | }; |
| 35381 | |
| 35382 | return .fromType(ty); |
| 35383 | } |
| 35384 | fn zirUnionDecl( |
| 35385 | sema: *Sema, |
| 35386 | block: *Block, |
| 35387 | inst: Zir.Inst.Index, |
| 35388 | ) CompileError!Air.Inst.Ref { |
| 35389 | const pt = sema.pt; |
| 35390 | const zcu = pt.zcu; |
| 35391 | const comp = zcu.comp; |
| 35392 | const gpa = comp.gpa; |
| 35393 | const io = comp.io; |
| 35394 | const ip = &zcu.intern_pool; |
| 35395 | |
| 35396 | const tracked_inst = try block.trackZir(inst); |
| 35397 | |
| 35398 | const src: LazySrcLoc = .{ |
| 35399 | .base_node_inst = tracked_inst, |
| 35400 | .offset = .nodeOffset(.zero), |
| 35401 | }; |
| 35402 | |
| 35403 | const union_decl = sema.code.getUnionDecl(inst); |
| 35404 | |
| 35405 | const captures = try sema.getCaptures(block, src, union_decl.captures, union_decl.capture_names); |
| 35406 | |
| 35407 | const ty: Type = switch (try ip.getDeclaredUnionType(gpa, io, pt.tid, .{ |
| 35408 | .zir_index = tracked_inst, |
| 35409 | .captures = captures, |
| 35410 | .fields_len = @intCast(union_decl.field_names.len), |
| 35411 | .layout = union_decl.kind.layout(), |
| 35412 | .any_field_aligns = union_decl.field_align_body_lens != null, |
| 35413 | .tag_usage = switch (union_decl.kind) { |
| 35414 | .auto => if (block.wantSafeTypes()) .safety else .none, |
| 35415 | |
| 35416 | .tagged_explicit, |
| 35417 | .tagged_enum, |
| 35418 | .tagged_enum_explicit, |
| 35419 | => .tagged, |
| 35420 | |
| 35421 | .@"extern", |
| 35422 | .@"packed", |
| 35423 | .packed_explicit, |
| 35424 | => .none, |
| 35425 | }, |
| 35426 | .enum_tag_mode = switch (union_decl.kind) { |
| 35427 | .tagged_explicit => .explicit, |
| 35428 | else => .auto, |
| 35429 | }, |
| 35430 | .packed_backing_mode = switch (union_decl.kind) { |
| 35431 | .packed_explicit => .explicit, |
| 35432 | else => .auto, |
| 35433 | }, |
| 35434 | })) { |
| 35435 | .existing => |ty| .fromInterned(ty), |
| 35436 | .wip => |wip| ty: { |
| 35437 | errdefer wip.cancel(ip, pt.tid); |
| 35438 | try sema.setTypeName(block, &wip, union_decl.name_strategy, "union", inst); |
| 35439 | const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ |
| 35440 | .parent = block.namespace.toOptional(), |
| 35441 | .owner_type = wip.index, |
| 35442 | .file_scope = block.getFileScopeIndex(zcu), |
| 35443 | .generation = zcu.generation, |
| 35444 | }); |
| 35445 | errdefer pt.destroyNamespace(new_namespace_index); |
| 35446 | try pt.scanNamespace(new_namespace_index, union_decl.decls); |
| 35447 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); |
| 35448 | break :ty .fromInterned(wip.finish(ip, new_namespace_index)); |
| 35449 | }, |
| 35450 | }; |
| 35451 | |
| 35452 | try sema.addTypeReferenceEntry(src, ty); |
| 35453 | pt.ensureNamespaceUpToDate(ty.getNamespaceIndex(zcu)) catch |err| switch (err) { |
| 35454 | error.LostZirContainerDecl => unreachable, // we literally just tracked it |
| 35455 | else => |e| return e, |
| 35456 | }; |
| 35457 | |
| 35458 | return .fromType(ty); |
| 35459 | } |
| 35460 | fn zirEnumDecl( |
| 35461 | sema: *Sema, |
| 35462 | block: *Block, |
| 35463 | inst: Zir.Inst.Index, |
| 35464 | ) CompileError!Air.Inst.Ref { |
| 35465 | const pt = sema.pt; |
| 35466 | const zcu = pt.zcu; |
| 35467 | const comp = zcu.comp; |
| 35468 | const gpa = comp.gpa; |
| 35469 | const io = comp.io; |
| 35470 | const ip = &zcu.intern_pool; |
| 35471 | |
| 35472 | const tracked_inst = try block.trackZir(inst); |
| 35473 | |
| 35474 | const src: LazySrcLoc = .{ |
| 35475 | .base_node_inst = tracked_inst, |
| 35476 | .offset = .nodeOffset(.zero), |
| 35477 | }; |
| 35478 | |
| 35479 | const enum_decl = sema.code.getEnumDecl(inst); |
| 35480 | |
| 35481 | const captures = try sema.getCaptures(block, src, enum_decl.captures, enum_decl.capture_names); |
| 35482 | |
| 35483 | const ty: Type = switch (try ip.getDeclaredEnumType(gpa, io, pt.tid, .{ |
| 35484 | .zir_index = tracked_inst, |
| 35485 | .captures = captures, |
| 35486 | .fields_len = @intCast(enum_decl.field_names.len), |
| 35487 | .nonexhaustive = enum_decl.nonexhaustive, |
| 35488 | .int_tag_mode = if (enum_decl.tag_type_body != null) .explicit else .auto, |
| 35489 | })) { |
| 35490 | .existing => |ty| .fromInterned(ty), |
| 35491 | .wip => |wip| ty: { |
| 35492 | errdefer wip.cancel(ip, pt.tid); |
| 35493 | try sema.setTypeName(block, &wip, enum_decl.name_strategy, "enum", inst); |
| 35494 | const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ |
| 35495 | .parent = block.namespace.toOptional(), |
| 35496 | .owner_type = wip.index, |
| 35497 | .file_scope = block.getFileScopeIndex(zcu), |
| 35498 | .generation = zcu.generation, |
| 35499 | }); |
| 35500 | errdefer pt.destroyNamespace(new_namespace_index); |
| 35501 | try pt.scanNamespace(new_namespace_index, enum_decl.decls); |
| 35502 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); |
| 35503 | break :ty .fromInterned(wip.finish(ip, new_namespace_index)); |
| 35504 | }, |
| 35505 | }; |
| 35506 | |
| 35507 | try sema.addTypeReferenceEntry(src, ty); |
| 35508 | pt.ensureNamespaceUpToDate(ty.getNamespaceIndex(zcu)) catch |err| switch (err) { |
| 35509 | error.LostZirContainerDecl => unreachable, // we literally just tracked it |
| 35510 | else => |e| return e, |
| 35511 | }; |
| 35512 | |
| 35513 | return .fromType(ty); |
| 35514 | } |
| 35515 | fn zirOpaqueDecl( |
| 35516 | sema: *Sema, |
| 35517 | block: *Block, |
| 35518 | inst: Zir.Inst.Index, |
| 35519 | ) CompileError!Air.Inst.Ref { |
| 35520 | const pt = sema.pt; |
| 35521 | const zcu = pt.zcu; |
| 35522 | const comp = zcu.comp; |
| 35523 | const gpa = comp.gpa; |
| 35524 | const io = comp.io; |
| 35525 | const ip = &zcu.intern_pool; |
| 35526 | |
| 35527 | const tracked_inst = try block.trackZir(inst); |
| 35528 | |
| 35529 | const src: LazySrcLoc = .{ |
| 35530 | .base_node_inst = tracked_inst, |
| 35531 | .offset = .nodeOffset(.zero), |
| 35532 | }; |
| 35533 | |
| 35534 | const opaque_decl = sema.code.getOpaqueDecl(inst); |
| 35535 | |
| 35536 | const captures = try sema.getCaptures(block, src, opaque_decl.captures, opaque_decl.capture_names); |
| 35537 | |
| 35538 | const ty: Type = switch (try ip.getDeclaredOpaqueType(gpa, io, pt.tid, .{ |
| 35539 | .zir_index = tracked_inst, |
| 35540 | .captures = captures, |
| 35541 | })) { |
| 35542 | .existing => |ty| .fromInterned(ty), |
| 35543 | .wip => |wip| ty: { |
| 35544 | errdefer wip.cancel(ip, pt.tid); |
| 35545 | try sema.setTypeName(block, &wip, opaque_decl.name_strategy, "opaque", inst); |
| 35546 | const new_namespace_index: InternPool.NamespaceIndex = try pt.createNamespace(.{ |
| 35547 | .parent = block.namespace.toOptional(), |
| 35548 | .owner_type = wip.index, |
| 35549 | .file_scope = block.getFileScopeIndex(zcu), |
| 35550 | .generation = zcu.generation, |
| 35551 | }); |
| 35552 | errdefer pt.destroyNamespace(new_namespace_index); |
| 35553 | try pt.scanNamespace(new_namespace_index, opaque_decl.decls); |
| 35554 | if (zcu.comp.debugIncremental()) try zcu.incremental_debug_state.newType(zcu, wip.index); |
| 35555 | break :ty .fromInterned(wip.finish(ip, new_namespace_index)); |
| 35556 | }, |
| 35557 | }; |
| 35558 | |
| 35559 | try sema.addTypeReferenceEntry(src, ty); |
| 35560 | pt.ensureNamespaceUpToDate(ty.getNamespaceIndex(zcu)) catch |err| switch (err) { |
| 35561 | error.LostZirContainerDecl => unreachable, // we literally just tracked it |
| 35562 | else => |e| return e, |
| 35563 | }; |
| 35564 | |
| 35565 | return .fromType(ty); |
| 35566 | } |
| 35567 | |
| 35568 | /// Registers an error indicating a dependency loop: we have introduced a dependency on `want` (with |
| 35569 | /// reason `want_reason`) but have learnt that `want` is already in `zcu.analysis_in_progress`. |
| 35570 | pub fn failWithDependencyLoop( |
| 35571 | sema: *Sema, |
| 35572 | want: AnalUnit, |
| 35573 | want_reason: *const Zcu.DependencyReason, |
| 35574 | ) SemaError { |
| 35575 | const pt = sema.pt; |
| 35576 | const zcu = pt.zcu; |
| 35577 | const gpa = zcu.comp.gpa; |
| 35578 | |
| 35579 | const in_progress_len = zcu.analysis_in_progress.count(); |
| 35580 | var index = zcu.analysis_in_progress.getIndex(want).? + 1; |
| 35581 | |
| 35582 | try zcu.dependency_loops.ensureUnusedCapacity(gpa, 1); |
| 35583 | try zcu.dependency_loop_nodes.ensureUnusedCapacity(gpa, in_progress_len - index + 1); |
| 35584 | |
| 35585 | zcu.dependency_loops.putAssumeCapacityNoClobber(want, {}); |
| 35586 | |
| 35587 | while (index <= in_progress_len) : (index += 1) { |
| 35588 | const parent_unit = zcu.analysis_in_progress.keys()[index - 1]; |
| 35589 | const unit, const reason = if (index == in_progress_len) .{ |
| 35590 | want, |
| 35591 | want_reason, |
| 35592 | } else .{ |
| 35593 | zcu.analysis_in_progress.keys()[index], |
| 35594 | zcu.analysis_in_progress.values()[index], |
| 35595 | }; |
| 35596 | |
| 35597 | zcu.dependency_loop_nodes.putAssumeCapacityNoClobber(parent_unit, .{ |
| 35598 | .unit = unit, |
| 35599 | .reason = reason.?.*, |
| 35600 | }); |
| 35601 | } |
| 35602 | |
| 35603 | // A dependency loop error will be reported. Mark us all as transitive failures. |
| 35604 | return sema.failTransitive(.dependency_loop); |
| 35605 | } |
| 35606 | |
| 35607 | /// Marks the owner of `sema` as having failed semantic failed *without* an error message, and |
| 35608 | /// returns failure. This function is suitable to call when any one of the following is true: |
| 35609 | /// |
| 35610 | /// * `sema.owner` is guaranteed to be unreferenced on this update, for instance because it uses a |
| 35611 | /// dead `InternPool.TrackedInst`. |
| 35612 | /// |
| 35613 | /// * There is guaranteed to be a compile error if this unit is referenced. In practice, this means |
| 35614 | /// that either there is an error elsewhere in the pipeline (e.g. AstGen), or we depend on another |
| 35615 | /// `AnalUnit` which has itself failed. |
| 35616 | pub fn failTransitive(sema: *Sema, reason: Zcu.TransitiveFailureReason) SemaError { |
| 35617 | assert(sema.err == null); |
| 35618 | const zcu = sema.pt.zcu; |
| 35619 | const unit = sema.owner; |
| 35620 | |
| 35621 | log.debug("transitive failure analyzing '{f}' ({t})", .{ zcu.fmtAnalUnit(unit), reason }); |
| 35622 | |
| 35623 | assert(!zcu.failed_analysis.contains(unit)); |
| 35624 | try zcu.transitive_failed_analysis.putNoClobber( |
| 35625 | zcu.comp.gpa, |
| 35626 | unit, |
| 35627 | if (build_options.enable_debug_extensions) reason, |
| 35628 | ); |
| 35629 | |
| 35630 | return error.AlreadyReported; |
| 35631 | } |