| author | |
| committer | |
| log | e9d1e5e533d12abe14582736d90e4cb173addc56 |
| tree | cb284bafd08c6287cb2ea50312d2cb13f4c91091 |
| parent | 07691db3ae061d8122f6c53c1f34c2fb0df2f7ef |
4 files changed, 125 insertions(+), 39 deletions(-)
src/codegen/llvm.zig+84-1| ... | @@ -1085,6 +1085,26 @@ pub const DeclGen = struct { | ... | @@ -1085,6 +1085,26 @@ pub const DeclGen = struct { |
| 1085 | const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(), .False); | 1085 | const llvm_int = llvm_usize.constInt(tv.val.toUnsignedInt(), .False); |
| 1086 | return llvm_int.constIntToPtr(try self.llvmType(tv.ty)); | 1086 | return llvm_int.constIntToPtr(try self.llvmType(tv.ty)); |
| 1087 | }, | 1087 | }, |
| 1088 | .field_ptr => { | ||
| 1089 | const field_ptr = tv.val.castTag(.field_ptr).?.data; | ||
| 1090 | const parent_ptr = try self.lowerParentPtr(field_ptr.container_ptr); | ||
| 1091 | const llvm_u32 = self.context.intType(32); | ||
| 1092 | const indices: [2]*const llvm.Value = .{ | ||
| 1093 | llvm_u32.constInt(0, .False), | ||
| 1094 | llvm_u32.constInt(field_ptr.field_index, .False), | ||
| 1095 | }; | ||
| 1096 | return parent_ptr.constInBoundsGEP(&indices, indices.len); | ||
| 1097 | }, | ||
| 1098 | .elem_ptr => { | ||
| 1099 | const elem_ptr = tv.val.castTag(.elem_ptr).?.data; | ||
| 1100 | const parent_ptr = try self.lowerParentPtr(elem_ptr.array_ptr); | ||
| 1101 | const llvm_usize = try self.llvmType(Type.usize); | ||
| 1102 | const indices: [2]*const llvm.Value = .{ | ||
| 1103 | llvm_usize.constInt(0, .False), | ||
| 1104 | llvm_usize.constInt(elem_ptr.index, .False), | ||
| 1105 | }; | ||
| 1106 | return parent_ptr.constInBoundsGEP(&indices, indices.len); | ||
| 1107 | }, | ||
| 1088 | else => |tag| return self.todo("implement const of pointer type '{}' ({})", .{ tv.ty, tag }), | 1108 | else => |tag| return self.todo("implement const of pointer type '{}' ({})", .{ tv.ty, tag }), |
| 1089 | }, | 1109 | }, |
| 1090 | .Array => switch (tv.val.tag()) { | 1110 | .Array => switch (tv.val.tag()) { |
| ... | @@ -1298,6 +1318,68 @@ pub const DeclGen = struct { | ... | @@ -1298,6 +1318,68 @@ pub const DeclGen = struct { |
| 1298 | } | 1318 | } |
| 1299 | } | 1319 | } |
| 1300 | 1320 | ||
| 1321 | const ParentPtr = struct { | ||
| 1322 | ty: Type, | ||
| 1323 | llvm_ptr: *const llvm.Value, | ||
| 1324 | }; | ||
| 1325 | |||
| 1326 | fn lowerParentPtrDecl( | ||
| 1327 | dg: *DeclGen, | ||
| 1328 | ptr_val: Value, | ||
| 1329 | decl: *Module.Decl, | ||
| 1330 | ) Error!ParentPtr { | ||
| 1331 | var ptr_ty_payload: Type.Payload.ElemType = .{ | ||
| 1332 | .base = .{ .tag = .single_mut_pointer }, | ||
| 1333 | .data = decl.ty, | ||
| 1334 | }; | ||
| 1335 | const ptr_ty = Type.initPayload(&ptr_ty_payload.base); | ||
| 1336 | const llvm_ptr = try dg.lowerDeclRefValue(.{ .ty = ptr_ty, .val = ptr_val }, decl); | ||
| 1337 | return ParentPtr{ | ||
| 1338 | .llvm_ptr = llvm_ptr, | ||
| 1339 | .ty = decl.ty, | ||
| 1340 | }; | ||
| 1341 | } | ||
| 1342 | |||
| 1343 | fn lowerParentPtr(dg: *DeclGen, ptr_val: Value) Error!*const llvm.Value { | ||
| 1344 | switch (ptr_val.tag()) { | ||
| 1345 | .decl_ref_mut => { | ||
| 1346 | const decl = ptr_val.castTag(.decl_ref_mut).?.data.decl; | ||
| 1347 | return (try dg.lowerParentPtrDecl(ptr_val, decl)).llvm_ptr; | ||
| 1348 | }, | ||
| 1349 | .decl_ref => { | ||
| 1350 | const decl = ptr_val.castTag(.decl_ref).?.data; | ||
| 1351 | return (try dg.lowerParentPtrDecl(ptr_val, decl)).llvm_ptr; | ||
| 1352 | }, | ||
| 1353 | .variable => { | ||
| 1354 | const decl = ptr_val.castTag(.variable).?.data.owner_decl; | ||
| 1355 | return (try dg.lowerParentPtrDecl(ptr_val, decl)).llvm_ptr; | ||
| 1356 | }, | ||
| 1357 | .field_ptr => { | ||
| 1358 | const field_ptr = ptr_val.castTag(.field_ptr).?.data; | ||
| 1359 | const parent_ptr = try dg.lowerParentPtr(field_ptr.container_ptr); | ||
| 1360 | const llvm_u32 = dg.context.intType(32); | ||
| 1361 | const indices: [2]*const llvm.Value = .{ | ||
| 1362 | llvm_u32.constInt(0, .False), | ||
| 1363 | llvm_u32.constInt(field_ptr.field_index, .False), | ||
| 1364 | }; | ||
| 1365 | return parent_ptr.constInBoundsGEP(&indices, indices.len); | ||
| 1366 | }, | ||
| 1367 | .elem_ptr => { | ||
| 1368 | const elem_ptr = ptr_val.castTag(.elem_ptr).?.data; | ||
| 1369 | const parent_ptr = try dg.lowerParentPtr(elem_ptr.array_ptr); | ||
| 1370 | const llvm_usize = try dg.llvmType(Type.usize); | ||
| 1371 | const indices: [2]*const llvm.Value = .{ | ||
| 1372 | llvm_usize.constInt(0, .False), | ||
| 1373 | llvm_usize.constInt(elem_ptr.index, .False), | ||
| 1374 | }; | ||
| 1375 | return parent_ptr.constInBoundsGEP(&indices, indices.len); | ||
| 1376 | }, | ||
| 1377 | .opt_payload_ptr => return dg.todo("implement lowerParentPtr for optional payload", .{}), | ||
| 1378 | .eu_payload_ptr => return dg.todo("implement lowerParentPtr for error union payload", .{}), | ||
| 1379 | else => unreachable, | ||
| 1380 | } | ||
| 1381 | } | ||
| 1382 | |||
| 1301 | fn lowerDeclRefValue( | 1383 | fn lowerDeclRefValue( |
| 1302 | self: *DeclGen, | 1384 | self: *DeclGen, |
| 1303 | tv: TypedValue, | 1385 | tv: TypedValue, |
| ... | @@ -1323,12 +1405,13 @@ pub const DeclGen = struct { | ... | @@ -1323,12 +1405,13 @@ pub const DeclGen = struct { |
| 1323 | return self.context.constStruct(&fields, fields.len, .False); | 1405 | return self.context.constStruct(&fields, fields.len, .False); |
| 1324 | } | 1406 | } |
| 1325 | 1407 | ||
| 1326 | decl.alive = true; | ||
| 1327 | const llvm_type = try self.llvmType(tv.ty); | 1408 | const llvm_type = try self.llvmType(tv.ty); |
| 1328 | if (!tv.ty.childType().hasCodeGenBits()) { | 1409 | if (!tv.ty.childType().hasCodeGenBits()) { |
| 1329 | return self.lowerPtrToVoid(tv.ty); | 1410 | return self.lowerPtrToVoid(tv.ty); |
| 1330 | } | 1411 | } |
| 1331 | 1412 | ||
| 1413 | decl.alive = true; | ||
| 1414 | |||
| 1332 | const llvm_val = if (decl.ty.zigTypeTag() == .Fn) | 1415 | const llvm_val = if (decl.ty.zigTypeTag() == .Fn) |
| 1333 | try self.resolveLlvmFunction(decl) | 1416 | try self.resolveLlvmFunction(decl) |
| 1334 | else | 1417 | else |
test/behavior.zig+2-1| ... | @@ -39,6 +39,7 @@ test { | ... | @@ -39,6 +39,7 @@ test { |
| 39 | _ = @import("behavior/math.zig"); | 39 | _ = @import("behavior/math.zig"); |
| 40 | _ = @import("behavior/maximum_minimum.zig"); | 40 | _ = @import("behavior/maximum_minimum.zig"); |
| 41 | _ = @import("behavior/member_func.zig"); | 41 | _ = @import("behavior/member_func.zig"); |
| 42 | _ = @import("behavior/null.zig"); | ||
| 42 | _ = @import("behavior/optional.zig"); | 43 | _ = @import("behavior/optional.zig"); |
| 43 | _ = @import("behavior/pointers.zig"); | 44 | _ = @import("behavior/pointers.zig"); |
| 44 | _ = @import("behavior/pub_enum.zig"); | 45 | _ = @import("behavior/pub_enum.zig"); |
| ... | @@ -137,7 +138,7 @@ test { | ... | @@ -137,7 +138,7 @@ test { |
| 137 | _ = @import("behavior/misc.zig"); | 138 | _ = @import("behavior/misc.zig"); |
| 138 | _ = @import("behavior/muladd.zig"); | 139 | _ = @import("behavior/muladd.zig"); |
| 139 | _ = @import("behavior/namespace_depends_on_compile_var.zig"); | 140 | _ = @import("behavior/namespace_depends_on_compile_var.zig"); |
| 140 | _ = @import("behavior/null.zig"); | 141 | _ = @import("behavior/null_stage1.zig"); |
| 141 | _ = @import("behavior/optional_stage1.zig"); | 142 | _ = @import("behavior/optional_stage1.zig"); |
| 142 | _ = @import("behavior/pointers_stage1.zig"); | 143 | _ = @import("behavior/pointers_stage1.zig"); |
| 143 | _ = @import("behavior/popcount.zig"); | 144 | _ = @import("behavior/popcount.zig"); |
test/behavior/null.zig+2-37| ... | @@ -1,4 +1,5 @@ | ... | @@ -1,4 +1,5 @@ |
| 1 | const expect = @import("std").testing.expect; | 1 | const std = @import("std"); |
| 2 | const expect = std.testing.expect; | ||
| 2 | 3 | ||
| 3 | test "optional type" { | 4 | test "optional type" { |
| 4 | const x: ?bool = true; | 5 | const x: ?bool = true; |
| ... | @@ -58,31 +59,6 @@ fn foo(x: ?i32) ?bool { | ... | @@ -58,31 +59,6 @@ fn foo(x: ?i32) ?bool { |
| 58 | return value > 1234; | 59 | return value > 1234; |
| 59 | } | 60 | } |
| 60 | 61 | ||
| 61 | test "if var maybe pointer" { | ||
| 62 | try expect(shouldBeAPlus1(Particle{ | ||
| 63 | .a = 14, | ||
| 64 | .b = 1, | ||
| 65 | .c = 1, | ||
| 66 | .d = 1, | ||
| 67 | }) == 15); | ||
| 68 | } | ||
| 69 | fn shouldBeAPlus1(p: Particle) u64 { | ||
| 70 | var maybe_particle: ?Particle = p; | ||
| 71 | if (maybe_particle) |*particle| { | ||
| 72 | particle.a += 1; | ||
| 73 | } | ||
| 74 | if (maybe_particle) |particle| { | ||
| 75 | return particle.a; | ||
| 76 | } | ||
| 77 | return 0; | ||
| 78 | } | ||
| 79 | const Particle = struct { | ||
| 80 | a: u64, | ||
| 81 | b: u64, | ||
| 82 | c: u64, | ||
| 83 | d: u64, | ||
| 84 | }; | ||
| 85 | |||
| 86 | test "null literal outside function" { | 62 | test "null literal outside function" { |
| 87 | const is_null = here_is_a_null_literal.context == null; | 63 | const is_null = here_is_a_null_literal.context == null; |
| 88 | try expect(is_null); | 64 | try expect(is_null); |
| ... | @@ -146,17 +122,6 @@ test "null with default unwrap" { | ... | @@ -146,17 +122,6 @@ test "null with default unwrap" { |
| 146 | try expect(x == 1); | 122 | try expect(x == 1); |
| 147 | } | 123 | } |
| 148 | 124 | ||
| 149 | test "optional types" { | ||
| 150 | comptime { | ||
| 151 | const opt_type_struct = StructWithOptionalType{ .t = u8 }; | ||
| 152 | try expect(opt_type_struct.t != null and opt_type_struct.t.? == u8); | ||
| 153 | } | ||
| 154 | } | ||
| 155 | |||
| 156 | const StructWithOptionalType = struct { | ||
| 157 | t: ?type, | ||
| 158 | }; | ||
| 159 | |||
| 160 | test "optional pointer to 0 bit type null value at runtime" { | 125 | test "optional pointer to 0 bit type null value at runtime" { |
| 161 | const EmptyStruct = struct {}; | 126 | const EmptyStruct = struct {}; |
| 162 | var x: ?*EmptyStruct = null; | 127 | var x: ?*EmptyStruct = null; |
test/behavior/null_stage1.zig created+37| ... | @@ -0,0 +1,37 @@ | ||
| 1 | const expect = @import("std").testing.expect; | ||
| 2 | |||
| 3 | test "if var maybe pointer" { | ||
| 4 | try expect(shouldBeAPlus1(Particle{ | ||
| 5 | .a = 14, | ||
| 6 | .b = 1, | ||
| 7 | .c = 1, | ||
| 8 | .d = 1, | ||
| 9 | }) == 15); | ||
| 10 | } | ||
| 11 | fn shouldBeAPlus1(p: Particle) u64 { | ||
| 12 | var maybe_particle: ?Particle = p; | ||
| 13 | if (maybe_particle) |*particle| { | ||
| 14 | particle.a += 1; | ||
| 15 | } | ||
| 16 | if (maybe_particle) |particle| { | ||
| 17 | return particle.a; | ||
| 18 | } | ||
| 19 | return 0; | ||
| 20 | } | ||
| 21 | const Particle = struct { | ||
| 22 | a: u64, | ||
| 23 | b: u64, | ||
| 24 | c: u64, | ||
| 25 | d: u64, | ||
| 26 | }; | ||
| 27 | |||
| 28 | test "optional types" { | ||
| 29 | comptime { | ||
| 30 | const opt_type_struct = StructWithOptionalType{ .t = u8 }; | ||
| 31 | try expect(opt_type_struct.t != null and opt_type_struct.t.? == u8); | ||
| 32 | } | ||
| 33 | } | ||
| 34 | |||
| 35 | const StructWithOptionalType = struct { | ||
| 36 | t: ?type, | ||
| 37 | }; | ||