| author | |
| committer | |
| log | 8509e7111d80a07e778aa2a57d58d2bea6945014 |
| tree | e7eedaf980ada713a6c3d2da3e6153751d03bed6 |
| parent | a132190cad80669306705b72276e9641401426fb |
* AstGen: always use `typeof` and never `typeof_elem` on the
`switch_cond`/`switch_cond_ref` instruction because both variants
return a value and not a pointer.
- Delete the `typeof_elem` ZIR instruction since it is no longer
needed.
* Sema: validateUnionInit now recognizes a comptime mutable value and
no longer emits a compile error saying "cannot evaluate constant
expression"
- Still to-do is detecting comptime union values in a function that
is not being executed at compile-time.
- This is still to-do for structs too.
* Sema: when emitting a call AIR instruction, call resolveTypeLayout on
all the parameter types as well as the return type.
* `Type.structFieldOffset` now works for unions in addition to structs.7 files changed, 115 insertions(+), 97 deletions(-)
src/AstGen.zig+2-4| ... | @@ -2109,7 +2109,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner | ... | @@ -2109,7 +2109,6 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner |
| 2109 | .negate, | 2109 | .negate, |
| 2110 | .negate_wrap, | 2110 | .negate_wrap, |
| 2111 | .typeof, | 2111 | .typeof, |
| 2112 | .typeof_elem, | ||
| 2113 | .xor, | 2112 | .xor, |
| 2114 | .optional_type, | 2113 | .optional_type, |
| 2115 | .optional_payload_safe, | 2114 | .optional_payload_safe, |
| ... | @@ -6028,8 +6027,7 @@ fn switchExpr( | ... | @@ -6028,8 +6027,7 @@ fn switchExpr( |
| 6028 | const cond_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_cond_ref else .switch_cond; | 6027 | const cond_tag: Zir.Inst.Tag = if (any_payload_is_ref) .switch_cond_ref else .switch_cond; |
| 6029 | const cond = try parent_gz.addUnNode(cond_tag, raw_operand, operand_node); | 6028 | const cond = try parent_gz.addUnNode(cond_tag, raw_operand, operand_node); |
| 6030 | // We need the type of the operand to use as the result location for all the prong items. | 6029 | // We need the type of the operand to use as the result location for all the prong items. |
| 6031 | const typeof_tag: Zir.Inst.Tag = if (any_payload_is_ref) .typeof_elem else .typeof; | 6030 | const cond_ty_inst = try parent_gz.addUnNode(.typeof, cond, operand_node); |
| 6032 | const cond_ty_inst = try parent_gz.addUnNode(typeof_tag, cond, operand_node); | ||
| 6033 | const item_rl: ResultLoc = .{ .ty = cond_ty_inst }; | 6031 | const item_rl: ResultLoc = .{ .ty = cond_ty_inst }; |
| 6034 | 6032 | ||
| 6035 | // These contain the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti. | 6033 | // These contain the data that goes into the `extra` array for the SwitchBlock/SwitchBlockMulti. |
| ... | @@ -6214,7 +6212,7 @@ fn switchExpr( | ... | @@ -6214,7 +6212,7 @@ fn switchExpr( |
| 6214 | .has_multi_cases = multi_cases_len != 0, | 6212 | .has_multi_cases = multi_cases_len != 0, |
| 6215 | .has_else = special_prong == .@"else", | 6213 | .has_else = special_prong == .@"else", |
| 6216 | .has_under = special_prong == .under, | 6214 | .has_under = special_prong == .under, |
| 6217 | .scalar_cases_len = @intCast(u28, scalar_cases_len), | 6215 | .scalar_cases_len = @intCast(Zir.Inst.SwitchBlock.Bits.ScalarCasesLen, scalar_cases_len), |
| 6218 | }, | 6216 | }, |
| 6219 | }); | 6217 | }); |
| 6220 | 6218 |
src/Sema.zig+22-22| ... | @@ -608,7 +608,6 @@ pub fn analyzeBody( | ... | @@ -608,7 +608,6 @@ pub fn analyzeBody( |
| 608 | .size_of => try sema.zirSizeOf(block, inst), | 608 | .size_of => try sema.zirSizeOf(block, inst), |
| 609 | .bit_size_of => try sema.zirBitSizeOf(block, inst), | 609 | .bit_size_of => try sema.zirBitSizeOf(block, inst), |
| 610 | .typeof => try sema.zirTypeof(block, inst), | 610 | .typeof => try sema.zirTypeof(block, inst), |
| 611 | .typeof_elem => try sema.zirTypeofElem(block, inst), | ||
| 612 | .log2_int_type => try sema.zirLog2IntType(block, inst), | 611 | .log2_int_type => try sema.zirLog2IntType(block, inst), |
| 613 | .typeof_log2_int_type => try sema.zirTypeofLog2IntType(block, inst), | 612 | .typeof_log2_int_type => try sema.zirTypeofLog2IntType(block, inst), |
| 614 | .xor => try sema.zirBitwise(block, inst, .xor), | 613 | .xor => try sema.zirBitwise(block, inst, .xor), |
| ... | @@ -2337,11 +2336,21 @@ fn validateUnionInit( | ... | @@ -2337,11 +2336,21 @@ fn validateUnionInit( |
| 2337 | return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); | 2336 | return sema.failWithBadUnionFieldAccess(block, union_obj, field_src, field_name); |
| 2338 | const field_index = @intCast(u32, field_index_big); | 2337 | const field_index = @intCast(u32, field_index_big); |
| 2339 | 2338 | ||
| 2340 | // TODO here we need to go back and see if we need to convert the union | 2339 | // Handle the possibility of the union value being comptime-known. |
| 2341 | // to a comptime-known value. This will involve editing the AIR code we have | 2340 | const union_ptr_inst = Air.refToIndex(sema.resolveInst(field_ptr_extra.lhs)).?; |
| 2342 | // generated so far - in particular deleting some runtime pointer bitcast | 2341 | switch (sema.air_instructions.items(.tag)[union_ptr_inst]) { |
| 2343 | // instructions which are not actually needed if the initialization expression | 2342 | .constant => return, // In this case the tag has already been set. No validation to do. |
| 2344 | // ends up being comptime-known. | 2343 | .bitcast => { |
| 2344 | // TODO here we need to go back and see if we need to convert the union | ||
| 2345 | // to a comptime-known value. In such case, we must delete all the instructions | ||
| 2346 | // added to the current block starting with the bitcast. | ||
| 2347 | // If the bitcast result ptr is an alloc, the alloc should be replaced with | ||
| 2348 | // a constant decl_ref. | ||
| 2349 | // Otherwise, the bitcast should be preserved and a store instruction should be | ||
| 2350 | // emitted to store the constant union value through the bitcast. | ||
| 2351 | }, | ||
| 2352 | else => unreachable, | ||
| 2353 | } | ||
| 2345 | 2354 | ||
| 2346 | // Otherwise, we set the new union tag now. | 2355 | // Otherwise, we set the new union tag now. |
| 2347 | const new_tag = try sema.addConstant( | 2356 | const new_tag = try sema.addConstant( |
| ... | @@ -4091,18 +4100,20 @@ fn analyzeCall( | ... | @@ -4091,18 +4100,20 @@ fn analyzeCall( |
| 4091 | zir_tags, | 4100 | zir_tags, |
| 4092 | ); | 4101 | ); |
| 4093 | } else res: { | 4102 | } else res: { |
| 4103 | try sema.requireRuntimeBlock(block, call_src); | ||
| 4104 | |||
| 4094 | const args = try sema.arena.alloc(Air.Inst.Ref, uncasted_args.len); | 4105 | const args = try sema.arena.alloc(Air.Inst.Ref, uncasted_args.len); |
| 4095 | for (uncasted_args) |uncasted_arg, i| { | 4106 | for (uncasted_args) |uncasted_arg, i| { |
| 4107 | const arg_src = call_src; // TODO: better source location | ||
| 4096 | if (i < fn_params_len) { | 4108 | if (i < fn_params_len) { |
| 4097 | const param_ty = func_ty.fnParamType(i); | 4109 | const param_ty = func_ty.fnParamType(i); |
| 4098 | const arg_src = call_src; // TODO: better source location | 4110 | try sema.resolveTypeLayout(block, arg_src, param_ty); |
| 4099 | args[i] = try sema.coerce(block, param_ty, uncasted_arg, arg_src); | 4111 | args[i] = try sema.coerce(block, param_ty, uncasted_arg, arg_src); |
| 4100 | } else { | 4112 | } else { |
| 4101 | args[i] = uncasted_arg; | 4113 | args[i] = uncasted_arg; |
| 4102 | } | 4114 | } |
| 4103 | } | 4115 | } |
| 4104 | 4116 | ||
| 4105 | try sema.requireRuntimeBlock(block, call_src); | ||
| 4106 | try sema.resolveTypeLayout(block, call_src, func_ty_info.return_type); | 4117 | try sema.resolveTypeLayout(block, call_src, func_ty_info.return_type); |
| 4107 | 4118 | ||
| 4108 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len + | 4119 | try sema.air_extra.ensureUnusedCapacity(gpa, @typeInfo(Air.Call).Struct.fields.len + |
| ... | @@ -4173,6 +4184,7 @@ fn finishGenericCall( | ... | @@ -4173,6 +4184,7 @@ fn finishGenericCall( |
| 4173 | const param_ty = new_fn_ty.fnParamType(runtime_i); | 4184 | const param_ty = new_fn_ty.fnParamType(runtime_i); |
| 4174 | const arg_src = call_src; // TODO: better source location | 4185 | const arg_src = call_src; // TODO: better source location |
| 4175 | const uncasted_arg = uncasted_args[total_i]; | 4186 | const uncasted_arg = uncasted_args[total_i]; |
| 4187 | try sema.resolveTypeLayout(block, arg_src, param_ty); | ||
| 4176 | const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src); | 4188 | const casted_arg = try sema.coerce(block, param_ty, uncasted_arg, arg_src); |
| 4177 | runtime_args[runtime_i] = casted_arg; | 4189 | runtime_args[runtime_i] = casted_arg; |
| 4178 | runtime_i += 1; | 4190 | runtime_i += 1; |
| ... | @@ -5548,7 +5560,7 @@ fn zirSwitchCapture( | ... | @@ -5548,7 +5560,7 @@ fn zirSwitchCapture( |
| 5548 | ); | 5560 | ); |
| 5549 | } | 5561 | } |
| 5550 | try sema.requireRuntimeBlock(block, operand_src); | 5562 | try sema.requireRuntimeBlock(block, operand_src); |
| 5551 | return block.addStructFieldPtr(operand_ptr, field_index, field.ty); | 5563 | return block.addStructFieldPtr(operand_ptr, field_index, field_ty_ptr); |
| 5552 | } | 5564 | } |
| 5553 | 5565 | ||
| 5554 | const operand = if (operand_is_ref) | 5566 | const operand = if (operand_is_ref) |
| ... | @@ -5669,11 +5681,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError | ... | @@ -5669,11 +5681,7 @@ fn zirSwitchBlock(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError |
| 5669 | const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset }; | 5681 | const special_prong_src: LazySrcLoc = .{ .node_offset_switch_special_prong = src_node_offset }; |
| 5670 | const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index); | 5682 | const extra = sema.code.extraData(Zir.Inst.SwitchBlock, inst_data.payload_index); |
| 5671 | 5683 | ||
| 5672 | const operand_ptr = sema.resolveInst(extra.data.operand); | 5684 | const operand = sema.resolveInst(extra.data.operand); |
| 5673 | const operand = if (extra.data.bits.is_ref) | ||
| 5674 | try sema.analyzeLoad(block, src, operand_ptr, operand_src) | ||
| 5675 | else | ||
| 5676 | operand_ptr; | ||
| 5677 | 5685 | ||
| 5678 | var header_extra_index: usize = extra.end; | 5686 | var header_extra_index: usize = extra.end; |
| 5679 | 5687 | ||
| ... | @@ -8675,14 +8683,6 @@ fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. | ... | @@ -8675,14 +8683,6 @@ fn zirTypeof(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air. |
| 8675 | return sema.addType(operand_ty); | 8683 | return sema.addType(operand_ty); |
| 8676 | } | 8684 | } |
| 8677 | 8685 | ||
| 8678 | fn zirTypeofElem(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | ||
| 8679 | _ = block; | ||
| 8680 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 8681 | const operand_ptr = sema.resolveInst(inst_data.operand); | ||
| 8682 | const elem_ty = sema.typeOf(operand_ptr).elemType(); | ||
| 8683 | return sema.addType(elem_ty); | ||
| 8684 | } | ||
| 8685 | |||
| 8686 | fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 8686 | fn zirTypeofLog2IntType(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 8687 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 8687 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 8688 | const src = inst_data.src(); | 8688 | const src = inst_data.src(); |
src/Zir.zig+11-6| ... | @@ -544,9 +544,6 @@ pub const Inst = struct { | ... | @@ -544,9 +544,6 @@ pub const Inst = struct { |
| 544 | /// Returns the type of a value. | 544 | /// Returns the type of a value. |
| 545 | /// Uses the `un_node` field. | 545 | /// Uses the `un_node` field. |
| 546 | typeof, | 546 | typeof, |
| 547 | /// Given a value which is a pointer, returns the element type. | ||
| 548 | /// Uses the `un_node` field. | ||
| 549 | typeof_elem, | ||
| 550 | /// Given a value, look at the type of it, which must be an integer type. | 547 | /// Given a value, look at the type of it, which must be an integer type. |
| 551 | /// Returns the integer type for the RHS of a shift operation. | 548 | /// Returns the integer type for the RHS of a shift operation. |
| 552 | /// Uses the `un_node` field. | 549 | /// Uses the `un_node` field. |
| ... | @@ -1045,7 +1042,6 @@ pub const Inst = struct { | ... | @@ -1045,7 +1042,6 @@ pub const Inst = struct { |
| 1045 | .negate, | 1042 | .negate, |
| 1046 | .negate_wrap, | 1043 | .negate_wrap, |
| 1047 | .typeof, | 1044 | .typeof, |
| 1048 | .typeof_elem, | ||
| 1049 | .xor, | 1045 | .xor, |
| 1050 | .optional_type, | 1046 | .optional_type, |
| 1051 | .optional_payload_safe, | 1047 | .optional_payload_safe, |
| ... | @@ -1312,7 +1308,6 @@ pub const Inst = struct { | ... | @@ -1312,7 +1308,6 @@ pub const Inst = struct { |
| 1312 | .negate = .un_node, | 1308 | .negate = .un_node, |
| 1313 | .negate_wrap = .un_node, | 1309 | .negate_wrap = .un_node, |
| 1314 | .typeof = .un_node, | 1310 | .typeof = .un_node, |
| 1315 | .typeof_elem = .un_node, | ||
| 1316 | .typeof_log2_int_type = .un_node, | 1311 | .typeof_log2_int_type = .un_node, |
| 1317 | .log2_int_type = .un_node, | 1312 | .log2_int_type = .un_node, |
| 1318 | .@"unreachable" = .@"unreachable", | 1313 | .@"unreachable" = .@"unreachable", |
| ... | @@ -2443,6 +2438,13 @@ pub const Inst = struct { | ... | @@ -2443,6 +2438,13 @@ pub const Inst = struct { |
| 2443 | /// body member Index for every body_len | 2438 | /// body member Index for every body_len |
| 2444 | /// } | 2439 | /// } |
| 2445 | pub const SwitchBlock = struct { | 2440 | pub const SwitchBlock = struct { |
| 2441 | /// This is always a `switch_cond` or `switch_cond_ref` instruction. | ||
| 2442 | /// If it is a `switch_cond_ref` instruction, bits.is_ref is always true. | ||
| 2443 | /// If it is a `switch_cond` instruction, bits.is_ref is always false. | ||
| 2444 | /// Both `switch_cond` and `switch_cond_ref` return a value, not a pointer, | ||
| 2445 | /// that is useful for the case items, but cannot be used for capture values. | ||
| 2446 | /// For the capture values, Sema is expected to find the operand of this operand | ||
| 2447 | /// and use that. | ||
| 2446 | operand: Ref, | 2448 | operand: Ref, |
| 2447 | bits: Bits, | 2449 | bits: Bits, |
| 2448 | 2450 | ||
| ... | @@ -2454,8 +2456,11 @@ pub const Inst = struct { | ... | @@ -2454,8 +2456,11 @@ pub const Inst = struct { |
| 2454 | /// If true, there is an underscore prong. This is mutually exclusive with `has_else`. | 2456 | /// If true, there is an underscore prong. This is mutually exclusive with `has_else`. |
| 2455 | has_under: bool, | 2457 | has_under: bool, |
| 2456 | /// If true, the `operand` is a pointer to the value being switched on. | 2458 | /// If true, the `operand` is a pointer to the value being switched on. |
| 2459 | /// TODO this flag is redundant with the tag of operand and can be removed. | ||
| 2457 | is_ref: bool, | 2460 | is_ref: bool, |
| 2458 | scalar_cases_len: u28, | 2461 | scalar_cases_len: ScalarCasesLen, |
| 2462 | |||
| 2463 | pub const ScalarCasesLen = u28; | ||
| 2459 | 2464 | ||
| 2460 | pub fn specialProng(bits: Bits) SpecialProng { | 2465 | pub fn specialProng(bits: Bits) SpecialProng { |
| 2461 | const has_else: u2 = @boolToInt(bits.has_else); | 2466 | const has_else: u2 = @boolToInt(bits.has_else); |
src/print_zir.zig-1| ... | @@ -184,7 +184,6 @@ const Writer = struct { | ... | @@ -184,7 +184,6 @@ const Writer = struct { |
| 184 | .is_non_err, | 184 | .is_non_err, |
| 185 | .is_non_err_ptr, | 185 | .is_non_err_ptr, |
| 186 | .typeof, | 186 | .typeof, |
| 187 | .typeof_elem, | ||
| 188 | .struct_init_empty, | 187 | .struct_init_empty, |
| 189 | .type_info, | 188 | .type_info, |
| 190 | .size_of, | 189 | .size_of, |
src/type.zig+37-22| ... | @@ -3391,34 +3391,49 @@ pub const Type = extern union { | ... | @@ -3391,34 +3391,49 @@ pub const Type = extern union { |
| 3391 | } | 3391 | } |
| 3392 | } | 3392 | } |
| 3393 | 3393 | ||
| 3394 | /// Supports structs and unions. | ||
| 3394 | pub fn structFieldOffset(ty: Type, index: usize, target: Target) u64 { | 3395 | pub fn structFieldOffset(ty: Type, index: usize, target: Target) u64 { |
| 3395 | const fields = ty.structFields(); | 3396 | switch (ty.tag()) { |
| 3396 | if (ty.castTag(.@"struct")) |payload| { | 3397 | .@"struct" => { |
| 3397 | const struct_obj = payload.data; | 3398 | const struct_obj = ty.castTag(.@"struct").?.data; |
| 3398 | assert(struct_obj.status == .have_layout); | 3399 | assert(struct_obj.status == .have_layout); |
| 3399 | const is_packed = struct_obj.layout == .Packed; | 3400 | const is_packed = struct_obj.layout == .Packed; |
| 3400 | if (is_packed) @panic("TODO packed structs"); | 3401 | if (is_packed) @panic("TODO packed structs"); |
| 3401 | } | ||
| 3402 | 3402 | ||
| 3403 | var offset: u64 = 0; | 3403 | var offset: u64 = 0; |
| 3404 | var big_align: u32 = 0; | 3404 | var big_align: u32 = 0; |
| 3405 | for (fields.values()) |field, i| { | 3405 | for (struct_obj.fields.values()) |field, i| { |
| 3406 | if (!field.ty.hasCodeGenBits()) continue; | 3406 | if (!field.ty.hasCodeGenBits()) continue; |
| 3407 | 3407 | ||
| 3408 | const field_align = a: { | 3408 | const field_align = a: { |
| 3409 | if (field.abi_align.tag() == .abi_align_default) { | 3409 | if (field.abi_align.tag() == .abi_align_default) { |
| 3410 | break :a field.ty.abiAlignment(target); | 3410 | break :a field.ty.abiAlignment(target); |
| 3411 | } else { | ||
| 3412 | break :a @intCast(u32, field.abi_align.toUnsignedInt()); | ||
| 3413 | } | ||
| 3414 | }; | ||
| 3415 | big_align = @maximum(big_align, field_align); | ||
| 3416 | offset = std.mem.alignForwardGeneric(u64, offset, field_align); | ||
| 3417 | if (i == index) return offset; | ||
| 3418 | offset += field.ty.abiSize(target); | ||
| 3419 | } | ||
| 3420 | offset = std.mem.alignForwardGeneric(u64, offset, big_align); | ||
| 3421 | return offset; | ||
| 3422 | }, | ||
| 3423 | .@"union" => return 0, | ||
| 3424 | .union_tagged => { | ||
| 3425 | const union_obj = ty.castTag(.union_tagged).?.data; | ||
| 3426 | const layout = union_obj.getLayout(target, true); | ||
| 3427 | if (layout.tag_align >= layout.payload_align) { | ||
| 3428 | // {Tag, Payload} | ||
| 3429 | return std.mem.alignForwardGeneric(u64, layout.tag_size, layout.payload_align); | ||
| 3411 | } else { | 3430 | } else { |
| 3412 | break :a @intCast(u32, field.abi_align.toUnsignedInt()); | 3431 | // {Payload, Tag} |
| 3432 | return 0; | ||
| 3413 | } | 3433 | } |
| 3414 | }; | 3434 | }, |
| 3415 | big_align = @maximum(big_align, field_align); | 3435 | else => unreachable, |
| 3416 | offset = std.mem.alignForwardGeneric(u64, offset, field_align); | ||
| 3417 | if (i == index) return offset; | ||
| 3418 | offset += field.ty.abiSize(target); | ||
| 3419 | } | 3436 | } |
| 3420 | offset = std.mem.alignForwardGeneric(u64, offset, big_align); | ||
| 3421 | return offset; | ||
| 3422 | } | 3437 | } |
| 3423 | 3438 | ||
| 3424 | pub fn declSrcLoc(ty: Type) Module.SrcLoc { | 3439 | pub fn declSrcLoc(ty: Type) Module.SrcLoc { |
test/behavior/switch.zig+43| ... | @@ -219,3 +219,46 @@ test "switch on global mutable var isn't constant-folded" { | ... | @@ -219,3 +219,46 @@ test "switch on global mutable var isn't constant-folded" { |
| 219 | poll(); | 219 | poll(); |
| 220 | } | 220 | } |
| 221 | } | 221 | } |
| 222 | |||
| 223 | const SwitchProngWithVarEnum = union(enum) { | ||
| 224 | One: i32, | ||
| 225 | Two: f32, | ||
| 226 | Meh: void, | ||
| 227 | }; | ||
| 228 | |||
| 229 | test "switch prong with variable" { | ||
| 230 | try switchProngWithVarFn(SwitchProngWithVarEnum{ .One = 13 }); | ||
| 231 | try switchProngWithVarFn(SwitchProngWithVarEnum{ .Two = 13.0 }); | ||
| 232 | try switchProngWithVarFn(SwitchProngWithVarEnum{ .Meh = {} }); | ||
| 233 | } | ||
| 234 | fn switchProngWithVarFn(a: SwitchProngWithVarEnum) !void { | ||
| 235 | switch (a) { | ||
| 236 | SwitchProngWithVarEnum.One => |x| { | ||
| 237 | try expect(x == 13); | ||
| 238 | }, | ||
| 239 | SwitchProngWithVarEnum.Two => |x| { | ||
| 240 | try expect(x == 13.0); | ||
| 241 | }, | ||
| 242 | SwitchProngWithVarEnum.Meh => |x| { | ||
| 243 | const v: void = x; | ||
| 244 | _ = v; | ||
| 245 | }, | ||
| 246 | } | ||
| 247 | } | ||
| 248 | |||
| 249 | test "switch on enum using pointer capture" { | ||
| 250 | try testSwitchEnumPtrCapture(); | ||
| 251 | comptime try testSwitchEnumPtrCapture(); | ||
| 252 | } | ||
| 253 | |||
| 254 | fn testSwitchEnumPtrCapture() !void { | ||
| 255 | var value = SwitchProngWithVarEnum{ .One = 1234 }; | ||
| 256 | switch (value) { | ||
| 257 | SwitchProngWithVarEnum.One => |*x| x.* += 1, | ||
| 258 | else => unreachable, | ||
| 259 | } | ||
| 260 | switch (value) { | ||
| 261 | SwitchProngWithVarEnum.One => |x| try expect(x == 1235), | ||
| 262 | else => unreachable, | ||
| 263 | } | ||
| 264 | } |
test/behavior/switch_stage1.zig-42| ... | @@ -3,48 +3,6 @@ const expect = std.testing.expect; | ... | @@ -3,48 +3,6 @@ const expect = std.testing.expect; |
| 3 | const expectError = std.testing.expectError; | 3 | const expectError = std.testing.expectError; |
| 4 | const expectEqual = std.testing.expectEqual; | 4 | const expectEqual = std.testing.expectEqual; |
| 5 | 5 | ||
| 6 | test "switch prong with variable" { | ||
| 7 | try switchProngWithVarFn(SwitchProngWithVarEnum{ .One = 13 }); | ||
| 8 | try switchProngWithVarFn(SwitchProngWithVarEnum{ .Two = 13.0 }); | ||
| 9 | try switchProngWithVarFn(SwitchProngWithVarEnum{ .Meh = {} }); | ||
| 10 | } | ||
| 11 | const SwitchProngWithVarEnum = union(enum) { | ||
| 12 | One: i32, | ||
| 13 | Two: f32, | ||
| 14 | Meh: void, | ||
| 15 | }; | ||
| 16 | fn switchProngWithVarFn(a: SwitchProngWithVarEnum) !void { | ||
| 17 | switch (a) { | ||
| 18 | SwitchProngWithVarEnum.One => |x| { | ||
| 19 | try expect(x == 13); | ||
| 20 | }, | ||
| 21 | SwitchProngWithVarEnum.Two => |x| { | ||
| 22 | try expect(x == 13.0); | ||
| 23 | }, | ||
| 24 | SwitchProngWithVarEnum.Meh => |x| { | ||
| 25 | const v: void = x; | ||
| 26 | _ = v; | ||
| 27 | }, | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | test "switch on enum using pointer capture" { | ||
| 32 | try testSwitchEnumPtrCapture(); | ||
| 33 | comptime try testSwitchEnumPtrCapture(); | ||
| 34 | } | ||
| 35 | |||
| 36 | fn testSwitchEnumPtrCapture() !void { | ||
| 37 | var value = SwitchProngWithVarEnum{ .One = 1234 }; | ||
| 38 | switch (value) { | ||
| 39 | SwitchProngWithVarEnum.One => |*x| x.* += 1, | ||
| 40 | else => unreachable, | ||
| 41 | } | ||
| 42 | switch (value) { | ||
| 43 | SwitchProngWithVarEnum.One => |x| try expect(x == 1235), | ||
| 44 | else => unreachable, | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | test "switch handles all cases of number" { | 6 | test "switch handles all cases of number" { |
| 49 | try testSwitchHandleAllCases(); | 7 | try testSwitchHandleAllCases(); |
| 50 | comptime try testSwitchHandleAllCases(); | 8 | comptime try testSwitchHandleAllCases(); |