| ... | ... | @@ -4255,6 +4255,134 @@ test "enum literals with switch" { |
| 4255 | 4255 | } |
| 4256 | 4256 | {#code_end#} |
| 4257 | 4257 | {#header_close#} |
| 4258 | |
| 4259 | {#header_open|Inline switch#} |
| 4260 | <p> |
| 4261 | Switch prongs can be marked as {#syntax#}inline{#endsyntax#} to generate |
| 4262 | the prong's body for each possible value it could have: |
| 4263 | </p> |
| 4264 | {#code_begin|test|test_inline_switch#} |
| 4265 | const std = @import("std"); |
| 4266 | const expect = std.testing.expect; |
| 4267 | const expectError = std.testing.expectError; |
| 4268 | |
| 4269 | fn isFieldOptional(comptime T: type, field_index: usize) !bool { |
| 4270 | const fields = @typeInfo(T).Struct.fields; |
| 4271 | return switch (field_index) { |
| 4272 | // This prong is analyzed `fields.len - 1` times with `idx` being an |
| 4273 | // unique comptime known value each time. |
| 4274 | inline 0...fields.len - 1 => |idx| @typeInfo(fields[idx].field_type) == .Optional, |
| 4275 | else => return error.IndexOutOfBounds, |
| 4276 | }; |
| 4277 | } |
| 4278 | |
| 4279 | const Struct1 = struct { a: u32, b: ?u32 }; |
| 4280 | |
| 4281 | test "using @typeInfo with runtime values" { |
| 4282 | var index: usize = 0; |
| 4283 | try expect(!try isFieldOptional(Struct1, index)); |
| 4284 | index += 1; |
| 4285 | try expect(try isFieldOptional(Struct1, index)); |
| 4286 | index += 1; |
| 4287 | try expectError(error.IndexOutOfBounds, isFieldOptional(Struct1, index)); |
| 4288 | } |
| 4289 | |
| 4290 | // Calls to `isFieldOptional` on `Struct1` get unrolled to an equivalent |
| 4291 | // of this function: |
| 4292 | fn isFieldOptionalUnrolled(field_index: usize) !bool { |
| 4293 | return switch (field_index) { |
| 4294 | 0 => false, |
| 4295 | 1 => true, |
| 4296 | else => return error.IndexOutOfBounds, |
| 4297 | }; |
| 4298 | } |
| 4299 | {#code_end#} |
| 4300 | <p> |
| 4301 | {#syntax#}inline else{#endsyntax#} prongs can be used as a type safe |
| 4302 | alternative to {#syntax#}inline for{#endsyntax#} loops: |
| 4303 | </p> |
| 4304 | {#code_begin|test|test_inline_else#} |
| 4305 | const std = @import("std"); |
| 4306 | const expect = std.testing.expect; |
| 4307 | |
| 4308 | const SliceTypeA = extern struct { |
| 4309 | len: usize, |
| 4310 | ptr: [*]u32, |
| 4311 | }; |
| 4312 | const SliceTypeB = extern struct { |
| 4313 | ptr: [*]SliceTypeA, |
| 4314 | len: usize, |
| 4315 | }; |
| 4316 | const AnySlice = union(enum) { |
| 4317 | a: SliceTypeA, |
| 4318 | b: SliceTypeB, |
| 4319 | c: []const u8, |
| 4320 | d: []AnySlice, |
| 4321 | }; |
| 4322 | |
| 4323 | fn withFor(any: AnySlice) usize { |
| 4324 | const Tag = @typeInfo(AnySlice).Union.tag_type.?; |
| 4325 | inline for (@typeInfo(Tag).Enum.fields) |field| { |
| 4326 | // With `inline for` the function gets generated as |
| 4327 | // a series of `if` statements relying on the optimizer |
| 4328 | // to convert it to a switch. |
| 4329 | if (field.value == @enumToInt(any)) { |
| 4330 | return @field(any, field.name).len; |
| 4331 | } |
| 4332 | } |
| 4333 | // When using `inline for` the compiler doesn't know that every |
| 4334 | // possible case has been handled requiring an explicit `unreachable`. |
| 4335 | unreachable; |
| 4336 | } |
| 4337 | |
| 4338 | fn withSwitch(any: AnySlice) usize { |
| 4339 | return switch (any) { |
| 4340 | // With `inline else` the function is explicitly generated |
| 4341 | // as the desired switch and the compiler can check that |
| 4342 | // every possible case is handled. |
| 4343 | inline else => |slice| slice.len, |
| 4344 | }; |
| 4345 | } |
| 4346 | |
| 4347 | test "inline for and inline else similarity" { |
| 4348 | var any = AnySlice{ .c = "hello" }; |
| 4349 | try expect(withFor(any) == 5); |
| 4350 | try expect(withSwitch(any) == 5); |
| 4351 | } |
| 4352 | {#code_end#} |
| 4353 | <p> |
| 4354 | When using an inline prong switching on an union an additional |
| 4355 | capture can be used to obtain the union's enum tag value. |
| 4356 | </p> |
| 4357 | {#code_begin|test|test_inline_switch_union_tag#} |
| 4358 | const std = @import("std"); |
| 4359 | const expect = std.testing.expect; |
| 4360 | |
| 4361 | const U = union(enum) { |
| 4362 | a: u32, |
| 4363 | b: f32, |
| 4364 | }; |
| 4365 | |
| 4366 | fn getNum(u: U) u32 { |
| 4367 | switch (u) { |
| 4368 | // Here `num` is a runtime known value that is either |
| 4369 | // `u.a` or `u.b` and `tag` is `u`'s comptime known tag value. |
| 4370 | inline else => |num, tag| { |
| 4371 | if (tag == .b) { |
| 4372 | return @floatToInt(u32, num); |
| 4373 | } |
| 4374 | return num; |
| 4375 | } |
| 4376 | } |
| 4377 | } |
| 4378 | |
| 4379 | test "test" { |
| 4380 | var u = U{ .b = 42 }; |
| 4381 | try expect(getNum(u) == 42); |
| 4382 | } |
| 4383 | {#code_end#} |
| 4384 | {#see_also|inline while|inline for#} |
| 4385 | {#header_close#} |
| 4258 | 4386 | {#header_close#} |
| 4259 | 4387 | |
| 4260 | 4388 | {#header_open|while#} |