| author | |
| committer | |
| log | 6e0fb060109349b4fb7855c1587b6d9c396d926b |
| tree | bbf9a1aed1c8a160caffe68821f71ffd5c1d6fec |
| parent | cb06d62603c764a91aeb9bcedc0b9472d746f9e5 |
| parent | ec4953504a07f3025d5f32344180dd9b6a4de8ae |
closes #60428 files changed, 420 insertions(+), 44 deletions(-)
src-self-hosted/Module.zig+85-5| ... | @@ -2219,11 +2219,6 @@ pub fn wantSafety(self: *Module, scope: *Scope) bool { | ... | @@ -2219,11 +2219,6 @@ pub fn wantSafety(self: *Module, scope: *Scope) bool { |
| 2219 | }; | 2219 | }; |
| 2220 | } | 2220 | } |
| 2221 | 2221 | ||
| 2222 | pub fn analyzeUnreach(self: *Module, scope: *Scope, src: usize) InnerError!*Inst { | ||
| 2223 | const b = try self.requireRuntimeBlock(scope, src); | ||
| 2224 | return self.addNoOp(b, src, Type.initTag(.noreturn), .unreach); | ||
| 2225 | } | ||
| 2226 | |||
| 2227 | pub fn analyzeIsNull( | 2222 | pub fn analyzeIsNull( |
| 2228 | self: *Module, | 2223 | self: *Module, |
| 2229 | scope: *Scope, | 2224 | scope: *Scope, |
| ... | @@ -2476,6 +2471,24 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst | ... | @@ -2476,6 +2471,24 @@ pub fn coerce(self: *Module, scope: *Scope, dest_type: Type, inst: *Inst) !*Inst |
| 2476 | } | 2471 | } |
| 2477 | assert(inst.ty.zigTypeTag() != .Undefined); | 2472 | assert(inst.ty.zigTypeTag() != .Undefined); |
| 2478 | 2473 | ||
| 2474 | // null to ?T | ||
| 2475 | if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) { | ||
| 2476 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = Value.initTag(.null_value) }); | ||
| 2477 | } | ||
| 2478 | |||
| 2479 | // T to ?T | ||
| 2480 | if (dest_type.zigTypeTag() == .Optional) { | ||
| 2481 | const child_type = dest_type.elemType(); | ||
| 2482 | if (inst.value()) |val| { | ||
| 2483 | if (child_type.eql(inst.ty)) { | ||
| 2484 | return self.constInst(scope, inst.src, .{ .ty = dest_type, .val = val }); | ||
| 2485 | } | ||
| 2486 | return self.fail(scope, inst.src, "TODO optional wrap {} to {}", .{ val, dest_type }); | ||
| 2487 | } else if (child_type.eql(inst.ty)) { | ||
| 2488 | return self.fail(scope, inst.src, "TODO optional wrap {}", .{dest_type}); | ||
| 2489 | } | ||
| 2490 | } | ||
| 2491 | |||
| 2479 | // *[N]T to []T | 2492 | // *[N]T to []T |
| 2480 | if (inst.ty.isSinglePointer() and dest_type.isSlice() and | 2493 | if (inst.ty.isSinglePointer() and dest_type.isSlice() and |
| 2481 | (!inst.ty.isConstPtr() or dest_type.isConstPtr())) | 2494 | (!inst.ty.isConstPtr() or dest_type.isConstPtr())) |
| ... | @@ -2884,3 +2897,70 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { | ... | @@ -2884,3 +2897,70 @@ pub fn dumpInst(self: *Module, scope: *Scope, inst: *Inst) void { |
| 2884 | }); | 2897 | }); |
| 2885 | } | 2898 | } |
| 2886 | } | 2899 | } |
| 2900 | |||
| 2901 | pub const PanicId = enum { | ||
| 2902 | unreach, | ||
| 2903 | unwrap_null, | ||
| 2904 | }; | ||
| 2905 | |||
| 2906 | pub fn addSafetyCheck(mod: *Module, parent_block: *Scope.Block, ok: *Inst, panic_id: PanicId) !void { | ||
| 2907 | const block_inst = try parent_block.arena.create(Inst.Block); | ||
| 2908 | block_inst.* = .{ | ||
| 2909 | .base = .{ | ||
| 2910 | .tag = Inst.Block.base_tag, | ||
| 2911 | .ty = Type.initTag(.void), | ||
| 2912 | .src = ok.src, | ||
| 2913 | }, | ||
| 2914 | .body = .{ | ||
| 2915 | .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the condbr. | ||
| 2916 | }, | ||
| 2917 | }; | ||
| 2918 | |||
| 2919 | const ok_body: ir.Body = .{ | ||
| 2920 | .instructions = try parent_block.arena.alloc(*Inst, 1), // Only need space for the brvoid. | ||
| 2921 | }; | ||
| 2922 | const brvoid = try parent_block.arena.create(Inst.BrVoid); | ||
| 2923 | brvoid.* = .{ | ||
| 2924 | .base = .{ | ||
| 2925 | .tag = .brvoid, | ||
| 2926 | .ty = Type.initTag(.noreturn), | ||
| 2927 | .src = ok.src, | ||
| 2928 | }, | ||
| 2929 | .block = block_inst, | ||
| 2930 | }; | ||
| 2931 | ok_body.instructions[0] = &brvoid.base; | ||
| 2932 | |||
| 2933 | var fail_block: Scope.Block = .{ | ||
| 2934 | .parent = parent_block, | ||
| 2935 | .func = parent_block.func, | ||
| 2936 | .decl = parent_block.decl, | ||
| 2937 | .instructions = .{}, | ||
| 2938 | .arena = parent_block.arena, | ||
| 2939 | }; | ||
| 2940 | defer fail_block.instructions.deinit(mod.gpa); | ||
| 2941 | |||
| 2942 | _ = try mod.safetyPanic(&fail_block, ok.src, panic_id); | ||
| 2943 | |||
| 2944 | const fail_body: ir.Body = .{ .instructions = try parent_block.arena.dupe(*Inst, fail_block.instructions.items) }; | ||
| 2945 | |||
| 2946 | const condbr = try parent_block.arena.create(Inst.CondBr); | ||
| 2947 | condbr.* = .{ | ||
| 2948 | .base = .{ | ||
| 2949 | .tag = .condbr, | ||
| 2950 | .ty = Type.initTag(.noreturn), | ||
| 2951 | .src = ok.src, | ||
| 2952 | }, | ||
| 2953 | .condition = ok, | ||
| 2954 | .then_body = ok_body, | ||
| 2955 | .else_body = fail_body, | ||
| 2956 | }; | ||
| 2957 | block_inst.body.instructions[0] = &condbr.base; | ||
| 2958 | |||
| 2959 | try parent_block.instructions.append(mod.gpa, &block_inst.base); | ||
| 2960 | } | ||
| 2961 | |||
| 2962 | pub fn safetyPanic(mod: *Module, block: *Scope.Block, src: usize, panic_id: PanicId) !*Inst { | ||
| 2963 | // TODO Once we have a panic function to call, call it here instead of breakpoint. | ||
| 2964 | _ = try mod.addNoOp(block, src, Type.initTag(.void), .breakpoint); | ||
| 2965 | return mod.addNoOp(block, src, Type.initTag(.noreturn), .unreach); | ||
| 2966 | } |
src-self-hosted/astgen.zig+24| ... | @@ -105,6 +105,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr | ... | @@ -105,6 +105,8 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node) InnerEr |
| 105 | .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)), | 105 | .UndefinedLiteral => return rlWrap(mod, scope, rl, try undefLiteral(mod, scope, node.castTag(.UndefinedLiteral).?)), |
| 106 | .BoolLiteral => return rlWrap(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)), | 106 | .BoolLiteral => return rlWrap(mod, scope, rl, try boolLiteral(mod, scope, node.castTag(.BoolLiteral).?)), |
| 107 | .NullLiteral => return rlWrap(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)), | 107 | .NullLiteral => return rlWrap(mod, scope, rl, try nullLiteral(mod, scope, node.castTag(.NullLiteral).?)), |
| 108 | .OptionalType => return rlWrap(mod, scope, rl, try optionalType(mod, scope, node.castTag(.OptionalType).?)), | ||
| 109 | .UnwrapOptional => return unwrapOptional(mod, scope, rl, node.castTag(.UnwrapOptional).?), | ||
| 108 | else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}), | 110 | else => return mod.failNode(scope, node, "TODO implement astgen.Expr for {}", .{@tagName(node.tag)}), |
| 109 | } | 111 | } |
| 110 | } | 112 | } |
| ... | @@ -293,6 +295,28 @@ fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerErr | ... | @@ -293,6 +295,28 @@ fn boolNot(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerErr |
| 293 | return addZIRUnOp(mod, scope, src, .boolnot, operand); | 295 | return addZIRUnOp(mod, scope, src, .boolnot, operand); |
| 294 | } | 296 | } |
| 295 | 297 | ||
| 298 | fn optionalType(mod: *Module, scope: *Scope, node: *ast.Node.SimplePrefixOp) InnerError!*zir.Inst { | ||
| 299 | const tree = scope.tree(); | ||
| 300 | const src = tree.token_locs[node.op_token].start; | ||
| 301 | const meta_type = try addZIRInstConst(mod, scope, src, .{ | ||
| 302 | .ty = Type.initTag(.type), | ||
| 303 | .val = Value.initTag(.type_type), | ||
| 304 | }); | ||
| 305 | const operand = try expr(mod, scope, .{ .ty = meta_type }, node.rhs); | ||
| 306 | return addZIRUnOp(mod, scope, src, .optional_type, operand); | ||
| 307 | } | ||
| 308 | |||
| 309 | fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleSuffixOp) InnerError!*zir.Inst { | ||
| 310 | const tree = scope.tree(); | ||
| 311 | const src = tree.token_locs[node.rtoken].start; | ||
| 312 | |||
| 313 | const operand = try expr(mod, scope, .lvalue, node.lhs); | ||
| 314 | const unwrapped_ptr = try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand); | ||
| 315 | if (rl == .lvalue) return unwrapped_ptr; | ||
| 316 | |||
| 317 | return rlWrap(mod, scope, rl, try addZIRUnOp(mod, scope, src, .deref, unwrapped_ptr)); | ||
| 318 | } | ||
| 319 | |||
| 296 | /// Identifier token -> String (allocated in scope.arena()) | 320 | /// Identifier token -> String (allocated in scope.arena()) |
| 297 | pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { | 321 | pub fn identifierTokenString(mod: *Module, scope: *Scope, token: ast.TokenIndex) InnerError![]const u8 { |
| 298 | const tree = scope.tree(); | 322 | const tree = scope.tree(); |
src-self-hosted/codegen.zig+10| ... | @@ -668,6 +668,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -668,6 +668,7 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 668 | .store => return self.genStore(inst.castTag(.store).?), | 668 | .store => return self.genStore(inst.castTag(.store).?), |
| 669 | .sub => return self.genSub(inst.castTag(.sub).?), | 669 | .sub => return self.genSub(inst.castTag(.sub).?), |
| 670 | .unreach => return MCValue{ .unreach = {} }, | 670 | .unreach => return MCValue{ .unreach = {} }, |
| 671 | .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?), | ||
| 671 | } | 672 | } |
| 672 | } | 673 | } |
| 673 | 674 | ||
| ... | @@ -817,6 +818,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { | ... | @@ -817,6 +818,15 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 817 | } | 818 | } |
| 818 | } | 819 | } |
| 819 | 820 | ||
| 821 | fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue { | ||
| 822 | // No side effects, so if it's unreferenced, do nothing. | ||
| 823 | if (inst.base.isUnused()) | ||
| 824 | return MCValue.dead; | ||
| 825 | switch (arch) { | ||
| 826 | else => return self.fail(inst.base.src, "TODO implement unwrap optional for {}", .{self.target.cpu.arch}), | ||
| 827 | } | ||
| 828 | } | ||
| 829 | |||
| 820 | fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue { | 830 | fn genLoad(self: *Self, inst: *ir.Inst.UnOp) !MCValue { |
| 821 | const elem_ty = inst.base.ty; | 831 | const elem_ty = inst.base.ty; |
| 822 | if (!elem_ty.hasCodeGenBits()) | 832 | if (!elem_ty.hasCodeGenBits()) |
src-self-hosted/ir.zig+2-1| ... | @@ -82,6 +82,7 @@ pub const Inst = struct { | ... | @@ -82,6 +82,7 @@ pub const Inst = struct { |
| 82 | not, | 82 | not, |
| 83 | floatcast, | 83 | floatcast, |
| 84 | intcast, | 84 | intcast, |
| 85 | unwrap_optional, | ||
| 85 | 86 | ||
| 86 | pub fn Type(tag: Tag) type { | 87 | pub fn Type(tag: Tag) type { |
| 87 | return switch (tag) { | 88 | return switch (tag) { |
| ... | @@ -102,6 +103,7 @@ pub const Inst = struct { | ... | @@ -102,6 +103,7 @@ pub const Inst = struct { |
| 102 | .floatcast, | 103 | .floatcast, |
| 103 | .intcast, | 104 | .intcast, |
| 104 | .load, | 105 | .load, |
| 106 | .unwrap_optional, | ||
| 105 | => UnOp, | 107 | => UnOp, |
| 106 | 108 | ||
| 107 | .add, | 109 | .add, |
| ... | @@ -419,7 +421,6 @@ pub const Inst = struct { | ... | @@ -419,7 +421,6 @@ pub const Inst = struct { |
| 419 | return null; | 421 | return null; |
| 420 | } | 422 | } |
| 421 | }; | 423 | }; |
| 422 | |||
| 423 | }; | 424 | }; |
| 424 | 425 | ||
| 425 | pub const Body = struct { | 426 | pub const Body = struct { |
src-self-hosted/type.zig+161-26| ... | @@ -70,6 +70,11 @@ pub const Type = extern union { | ... | @@ -70,6 +70,11 @@ pub const Type = extern union { |
| 70 | .single_mut_pointer => return .Pointer, | 70 | .single_mut_pointer => return .Pointer, |
| 71 | .single_const_pointer_to_comptime_int => return .Pointer, | 71 | .single_const_pointer_to_comptime_int => return .Pointer, |
| 72 | .const_slice_u8 => return .Pointer, | 72 | .const_slice_u8 => return .Pointer, |
| 73 | |||
| 74 | .optional, | ||
| 75 | .optional_single_const_pointer, | ||
| 76 | .optional_single_mut_pointer, | ||
| 77 | => return .Optional, | ||
| 73 | } | 78 | } |
| 74 | } | 79 | } |
| 75 | 80 | ||
| ... | @@ -179,9 +184,11 @@ pub const Type = extern union { | ... | @@ -179,9 +184,11 @@ pub const Type = extern union { |
| 179 | } | 184 | } |
| 180 | return true; | 185 | return true; |
| 181 | }, | 186 | }, |
| 187 | .Optional => { | ||
| 188 | return a.elemType().eql(b.elemType()); | ||
| 189 | }, | ||
| 182 | .Float, | 190 | .Float, |
| 183 | .Struct, | 191 | .Struct, |
| 184 | .Optional, | ||
| 185 | .ErrorUnion, | 192 | .ErrorUnion, |
| 186 | .ErrorSet, | 193 | .ErrorSet, |
| 187 | .Enum, | 194 | .Enum, |
| ... | @@ -241,9 +248,11 @@ pub const Type = extern union { | ... | @@ -241,9 +248,11 @@ pub const Type = extern union { |
| 241 | std.hash.autoHash(&hasher, self.fnParamType(i).hash()); | 248 | std.hash.autoHash(&hasher, self.fnParamType(i).hash()); |
| 242 | } | 249 | } |
| 243 | }, | 250 | }, |
| 251 | .Optional => { | ||
| 252 | std.hash.autoHash(&hasher, self.elemType().hash()); | ||
| 253 | }, | ||
| 244 | .Float, | 254 | .Float, |
| 245 | .Struct, | 255 | .Struct, |
| 246 | .Optional, | ||
| 247 | .ErrorUnion, | 256 | .ErrorUnion, |
| 248 | .ErrorSet, | 257 | .ErrorSet, |
| 249 | .Enum, | 258 | .Enum, |
| ... | @@ -317,24 +326,8 @@ pub const Type = extern union { | ... | @@ -317,24 +326,8 @@ pub const Type = extern union { |
| 317 | }; | 326 | }; |
| 318 | return Type{ .ptr_otherwise = &new_payload.base }; | 327 | return Type{ .ptr_otherwise = &new_payload.base }; |
| 319 | }, | 328 | }, |
| 320 | .single_const_pointer => { | 329 | .single_const_pointer => return self.copyPayloadSingleField(allocator, Payload.SingleConstPointer, "pointee_type"), |
| 321 | const payload = @fieldParentPtr(Payload.SingleConstPointer, "base", self.ptr_otherwise); | 330 | .single_mut_pointer => return self.copyPayloadSingleField(allocator, Payload.SingleMutPointer, "pointee_type"), |
| 322 | const new_payload = try allocator.create(Payload.SingleConstPointer); | ||
| 323 | new_payload.* = .{ | ||
| 324 | .base = payload.base, | ||
| 325 | .pointee_type = try payload.pointee_type.copy(allocator), | ||
| 326 | }; | ||
| 327 | return Type{ .ptr_otherwise = &new_payload.base }; | ||
| 328 | }, | ||
| 329 | .single_mut_pointer => { | ||
| 330 | const payload = @fieldParentPtr(Payload.SingleMutPointer, "base", self.ptr_otherwise); | ||
| 331 | const new_payload = try allocator.create(Payload.SingleMutPointer); | ||
| 332 | new_payload.* = .{ | ||
| 333 | .base = payload.base, | ||
| 334 | .pointee_type = try payload.pointee_type.copy(allocator), | ||
| 335 | }; | ||
| 336 | return Type{ .ptr_otherwise = &new_payload.base }; | ||
| 337 | }, | ||
| 338 | .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned), | 331 | .int_signed => return self.copyPayloadShallow(allocator, Payload.IntSigned), |
| 339 | .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned), | 332 | .int_unsigned => return self.copyPayloadShallow(allocator, Payload.IntUnsigned), |
| 340 | .function => { | 333 | .function => { |
| ... | @@ -352,6 +345,9 @@ pub const Type = extern union { | ... | @@ -352,6 +345,9 @@ pub const Type = extern union { |
| 352 | }; | 345 | }; |
| 353 | return Type{ .ptr_otherwise = &new_payload.base }; | 346 | return Type{ .ptr_otherwise = &new_payload.base }; |
| 354 | }, | 347 | }, |
| 348 | .optional => return self.copyPayloadSingleField(allocator, Payload.Optional, "child_type"), | ||
| 349 | .optional_single_mut_pointer => return self.copyPayloadSingleField(allocator, Payload.OptionalSingleMutPointer, "pointee_type"), | ||
| 350 | .optional_single_const_pointer => return self.copyPayloadSingleField(allocator, Payload.OptionalSingleConstPointer, "pointee_type"), | ||
| 355 | } | 351 | } |
| 356 | } | 352 | } |
| 357 | 353 | ||
| ... | @@ -362,6 +358,14 @@ pub const Type = extern union { | ... | @@ -362,6 +358,14 @@ pub const Type = extern union { |
| 362 | return Type{ .ptr_otherwise = &new_payload.base }; | 358 | return Type{ .ptr_otherwise = &new_payload.base }; |
| 363 | } | 359 | } |
| 364 | 360 | ||
| 361 | fn copyPayloadSingleField(self: Type, allocator: *Allocator, comptime T: type, comptime field_name: []const u8) error{OutOfMemory}!Type { | ||
| 362 | const payload = @fieldParentPtr(T, "base", self.ptr_otherwise); | ||
| 363 | const new_payload = try allocator.create(T); | ||
| 364 | new_payload.base = payload.base; | ||
| 365 | @field(new_payload, field_name) = try @field(payload, field_name).copy(allocator); | ||
| 366 | return Type{ .ptr_otherwise = &new_payload.base }; | ||
| 367 | } | ||
| 368 | |||
| 365 | pub fn format( | 369 | pub fn format( |
| 366 | self: Type, | 370 | self: Type, |
| 367 | comptime fmt: []const u8, | 371 | comptime fmt: []const u8, |
| ... | @@ -456,6 +460,24 @@ pub const Type = extern union { | ... | @@ -456,6 +460,24 @@ pub const Type = extern union { |
| 456 | const payload = @fieldParentPtr(Payload.IntUnsigned, "base", ty.ptr_otherwise); | 460 | const payload = @fieldParentPtr(Payload.IntUnsigned, "base", ty.ptr_otherwise); |
| 457 | return out_stream.print("u{}", .{payload.bits}); | 461 | return out_stream.print("u{}", .{payload.bits}); |
| 458 | }, | 462 | }, |
| 463 | .optional => { | ||
| 464 | const payload = @fieldParentPtr(Payload.Optional, "base", ty.ptr_otherwise); | ||
| 465 | try out_stream.writeByte('?'); | ||
| 466 | ty = payload.child_type; | ||
| 467 | continue; | ||
| 468 | }, | ||
| 469 | .optional_single_const_pointer => { | ||
| 470 | const payload = @fieldParentPtr(Payload.OptionalSingleConstPointer, "base", ty.ptr_otherwise); | ||
| 471 | try out_stream.writeAll("?*const "); | ||
| 472 | ty = payload.pointee_type; | ||
| 473 | continue; | ||
| 474 | }, | ||
| 475 | .optional_single_mut_pointer => { | ||
| 476 | const payload = @fieldParentPtr(Payload.OptionalSingleMutPointer, "base", ty.ptr_otherwise); | ||
| 477 | try out_stream.writeAll("?*"); | ||
| 478 | ty = payload.pointee_type; | ||
| 479 | continue; | ||
| 480 | }, | ||
| 459 | } | 481 | } |
| 460 | unreachable; | 482 | unreachable; |
| 461 | } | 483 | } |
| ... | @@ -545,12 +567,16 @@ pub const Type = extern union { | ... | @@ -545,12 +567,16 @@ pub const Type = extern union { |
| 545 | .single_const_pointer_to_comptime_int, | 567 | .single_const_pointer_to_comptime_int, |
| 546 | .const_slice_u8, | 568 | .const_slice_u8, |
| 547 | .array_u8_sentinel_0, | 569 | .array_u8_sentinel_0, |
| 548 | .array, // TODO check for zero bits | 570 | .optional, |
| 549 | .single_const_pointer, | 571 | .optional_single_mut_pointer, |
| 550 | .single_mut_pointer, | 572 | .optional_single_const_pointer, |
| 551 | .int_signed, // TODO check for zero bits | ||
| 552 | .int_unsigned, // TODO check for zero bits | ||
| 553 | => true, | 573 | => true, |
| 574 | // TODO lazy types | ||
| 575 | .array => self.elemType().hasCodeGenBits() and self.arrayLen() != 0, | ||
| 576 | .single_const_pointer => self.elemType().hasCodeGenBits(), | ||
| 577 | .single_mut_pointer => self.elemType().hasCodeGenBits(), | ||
| 578 | .int_signed => self.cast(Payload.IntSigned).?.bits == 0, | ||
| 579 | .int_unsigned => self.cast(Payload.IntUnsigned).?.bits == 0, | ||
| 554 | 580 | ||
| 555 | .c_void, | 581 | .c_void, |
| 556 | .void, | 582 | .void, |
| ... | @@ -597,6 +623,8 @@ pub const Type = extern union { | ... | @@ -597,6 +623,8 @@ pub const Type = extern union { |
| 597 | .const_slice_u8, | 623 | .const_slice_u8, |
| 598 | .single_const_pointer, | 624 | .single_const_pointer, |
| 599 | .single_mut_pointer, | 625 | .single_mut_pointer, |
| 626 | .optional_single_const_pointer, | ||
| 627 | .optional_single_mut_pointer, | ||
| 600 | => return @divExact(target.cpu.arch.ptrBitWidth(), 8), | 628 | => return @divExact(target.cpu.arch.ptrBitWidth(), 8), |
| 601 | 629 | ||
| 602 | .c_short => return @divExact(CType.short.sizeInBits(target), 8), | 630 | .c_short => return @divExact(CType.short.sizeInBits(target), 8), |
| ... | @@ -629,6 +657,16 @@ pub const Type = extern union { | ... | @@ -629,6 +657,16 @@ pub const Type = extern union { |
| 629 | return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8); | 657 | return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8); |
| 630 | }, | 658 | }, |
| 631 | 659 | ||
| 660 | .optional => { | ||
| 661 | const child_type = self.cast(Payload.Optional).?.child_type; | ||
| 662 | if (!child_type.hasCodeGenBits()) return 1; | ||
| 663 | |||
| 664 | if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr()) | ||
| 665 | return @divExact(target.cpu.arch.ptrBitWidth(), 8); | ||
| 666 | |||
| 667 | return child_type.abiAlignment(target); | ||
| 668 | }, | ||
| 669 | |||
| 632 | .c_void, | 670 | .c_void, |
| 633 | .void, | 671 | .void, |
| 634 | .type, | 672 | .type, |
| ... | @@ -679,6 +717,8 @@ pub const Type = extern union { | ... | @@ -679,6 +717,8 @@ pub const Type = extern union { |
| 679 | .const_slice_u8, | 717 | .const_slice_u8, |
| 680 | .single_const_pointer, | 718 | .single_const_pointer, |
| 681 | .single_mut_pointer, | 719 | .single_mut_pointer, |
| 720 | .optional_single_const_pointer, | ||
| 721 | .optional_single_mut_pointer, | ||
| 682 | => return @divExact(target.cpu.arch.ptrBitWidth(), 8), | 722 | => return @divExact(target.cpu.arch.ptrBitWidth(), 8), |
| 683 | 723 | ||
| 684 | .c_short => return @divExact(CType.short.sizeInBits(target), 8), | 724 | .c_short => return @divExact(CType.short.sizeInBits(target), 8), |
| ... | @@ -708,6 +748,20 @@ pub const Type = extern union { | ... | @@ -708,6 +748,20 @@ pub const Type = extern union { |
| 708 | 748 | ||
| 709 | return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8); | 749 | return std.math.ceilPowerOfTwoPromote(u16, (bits + 7) / 8); |
| 710 | }, | 750 | }, |
| 751 | |||
| 752 | .optional => { | ||
| 753 | const child_type = self.cast(Payload.Optional).?.child_type; | ||
| 754 | if (!child_type.hasCodeGenBits()) return 1; | ||
| 755 | |||
| 756 | if (child_type.zigTypeTag() == .Pointer and !child_type.isCPtr()) | ||
| 757 | return @divExact(target.cpu.arch.ptrBitWidth(), 8); | ||
| 758 | |||
| 759 | // Optional types are represented as a struct with the child type as the first | ||
| 760 | // field and a boolean as the second. Since the child type's abi alignment is | ||
| 761 | // guaranteed to be >= that of bool's (1 byte) the added size is exactly equal | ||
| 762 | // to the child type's ABI alignment. | ||
| 763 | return child_type.abiAlignment(target) + child_type.abiSize(target); | ||
| 764 | }, | ||
| 711 | }; | 765 | }; |
| 712 | } | 766 | } |
| 713 | 767 | ||
| ... | @@ -756,6 +810,9 @@ pub const Type = extern union { | ... | @@ -756,6 +810,9 @@ pub const Type = extern union { |
| 756 | .function, | 810 | .function, |
| 757 | .int_unsigned, | 811 | .int_unsigned, |
| 758 | .int_signed, | 812 | .int_signed, |
| 813 | .optional, | ||
| 814 | .optional_single_mut_pointer, | ||
| 815 | .optional_single_const_pointer, | ||
| 759 | => false, | 816 | => false, |
| 760 | 817 | ||
| 761 | .single_const_pointer, | 818 | .single_const_pointer, |
| ... | @@ -812,6 +869,9 @@ pub const Type = extern union { | ... | @@ -812,6 +869,9 @@ pub const Type = extern union { |
| 812 | .function, | 869 | .function, |
| 813 | .int_unsigned, | 870 | .int_unsigned, |
| 814 | .int_signed, | 871 | .int_signed, |
| 872 | .optional, | ||
| 873 | .optional_single_mut_pointer, | ||
| 874 | .optional_single_const_pointer, | ||
| 815 | => false, | 875 | => false, |
| 816 | 876 | ||
| 817 | .const_slice_u8 => true, | 877 | .const_slice_u8 => true, |
| ... | @@ -863,6 +923,9 @@ pub const Type = extern union { | ... | @@ -863,6 +923,9 @@ pub const Type = extern union { |
| 863 | .int_unsigned, | 923 | .int_unsigned, |
| 864 | .int_signed, | 924 | .int_signed, |
| 865 | .single_mut_pointer, | 925 | .single_mut_pointer, |
| 926 | .optional, | ||
| 927 | .optional_single_mut_pointer, | ||
| 928 | .optional_single_const_pointer, | ||
| 866 | => false, | 929 | => false, |
| 867 | 930 | ||
| 868 | .single_const_pointer, | 931 | .single_const_pointer, |
| ... | @@ -920,11 +983,14 @@ pub const Type = extern union { | ... | @@ -920,11 +983,14 @@ pub const Type = extern union { |
| 920 | .single_const_pointer, | 983 | .single_const_pointer, |
| 921 | .single_const_pointer_to_comptime_int, | 984 | .single_const_pointer_to_comptime_int, |
| 922 | .const_slice_u8, | 985 | .const_slice_u8, |
| 986 | .optional, | ||
| 987 | .optional_single_mut_pointer, | ||
| 988 | .optional_single_const_pointer, | ||
| 923 | => false, | 989 | => false, |
| 924 | }; | 990 | }; |
| 925 | } | 991 | } |
| 926 | 992 | ||
| 927 | /// Asserts the type is a pointer or array type. | 993 | /// Asserts the type is a pointer, optional or array type. |
| 928 | pub fn elemType(self: Type) Type { | 994 | pub fn elemType(self: Type) Type { |
| 929 | return switch (self.tag()) { | 995 | return switch (self.tag()) { |
| 930 | .u8, | 996 | .u8, |
| ... | @@ -974,6 +1040,9 @@ pub const Type = extern union { | ... | @@ -974,6 +1040,9 @@ pub const Type = extern union { |
| 974 | .single_mut_pointer => self.cast(Payload.SingleMutPointer).?.pointee_type, | 1040 | .single_mut_pointer => self.cast(Payload.SingleMutPointer).?.pointee_type, |
| 975 | .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8), | 1041 | .array_u8_sentinel_0, .const_slice_u8 => Type.initTag(.u8), |
| 976 | .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int), | 1042 | .single_const_pointer_to_comptime_int => Type.initTag(.comptime_int), |
| 1043 | .optional => self.cast(Payload.Optional).?.child_type, | ||
| 1044 | .optional_single_mut_pointer => self.cast(Payload.OptionalSingleMutPointer).?.pointee_type, | ||
| 1045 | .optional_single_const_pointer => self.cast(Payload.OptionalSingleConstPointer).?.pointee_type, | ||
| 977 | }; | 1046 | }; |
| 978 | } | 1047 | } |
| 979 | 1048 | ||
| ... | @@ -1024,6 +1093,9 @@ pub const Type = extern union { | ... | @@ -1024,6 +1093,9 @@ pub const Type = extern union { |
| 1024 | .const_slice_u8, | 1093 | .const_slice_u8, |
| 1025 | .int_unsigned, | 1094 | .int_unsigned, |
| 1026 | .int_signed, | 1095 | .int_signed, |
| 1096 | .optional, | ||
| 1097 | .optional_single_mut_pointer, | ||
| 1098 | .optional_single_const_pointer, | ||
| 1027 | => unreachable, | 1099 | => unreachable, |
| 1028 | 1100 | ||
| 1029 | .array => self.cast(Payload.Array).?.len, | 1101 | .array => self.cast(Payload.Array).?.len, |
| ... | @@ -1078,6 +1150,9 @@ pub const Type = extern union { | ... | @@ -1078,6 +1150,9 @@ pub const Type = extern union { |
| 1078 | .const_slice_u8, | 1150 | .const_slice_u8, |
| 1079 | .int_unsigned, | 1151 | .int_unsigned, |
| 1080 | .int_signed, | 1152 | .int_signed, |
| 1153 | .optional, | ||
| 1154 | .optional_single_mut_pointer, | ||
| 1155 | .optional_single_const_pointer, | ||
| 1081 | => unreachable, | 1156 | => unreachable, |
| 1082 | 1157 | ||
| 1083 | .array => return null, | 1158 | .array => return null, |
| ... | @@ -1129,6 +1204,9 @@ pub const Type = extern union { | ... | @@ -1129,6 +1204,9 @@ pub const Type = extern union { |
| 1129 | .u16, | 1204 | .u16, |
| 1130 | .u32, | 1205 | .u32, |
| 1131 | .u64, | 1206 | .u64, |
| 1207 | .optional, | ||
| 1208 | .optional_single_mut_pointer, | ||
| 1209 | .optional_single_const_pointer, | ||
| 1132 | => false, | 1210 | => false, |
| 1133 | 1211 | ||
| 1134 | .int_signed, | 1212 | .int_signed, |
| ... | @@ -1184,6 +1262,9 @@ pub const Type = extern union { | ... | @@ -1184,6 +1262,9 @@ pub const Type = extern union { |
| 1184 | .i16, | 1262 | .i16, |
| 1185 | .i32, | 1263 | .i32, |
| 1186 | .i64, | 1264 | .i64, |
| 1265 | .optional, | ||
| 1266 | .optional_single_mut_pointer, | ||
| 1267 | .optional_single_const_pointer, | ||
| 1187 | => false, | 1268 | => false, |
| 1188 | 1269 | ||
| 1189 | .int_unsigned, | 1270 | .int_unsigned, |
| ... | @@ -1229,6 +1310,9 @@ pub const Type = extern union { | ... | @@ -1229,6 +1310,9 @@ pub const Type = extern union { |
| 1229 | .single_const_pointer_to_comptime_int, | 1310 | .single_const_pointer_to_comptime_int, |
| 1230 | .array_u8_sentinel_0, | 1311 | .array_u8_sentinel_0, |
| 1231 | .const_slice_u8, | 1312 | .const_slice_u8, |
| 1313 | .optional, | ||
| 1314 | .optional_single_mut_pointer, | ||
| 1315 | .optional_single_const_pointer, | ||
| 1232 | => unreachable, | 1316 | => unreachable, |
| 1233 | 1317 | ||
| 1234 | .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits }, | 1318 | .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits }, |
| ... | @@ -1292,6 +1376,9 @@ pub const Type = extern union { | ... | @@ -1292,6 +1376,9 @@ pub const Type = extern union { |
| 1292 | .i32, | 1376 | .i32, |
| 1293 | .u64, | 1377 | .u64, |
| 1294 | .i64, | 1378 | .i64, |
| 1379 | .optional, | ||
| 1380 | .optional_single_mut_pointer, | ||
| 1381 | .optional_single_const_pointer, | ||
| 1295 | => false, | 1382 | => false, |
| 1296 | 1383 | ||
| 1297 | .usize, | 1384 | .usize, |
| ... | @@ -1384,6 +1471,9 @@ pub const Type = extern union { | ... | @@ -1384,6 +1471,9 @@ pub const Type = extern union { |
| 1384 | .c_ulonglong, | 1471 | .c_ulonglong, |
| 1385 | .int_unsigned, | 1472 | .int_unsigned, |
| 1386 | .int_signed, | 1473 | .int_signed, |
| 1474 | .optional, | ||
| 1475 | .optional_single_mut_pointer, | ||
| 1476 | .optional_single_const_pointer, | ||
| 1387 | => unreachable, | 1477 | => unreachable, |
| 1388 | }; | 1478 | }; |
| 1389 | } | 1479 | } |
| ... | @@ -1442,6 +1532,9 @@ pub const Type = extern union { | ... | @@ -1442,6 +1532,9 @@ pub const Type = extern union { |
| 1442 | .c_ulonglong, | 1532 | .c_ulonglong, |
| 1443 | .int_unsigned, | 1533 | .int_unsigned, |
| 1444 | .int_signed, | 1534 | .int_signed, |
| 1535 | .optional, | ||
| 1536 | .optional_single_mut_pointer, | ||
| 1537 | .optional_single_const_pointer, | ||
| 1445 | => unreachable, | 1538 | => unreachable, |
| 1446 | } | 1539 | } |
| 1447 | } | 1540 | } |
| ... | @@ -1499,6 +1592,9 @@ pub const Type = extern union { | ... | @@ -1499,6 +1592,9 @@ pub const Type = extern union { |
| 1499 | .c_ulonglong, | 1592 | .c_ulonglong, |
| 1500 | .int_unsigned, | 1593 | .int_unsigned, |
| 1501 | .int_signed, | 1594 | .int_signed, |
| 1595 | .optional, | ||
| 1596 | .optional_single_mut_pointer, | ||
| 1597 | .optional_single_const_pointer, | ||
| 1502 | => unreachable, | 1598 | => unreachable, |
| 1503 | } | 1599 | } |
| 1504 | } | 1600 | } |
| ... | @@ -1556,6 +1652,9 @@ pub const Type = extern union { | ... | @@ -1556,6 +1652,9 @@ pub const Type = extern union { |
| 1556 | .c_ulonglong, | 1652 | .c_ulonglong, |
| 1557 | .int_unsigned, | 1653 | .int_unsigned, |
| 1558 | .int_signed, | 1654 | .int_signed, |
| 1655 | .optional, | ||
| 1656 | .optional_single_mut_pointer, | ||
| 1657 | .optional_single_const_pointer, | ||
| 1559 | => unreachable, | 1658 | => unreachable, |
| 1560 | }; | 1659 | }; |
| 1561 | } | 1660 | } |
| ... | @@ -1610,6 +1709,9 @@ pub const Type = extern union { | ... | @@ -1610,6 +1709,9 @@ pub const Type = extern union { |
| 1610 | .c_ulonglong, | 1709 | .c_ulonglong, |
| 1611 | .int_unsigned, | 1710 | .int_unsigned, |
| 1612 | .int_signed, | 1711 | .int_signed, |
| 1712 | .optional, | ||
| 1713 | .optional_single_mut_pointer, | ||
| 1714 | .optional_single_const_pointer, | ||
| 1613 | => unreachable, | 1715 | => unreachable, |
| 1614 | }; | 1716 | }; |
| 1615 | } | 1717 | } |
| ... | @@ -1664,6 +1766,9 @@ pub const Type = extern union { | ... | @@ -1664,6 +1766,9 @@ pub const Type = extern union { |
| 1664 | .c_ulonglong, | 1766 | .c_ulonglong, |
| 1665 | .int_unsigned, | 1767 | .int_unsigned, |
| 1666 | .int_signed, | 1768 | .int_signed, |
| 1769 | .optional, | ||
| 1770 | .optional_single_mut_pointer, | ||
| 1771 | .optional_single_const_pointer, | ||
| 1667 | => unreachable, | 1772 | => unreachable, |
| 1668 | }; | 1773 | }; |
| 1669 | } | 1774 | } |
| ... | @@ -1718,6 +1823,9 @@ pub const Type = extern union { | ... | @@ -1718,6 +1823,9 @@ pub const Type = extern union { |
| 1718 | .single_const_pointer_to_comptime_int, | 1823 | .single_const_pointer_to_comptime_int, |
| 1719 | .array_u8_sentinel_0, | 1824 | .array_u8_sentinel_0, |
| 1720 | .const_slice_u8, | 1825 | .const_slice_u8, |
| 1826 | .optional, | ||
| 1827 | .optional_single_mut_pointer, | ||
| 1828 | .optional_single_const_pointer, | ||
| 1721 | => false, | 1829 | => false, |
| 1722 | }; | 1830 | }; |
| 1723 | } | 1831 | } |
| ... | @@ -1762,6 +1870,9 @@ pub const Type = extern union { | ... | @@ -1762,6 +1870,9 @@ pub const Type = extern union { |
| 1762 | .array_u8_sentinel_0, | 1870 | .array_u8_sentinel_0, |
| 1763 | .const_slice_u8, | 1871 | .const_slice_u8, |
| 1764 | .c_void, | 1872 | .c_void, |
| 1873 | .optional, | ||
| 1874 | .optional_single_mut_pointer, | ||
| 1875 | .optional_single_const_pointer, | ||
| 1765 | => return null, | 1876 | => return null, |
| 1766 | 1877 | ||
| 1767 | .void => return Value.initTag(.void_value), | 1878 | .void => return Value.initTag(.void_value), |
| ... | @@ -1851,6 +1962,9 @@ pub const Type = extern union { | ... | @@ -1851,6 +1962,9 @@ pub const Type = extern union { |
| 1851 | .array, | 1962 | .array, |
| 1852 | .single_const_pointer, | 1963 | .single_const_pointer, |
| 1853 | .single_mut_pointer, | 1964 | .single_mut_pointer, |
| 1965 | .optional, | ||
| 1966 | .optional_single_mut_pointer, | ||
| 1967 | .optional_single_const_pointer, | ||
| 1854 | => return false, | 1968 | => return false, |
| 1855 | }; | 1969 | }; |
| 1856 | } | 1970 | } |
| ... | @@ -1911,6 +2025,9 @@ pub const Type = extern union { | ... | @@ -1911,6 +2025,9 @@ pub const Type = extern union { |
| 1911 | int_signed, | 2025 | int_signed, |
| 1912 | int_unsigned, | 2026 | int_unsigned, |
| 1913 | function, | 2027 | function, |
| 2028 | optional, | ||
| 2029 | optional_single_mut_pointer, | ||
| 2030 | optional_single_const_pointer, | ||
| 1914 | 2031 | ||
| 1915 | pub const last_no_payload_tag = Tag.const_slice_u8; | 2032 | pub const last_no_payload_tag = Tag.const_slice_u8; |
| 1916 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; | 2033 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| ... | @@ -1963,6 +2080,24 @@ pub const Type = extern union { | ... | @@ -1963,6 +2080,24 @@ pub const Type = extern union { |
| 1963 | return_type: Type, | 2080 | return_type: Type, |
| 1964 | cc: std.builtin.CallingConvention, | 2081 | cc: std.builtin.CallingConvention, |
| 1965 | }; | 2082 | }; |
| 2083 | |||
| 2084 | pub const Optional = struct { | ||
| 2085 | base: Payload = Payload{ .tag = .optional }, | ||
| 2086 | |||
| 2087 | child_type: Type, | ||
| 2088 | }; | ||
| 2089 | |||
| 2090 | pub const OptionalSingleConstPointer = struct { | ||
| 2091 | base: Payload = Payload{ .tag = .optional_single_const_pointer }, | ||
| 2092 | |||
| 2093 | pointee_type: Type, | ||
| 2094 | }; | ||
| 2095 | |||
| 2096 | pub const OptionalSingleMutPointer = struct { | ||
| 2097 | base: Payload = Payload{ .tag = .optional_single_mut_pointer }, | ||
| 2098 | |||
| 2099 | pointee_type: Type, | ||
| 2100 | }; | ||
| 1966 | }; | 2101 | }; |
| 1967 | }; | 2102 | }; |
| 1968 | 2103 |
src-self-hosted/zir.zig+27| ... | @@ -212,6 +212,12 @@ pub const Inst = struct { | ... | @@ -212,6 +212,12 @@ pub const Inst = struct { |
| 212 | @"unreachable", | 212 | @"unreachable", |
| 213 | /// Bitwise XOR. `^` | 213 | /// Bitwise XOR. `^` |
| 214 | xor, | 214 | xor, |
| 215 | /// Create an optional type '?T' | ||
| 216 | optional_type, | ||
| 217 | /// Unwraps an optional value 'lhs.?' | ||
| 218 | unwrap_optional_safe, | ||
| 219 | /// Same as previous, but without safety checks. Used for orelse, if and while | ||
| 220 | unwrap_optional_unsafe, | ||
| 215 | 221 | ||
| 216 | pub fn Type(tag: Tag) type { | 222 | pub fn Type(tag: Tag) type { |
| 217 | return switch (tag) { | 223 | return switch (tag) { |
| ... | @@ -240,6 +246,9 @@ pub const Inst = struct { | ... | @@ -240,6 +246,9 @@ pub const Inst = struct { |
| 240 | .typeof, | 246 | .typeof, |
| 241 | .single_const_ptr_type, | 247 | .single_const_ptr_type, |
| 242 | .single_mut_ptr_type, | 248 | .single_mut_ptr_type, |
| 249 | .optional_type, | ||
| 250 | .unwrap_optional_safe, | ||
| 251 | .unwrap_optional_unsafe, | ||
| 243 | => UnOp, | 252 | => UnOp, |
| 244 | 253 | ||
| 245 | .add, | 254 | .add, |
| ... | @@ -372,6 +381,9 @@ pub const Inst = struct { | ... | @@ -372,6 +381,9 @@ pub const Inst = struct { |
| 372 | .subwrap, | 381 | .subwrap, |
| 373 | .typeof, | 382 | .typeof, |
| 374 | .xor, | 383 | .xor, |
| 384 | .optional_type, | ||
| 385 | .unwrap_optional_safe, | ||
| 386 | .unwrap_optional_unsafe, | ||
| 375 | => false, | 387 | => false, |
| 376 | 388 | ||
| 377 | .@"break", | 389 | .@"break", |
| ... | @@ -1915,6 +1927,7 @@ const EmitZIR = struct { | ... | @@ -1915,6 +1927,7 @@ const EmitZIR = struct { |
| 1915 | .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull), | 1927 | .isnonnull => try self.emitUnOp(inst.src, new_body, inst.castTag(.isnonnull).?, .isnonnull), |
| 1916 | .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref), | 1928 | .load => try self.emitUnOp(inst.src, new_body, inst.castTag(.load).?, .deref), |
| 1917 | .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref), | 1929 | .ref => try self.emitUnOp(inst.src, new_body, inst.castTag(.ref).?, .ref), |
| 1930 | .unwrap_optional => try self.emitUnOp(inst.src, new_body, inst.castTag(.unwrap_optional).?, .unwrap_optional_unsafe), | ||
| 1918 | 1931 | ||
| 1919 | .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add), | 1932 | .add => try self.emitBinOp(inst.src, new_body, inst.castTag(.add).?, .add), |
| 1920 | .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub), | 1933 | .sub => try self.emitBinOp(inst.src, new_body, inst.castTag(.sub).?, .sub), |
| ... | @@ -2242,6 +2255,20 @@ const EmitZIR = struct { | ... | @@ -2242,6 +2255,20 @@ const EmitZIR = struct { |
| 2242 | std.debug.panic("TODO implement emitType for {}", .{ty}); | 2255 | std.debug.panic("TODO implement emitType for {}", .{ty}); |
| 2243 | } | 2256 | } |
| 2244 | }, | 2257 | }, |
| 2258 | .Optional => { | ||
| 2259 | const inst = try self.arena.allocator.create(Inst.UnOp); | ||
| 2260 | inst.* = .{ | ||
| 2261 | .base = .{ | ||
| 2262 | .src = src, | ||
| 2263 | .tag = .optional_type, | ||
| 2264 | }, | ||
| 2265 | .positionals = .{ | ||
| 2266 | .operand = (try self.emitType(src, ty.elemType())).inst, | ||
| 2267 | }, | ||
| 2268 | .kw_args = .{}, | ||
| 2269 | }; | ||
| 2270 | return self.emitUnnamedDecl(&inst.base); | ||
| 2271 | }, | ||
| 2245 | else => std.debug.panic("TODO implement emitType for {}", .{ty}), | 2272 | else => std.debug.panic("TODO implement emitType for {}", .{ty}), |
| 2246 | }, | 2273 | }, |
| 2247 | } | 2274 | } |
src-self-hosted/zir_sema.zig+87-12| ... | @@ -68,8 +68,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! | ... | @@ -68,8 +68,8 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! |
| 68 | .deref => return analyzeInstDeref(mod, scope, old_inst.castTag(.deref).?), | 68 | .deref => return analyzeInstDeref(mod, scope, old_inst.castTag(.deref).?), |
| 69 | .as => return analyzeInstAs(mod, scope, old_inst.castTag(.as).?), | 69 | .as => return analyzeInstAs(mod, scope, old_inst.castTag(.as).?), |
| 70 | .@"asm" => return analyzeInstAsm(mod, scope, old_inst.castTag(.@"asm").?), | 70 | .@"asm" => return analyzeInstAsm(mod, scope, old_inst.castTag(.@"asm").?), |
| 71 | .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?), | 71 | .@"unreachable" => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.@"unreachable").?, true), |
| 72 | .unreach_nocheck => return analyzeInstUnreachNoChk(mod, scope, old_inst.castTag(.unreach_nocheck).?), | 72 | .unreach_nocheck => return analyzeInstUnreachable(mod, scope, old_inst.castTag(.unreach_nocheck).?, false), |
| 73 | .@"return" => return analyzeInstRet(mod, scope, old_inst.castTag(.@"return").?), | 73 | .@"return" => return analyzeInstRet(mod, scope, old_inst.castTag(.@"return").?), |
| 74 | .returnvoid => return analyzeInstRetVoid(mod, scope, old_inst.castTag(.returnvoid).?), | 74 | .returnvoid => return analyzeInstRetVoid(mod, scope, old_inst.castTag(.returnvoid).?), |
| 75 | .@"fn" => return analyzeInstFn(mod, scope, old_inst.castTag(.@"fn").?), | 75 | .@"fn" => return analyzeInstFn(mod, scope, old_inst.castTag(.@"fn").?), |
| ... | @@ -106,6 +106,9 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! | ... | @@ -106,6 +106,9 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! |
| 106 | .isnonnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnonnull).?, false), | 106 | .isnonnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnonnull).?, false), |
| 107 | .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?), | 107 | .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?), |
| 108 | .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?), | 108 | .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?), |
| 109 | .optional_type => return analyzeInstOptionalType(mod, scope, old_inst.castTag(.optional_type).?), | ||
| 110 | .unwrap_optional_safe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_safe).?, true), | ||
| 111 | .unwrap_optional_unsafe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_unsafe).?, false), | ||
| 109 | } | 112 | } |
| 110 | } | 113 | } |
| 111 | 114 | ||
| ... | @@ -305,8 +308,19 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr | ... | @@ -305,8 +308,19 @@ fn analyzeInstRetPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.NoOp) InnerErr |
| 305 | 308 | ||
| 306 | fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | 309 | fn analyzeInstRef(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { |
| 307 | const operand = try resolveInst(mod, scope, inst.positionals.operand); | 310 | const operand = try resolveInst(mod, scope, inst.positionals.operand); |
| 308 | const b = try mod.requireRuntimeBlock(scope, inst.base.src); | ||
| 309 | const ptr_type = try mod.singleConstPtrType(scope, inst.base.src, operand.ty); | 311 | const ptr_type = try mod.singleConstPtrType(scope, inst.base.src, operand.ty); |
| 312 | |||
| 313 | if (operand.value()) |val| { | ||
| 314 | const ref_payload = try scope.arena().create(Value.Payload.RefVal); | ||
| 315 | ref_payload.* = .{ .val = val }; | ||
| 316 | |||
| 317 | return mod.constInst(scope, inst.base.src, .{ | ||
| 318 | .ty = ptr_type, | ||
| 319 | .val = Value.initPayload(&ref_payload.base), | ||
| 320 | }); | ||
| 321 | } | ||
| 322 | |||
| 323 | const b = try mod.requireRuntimeBlock(scope, inst.base.src); | ||
| 310 | return mod.addUnOp(b, inst.base.src, ptr_type, .ref, operand); | 324 | return mod.addUnOp(b, inst.base.src, ptr_type, .ref, operand); |
| 311 | } | 325 | } |
| 312 | 326 | ||
| ... | @@ -620,6 +634,66 @@ fn analyzeInstIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) I | ... | @@ -620,6 +634,66 @@ fn analyzeInstIntType(mod: *Module, scope: *Scope, inttype: *zir.Inst.IntType) I |
| 620 | return mod.fail(scope, inttype.base.src, "TODO implement inttype", .{}); | 634 | return mod.fail(scope, inttype.base.src, "TODO implement inttype", .{}); |
| 621 | } | 635 | } |
| 622 | 636 | ||
| 637 | fn analyzeInstOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerError!*Inst { | ||
| 638 | const child_type = try resolveType(mod, scope, optional.positionals.operand); | ||
| 639 | |||
| 640 | return mod.constType(scope, optional.base.src, Type.initPayload(switch (child_type.tag()) { | ||
| 641 | .single_const_pointer => blk: { | ||
| 642 | const payload = try scope.arena().create(Type.Payload.OptionalSingleConstPointer); | ||
| 643 | payload.* = .{ | ||
| 644 | .pointee_type = child_type.elemType(), | ||
| 645 | }; | ||
| 646 | break :blk &payload.base; | ||
| 647 | }, | ||
| 648 | .single_mut_pointer => blk: { | ||
| 649 | const payload = try scope.arena().create(Type.Payload.OptionalSingleMutPointer); | ||
| 650 | payload.* = .{ | ||
| 651 | .pointee_type = child_type.elemType(), | ||
| 652 | }; | ||
| 653 | break :blk &payload.base; | ||
| 654 | }, | ||
| 655 | else => blk: { | ||
| 656 | const payload = try scope.arena().create(Type.Payload.Optional); | ||
| 657 | payload.* = .{ | ||
| 658 | .child_type = child_type, | ||
| 659 | }; | ||
| 660 | break :blk &payload.base; | ||
| 661 | }, | ||
| 662 | })); | ||
| 663 | } | ||
| 664 | |||
| 665 | fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { | ||
| 666 | const operand = try resolveInst(mod, scope, unwrap.positionals.operand); | ||
| 667 | assert(operand.ty.zigTypeTag() == .Pointer); | ||
| 668 | |||
| 669 | if (operand.ty.elemType().zigTypeTag() != .Optional) { | ||
| 670 | return mod.fail(scope, unwrap.base.src, "expected optional type, found {}", .{operand.ty.elemType()}); | ||
| 671 | } | ||
| 672 | |||
| 673 | const child_type = operand.ty.elemType().elemType(); | ||
| 674 | const child_pointer = if (operand.ty.isConstPtr()) | ||
| 675 | try mod.singleConstPtrType(scope, unwrap.base.src, child_type) | ||
| 676 | else | ||
| 677 | try mod.singleMutPtrType(scope, unwrap.base.src, child_type); | ||
| 678 | |||
| 679 | if (operand.value()) |val| { | ||
| 680 | if (val.isNull()) { | ||
| 681 | return mod.fail(scope, unwrap.base.src, "unable to unwrap null", .{}); | ||
| 682 | } | ||
| 683 | return mod.constInst(scope, unwrap.base.src, .{ | ||
| 684 | .ty = child_pointer, | ||
| 685 | .val = val, | ||
| 686 | }); | ||
| 687 | } | ||
| 688 | |||
| 689 | const b = try mod.requireRuntimeBlock(scope, unwrap.base.src); | ||
| 690 | if (safety_check and mod.wantSafety(scope)) { | ||
| 691 | const is_non_null = try mod.addUnOp(b, unwrap.base.src, Type.initTag(.bool), .isnonnull, operand); | ||
| 692 | try mod.addSafetyCheck(b, is_non_null, .unwrap_null); | ||
| 693 | } | ||
| 694 | return mod.addUnOp(b, unwrap.base.src, child_pointer, .unwrap_optional, operand); | ||
| 695 | } | ||
| 696 | |||
| 623 | fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { | 697 | fn analyzeInstFnType(mod: *Module, scope: *Scope, fntype: *zir.Inst.FnType) InnerError!*Inst { |
| 624 | const return_type = try resolveType(mod, scope, fntype.positionals.return_type); | 698 | const return_type = try resolveType(mod, scope, fntype.positionals.return_type); |
| 625 | 699 | ||
| ... | @@ -1094,18 +1168,19 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE | ... | @@ -1094,18 +1168,19 @@ fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerE |
| 1094 | return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body); | 1168 | return mod.addCondBr(parent_block, inst.base.src, cond, then_body, else_body); |
| 1095 | } | 1169 | } |
| 1096 | 1170 | ||
| 1097 | fn analyzeInstUnreachNoChk(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst { | 1171 | fn analyzeInstUnreachable( |
| 1098 | return mod.analyzeUnreach(scope, unreach.base.src); | 1172 | mod: *Module, |
| 1099 | } | 1173 | scope: *Scope, |
| 1100 | 1174 | unreach: *zir.Inst.NoOp, | |
| 1101 | fn analyzeInstUnreachable(mod: *Module, scope: *Scope, unreach: *zir.Inst.NoOp) InnerError!*Inst { | 1175 | safety_check: bool, |
| 1176 | ) InnerError!*Inst { | ||
| 1102 | const b = try mod.requireRuntimeBlock(scope, unreach.base.src); | 1177 | const b = try mod.requireRuntimeBlock(scope, unreach.base.src); |
| 1103 | // TODO Add compile error for @optimizeFor occurring too late in a scope. | 1178 | // TODO Add compile error for @optimizeFor occurring too late in a scope. |
| 1104 | if (mod.wantSafety(scope)) { | 1179 | if (safety_check and mod.wantSafety(scope)) { |
| 1105 | // TODO Once we have a panic function to call, call it here instead of this. | 1180 | return mod.safetyPanic(b, unreach.base.src, .unreach); |
| 1106 | _ = try mod.addNoOp(b, unreach.base.src, Type.initTag(.void), .breakpoint); | 1181 | } else { |
| 1182 | return mod.addNoOp(b, unreach.base.src, Type.initTag(.noreturn), .unreach); | ||
| 1107 | } | 1183 | } |
| 1108 | return mod.analyzeUnreach(scope, unreach.base.src); | ||
| 1109 | } | 1184 | } |
| 1110 | 1185 | ||
| 1111 | fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | 1186 | fn analyzeInstRet(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { |
test/stage2/compare_output.zig+24| ... | @@ -441,5 +441,29 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -441,5 +441,29 @@ pub fn addCases(ctx: *TestContext) !void { |
| 441 | , | 441 | , |
| 442 | "", | 442 | "", |
| 443 | ); | 443 | ); |
| 444 | |||
| 445 | // Optionals | ||
| 446 | case.addCompareOutput( | ||
| 447 | \\export fn _start() noreturn { | ||
| 448 | \\ const a: u32 = 2; | ||
| 449 | \\ const b: ?u32 = a; | ||
| 450 | \\ const c = b.?; | ||
| 451 | \\ if (c != 2) unreachable; | ||
| 452 | \\ | ||
| 453 | \\ exit(); | ||
| 454 | \\} | ||
| 455 | \\ | ||
| 456 | \\fn exit() noreturn { | ||
| 457 | \\ asm volatile ("syscall" | ||
| 458 | \\ : | ||
| 459 | \\ : [number] "{rax}" (231), | ||
| 460 | \\ [arg1] "{rdi}" (0) | ||
| 461 | \\ : "rcx", "r11", "memory" | ||
| 462 | \\ ); | ||
| 463 | \\ unreachable; | ||
| 464 | \\} | ||
| 465 | , | ||
| 466 | "", | ||
| 467 | ); | ||
| 444 | } | 468 | } |
| 445 | } | 469 | } |