authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-02 21:03:50-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2021-03-02 21:03:50-08:00
log4b57fb5f23aa6513ceaebe396d1faf215cf0a475
treea6060a67d81b1f875e0cfb947fadec9325b37a89
parent3ad9cb8b473820ec5ea11d85aa72e8ddc83cfa03
parent713f1138222dc40355c34c70d83b0a0805bd46c6
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7741 from FireFox317/optionals-llvm

stage2: add support for optionals in the LLVM backend

11 files changed, 258 insertions(+), 31 deletions(-)

src/astgen.zig+46-9
......@@ -453,13 +453,23 @@ pub fn expr(mod: *Module, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) In
453453 return rvalue(mod, scope, rl, result);
454454 },
455455 .unwrap_optional => {
456 const operand = try expr(mod, scope, rl, node_datas[node].lhs);
457 const op: zir.Inst.Tag = switch (rl) {
458 .ref => .optional_payload_safe_ptr,
459 else => .optional_payload_safe,
460 };
461456 const src = token_starts[main_tokens[node]];
462 return addZIRUnOp(mod, scope, src, op, operand);
457 switch (rl) {
458 .ref => return addZIRUnOp(
459 mod,
460 scope,
461 src,
462 .optional_payload_safe_ptr,
463 try expr(mod, scope, .ref, node_datas[node].lhs),
464 ),
465 else => return rvalue(mod, scope, rl, try addZIRUnOp(
466 mod,
467 scope,
468 src,
469 .optional_payload_safe,
470 try expr(mod, scope, .none, node_datas[node].lhs),
471 )),
472 }
463473 },
464474 .block_two, .block_two_semicolon => {
465475 const statements = [2]ast.Node.Index{ node_datas[node].lhs, node_datas[node].rhs };
......@@ -1699,9 +1709,13 @@ fn orelseCatchExpr(
16991709 setBlockResultLoc(&block_scope, rl);
17001710 defer block_scope.instructions.deinit(mod.gpa);
17011711
1702 // This could be a pointer or value depending on the `rl` parameter.
1712 // This could be a pointer or value depending on the `operand_rl` parameter.
1713 // We cannot use `block_scope.break_result_loc` because that has the bare
1714 // type, whereas this expression has the optional type. Later we make
1715 // up for this fact by calling rvalue on the else branch.
17031716 block_scope.break_count += 1;
1704 const operand = try expr(mod, &block_scope.base, block_scope.break_result_loc, lhs);
1717 const operand_rl = try makeOptionalTypeResultLoc(mod, &block_scope.base, src, block_scope.break_result_loc);
1718 const operand = try expr(mod, &block_scope.base, operand_rl, lhs);
17051719 const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand);
17061720
17071721 const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{
......@@ -1753,6 +1767,10 @@ fn orelseCatchExpr(
17531767
17541768 // This could be a pointer or value depending on `unwrap_op`.
17551769 const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand);
1770 const else_result = switch (rl) {
1771 .ref => unwrapped_payload,
1772 else => try rvalue(mod, &else_scope.base, block_scope.break_result_loc, unwrapped_payload),
1773 };
17561774
17571775 return finishThenElseBlock(
17581776 mod,
......@@ -1766,7 +1784,7 @@ fn orelseCatchExpr(
17661784 src,
17671785 src,
17681786 then_result,
1769 unwrapped_payload,
1787 else_result,
17701788 block,
17711789 block,
17721790 );
......@@ -3955,6 +3973,25 @@ fn rlStrategy(rl: ResultLoc, block_scope: *Scope.GenZIR) ResultLoc.Strategy {
39553973 }
39563974}
39573975
3976/// If the input ResultLoc is ref, returns ResultLoc.ref. Otherwise:
3977/// Returns ResultLoc.ty, where the type is determined by the input
3978/// ResultLoc type, wrapped in an optional type. If the input ResultLoc
3979/// has no type, .none is returned.
3980fn makeOptionalTypeResultLoc(mod: *Module, scope: *Scope, src: usize, rl: ResultLoc) !ResultLoc {
3981 switch (rl) {
3982 .ref => return ResultLoc.ref,
3983 .discard, .none, .block_ptr, .inferred_ptr, .bitcasted_ptr => return ResultLoc.none,
3984 .ty => |elem_ty| {
3985 const wrapped_ty = try addZIRUnOp(mod, scope, src, .optional_type, elem_ty);
3986 return ResultLoc{ .ty = wrapped_ty };
3987 },
3988 .ptr => |ptr_ty| {
3989 const wrapped_ty = try addZIRUnOp(mod, scope, src, .optional_type_from_ptr_elem, ptr_ty);
3990 return ResultLoc{ .ty = wrapped_ty };
3991 },
3992 }
3993}
3994
39583995fn setBlockResultLoc(block_scope: *Scope.GenZIR, parent_rl: ResultLoc) void {
39593996 // Depending on whether the result location is a pointer or value, different
39603997 // ZIR needs to be generated. In the former case we rely on storing to the
src/codegen/llvm.zig+105-8
......@@ -397,6 +397,7 @@ pub const LLVMIRModule = struct {
397397 .block => try self.genBlock(inst.castTag(.block).?),
398398 .br => try self.genBr(inst.castTag(.br).?),
399399 .breakpoint => try self.genBreakpoint(inst.castTag(.breakpoint).?),
400 .br_void => try self.genBrVoid(inst.castTag(.br_void).?),
400401 .call => try self.genCall(inst.castTag(.call).?),
401402 .cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?, .eq),
402403 .cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?, .gt),
......@@ -406,6 +407,10 @@ pub const LLVMIRModule = struct {
406407 .cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?, .neq),
407408 .condbr => try self.genCondBr(inst.castTag(.condbr).?),
408409 .intcast => try self.genIntCast(inst.castTag(.intcast).?),
410 .is_non_null => try self.genIsNonNull(inst.castTag(.is_non_null).?, false),
411 .is_non_null_ptr => try self.genIsNonNull(inst.castTag(.is_non_null_ptr).?, true),
412 .is_null => try self.genIsNull(inst.castTag(.is_null).?, false),
413 .is_null_ptr => try self.genIsNull(inst.castTag(.is_null_ptr).?, true),
409414 .load => try self.genLoad(inst.castTag(.load).?),
410415 .loop => try self.genLoop(inst.castTag(.loop).?),
411416 .not => try self.genNot(inst.castTag(.not).?),
......@@ -414,6 +419,8 @@ pub const LLVMIRModule = struct {
414419 .store => try self.genStore(inst.castTag(.store).?),
415420 .sub => try self.genSub(inst.castTag(.sub).?),
416421 .unreach => self.genUnreach(inst.castTag(.unreach).?),
422 .optional_payload => try self.genOptionalPayload(inst.castTag(.optional_payload).?, false),
423 .optional_payload_ptr => try self.genOptionalPayload(inst.castTag(.optional_payload_ptr).?, true),
417424 .dbg_stmt => blk: {
418425 // TODO: implement debug info
419426 break :blk null;
......@@ -534,21 +541,29 @@ pub const LLVMIRModule = struct {
534541 }
535542
536543 fn genBr(self: *LLVMIRModule, inst: *Inst.Br) !?*const llvm.Value {
537 // Get the block that we want to break to.
538544 var block = self.blocks.get(inst.block).?;
539 _ = self.builder.buildBr(block.parent_bb);
540545
541546 // If the break doesn't break a value, then we don't have to add
542547 // the values to the lists.
543 if (!inst.operand.ty.hasCodeGenBits()) return null;
548 if (!inst.operand.ty.hasCodeGenBits()) {
549 // TODO: in astgen these instructions should turn into `br_void` instructions.
550 _ = self.builder.buildBr(block.parent_bb);
551 } else {
552 const val = try self.resolveInst(inst.operand);
544553
545 // For the phi node, we need the basic blocks and the values of the
546 // break instructions.
547 try block.break_bbs.append(self.gpa, self.builder.getInsertBlock());
554 // For the phi node, we need the basic blocks and the values of the
555 // break instructions.
556 try block.break_bbs.append(self.gpa, self.builder.getInsertBlock());
557 try block.break_vals.append(self.gpa, val);
548558
549 const val = try self.resolveInst(inst.operand);
550 try block.break_vals.append(self.gpa, val);
559 _ = self.builder.buildBr(block.parent_bb);
560 }
561 return null;
562 }
551563
564 fn genBrVoid(self: *LLVMIRModule, inst: *Inst.BrVoid) !?*const llvm.Value {
565 var block = self.blocks.get(inst.block).?;
566 _ = self.builder.buildBr(block.parent_bb);
552567 return null;
553568 }
554569
......@@ -591,6 +606,44 @@ pub const LLVMIRModule = struct {
591606 return null;
592607 }
593608
609 fn genIsNonNull(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
610 const operand = try self.resolveInst(inst.operand);
611
612 if (operand_is_ptr) {
613 const index_type = self.context.intType(32);
614
615 var indices: [2]*const llvm.Value = .{
616 index_type.constNull(),
617 index_type.constInt(1, false),
618 };
619
620 return self.builder.buildLoad(self.builder.buildInBoundsGEP(operand, &indices, 2, ""), "");
621 } else {
622 return self.builder.buildExtractValue(operand, 1, "");
623 }
624 }
625
626 fn genIsNull(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
627 return self.builder.buildNot((try self.genIsNonNull(inst, operand_is_ptr)).?, "");
628 }
629
630 fn genOptionalPayload(self: *LLVMIRModule, inst: *Inst.UnOp, operand_is_ptr: bool) !?*const llvm.Value {
631 const operand = try self.resolveInst(inst.operand);
632
633 if (operand_is_ptr) {
634 const index_type = self.context.intType(32);
635
636 var indices: [2]*const llvm.Value = .{
637 index_type.constNull(),
638 index_type.constNull(),
639 };
640
641 return self.builder.buildInBoundsGEP(operand, &indices, 2, "");
642 } else {
643 return self.builder.buildExtractValue(operand, 0, "");
644 }
645 }
646
594647 fn genAdd(self: *LLVMIRModule, inst: *Inst.BinOp) !?*const llvm.Value {
595648 const lhs = try self.resolveInst(inst.lhs);
596649 const rhs = try self.resolveInst(inst.rhs);
......@@ -751,6 +804,13 @@ pub const LLVMIRModule = struct {
751804 // TODO: consider using buildInBoundsGEP2 for opaque pointers
752805 return self.builder.buildInBoundsGEP(val, &indices, 2, "");
753806 },
807 .ref_val => {
808 const elem_value = tv.val.castTag(.ref_val).?.data;
809 const elem_type = tv.ty.castPointer().?.data;
810 const alloca = self.buildAlloca(try self.getLLVMType(elem_type, src));
811 _ = self.builder.buildStore(try self.genTypedValue(src, .{ .ty = elem_type, .val = elem_value }), alloca);
812 return alloca;
813 },
754814 else => return self.fail(src, "TODO implement const of pointer type '{}'", .{tv.ty}),
755815 },
756816 .Array => {
......@@ -765,6 +825,29 @@ pub const LLVMIRModule = struct {
765825 return self.fail(src, "TODO handle more array values", .{});
766826 }
767827 },
828 .Optional => {
829 if (!tv.ty.isPtrLikeOptional()) {
830 var buf: Type.Payload.ElemType = undefined;
831 const child_type = tv.ty.optionalChild(&buf);
832 const llvm_child_type = try self.getLLVMType(child_type, src);
833
834 if (tv.val.tag() == .null_value) {
835 var optional_values: [2]*const llvm.Value = .{
836 llvm_child_type.constNull(),
837 self.context.intType(1).constNull(),
838 };
839 return self.context.constStruct(&optional_values, 2, false);
840 } else {
841 var optional_values: [2]*const llvm.Value = .{
842 try self.genTypedValue(src, .{ .ty = child_type, .val = tv.val }),
843 self.context.intType(1).constAllOnes(),
844 };
845 return self.context.constStruct(&optional_values, 2, false);
846 }
847 } else {
848 return self.fail(src, "TODO implement const of optional pointer", .{});
849 }
850 },
768851 else => return self.fail(src, "TODO implement const of type '{}'", .{tv.ty}),
769852 }
770853 }
......@@ -790,6 +873,20 @@ pub const LLVMIRModule = struct {
790873 const elem_type = try self.getLLVMType(t.elemType(), src);
791874 return elem_type.arrayType(@intCast(c_uint, t.abiSize(self.module.getTarget())));
792875 },
876 .Optional => {
877 if (!t.isPtrLikeOptional()) {
878 var buf: Type.Payload.ElemType = undefined;
879 const child_type = t.optionalChild(&buf);
880
881 var optional_types: [2]*const llvm.Type = .{
882 try self.getLLVMType(child_type, src),
883 self.context.intType(1),
884 };
885 return self.context.structType(&optional_types, 2, false);
886 } else {
887 return self.fail(src, "TODO implement optional pointers as actual pointers", .{});
888 }
889 },
793890 else => return self.fail(src, "TODO implement getLLVMType for type '{}'", .{t}),
794891 }
795892 }
src/codegen/llvm/bindings.zig+9
......@@ -21,9 +21,15 @@ pub const Context = opaque {
2121 pub const voidType = LLVMVoidTypeInContext;
2222 extern fn LLVMVoidTypeInContext(C: *const Context) *const Type;
2323
24 pub const structType = LLVMStructTypeInContext;
25 extern fn LLVMStructTypeInContext(C: *const Context, ElementTypes: [*]*const Type, ElementCount: c_uint, Packed: LLVMBool) *const Type;
26
2427 pub const constString = LLVMConstStringInContext;
2528 extern fn LLVMConstStringInContext(C: *const Context, Str: [*]const u8, Length: c_uint, DontNullTerminate: LLVMBool) *const Value;
2629
30 pub const constStruct = LLVMConstStructInContext;
31 extern fn LLVMConstStructInContext(C: *const Context, ConstantVals: [*]*const Value, Count: c_uint, Packed: LLVMBool) *const Value;
32
2733 pub const createBasicBlock = LLVMCreateBasicBlockInContext;
2834 extern fn LLVMCreateBasicBlockInContext(C: *const Context, Name: [*:0]const u8) *const BasicBlock;
2935
......@@ -204,6 +210,9 @@ pub const Builder = opaque {
204210
205211 pub const buildPhi = LLVMBuildPhi;
206212 extern fn LLVMBuildPhi(*const Builder, Ty: *const Type, Name: [*:0]const u8) *const Value;
213
214 pub const buildExtractValue = LLVMBuildExtractValue;
215 extern fn LLVMBuildExtractValue(*const Builder, AggVal: *const Value, Index: c_uint, Name: [*:0]const u8) *const Value;
207216};
208217
209218pub const IntPredicate = extern enum {
src/link.zig+2-2
......@@ -550,11 +550,11 @@ pub const File = struct {
550550 id_symlink_basename,
551551 &prev_digest_buf,
552552 ) catch |err| b: {
553 log.debug("archive new_digest={x} readFile error: {s}", .{ digest, @errorName(err) });
553 log.debug("archive new_digest={s} readFile error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
554554 break :b prev_digest_buf[0..0];
555555 };
556556 if (mem.eql(u8, prev_digest, &digest)) {
557 log.debug("archive digest={x} match - skipping invocation", .{digest});
557 log.debug("archive digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
558558 base.lock = man.toOwnedLock();
559559 return;
560560 }
src/link/Coff.zig+3-3
......@@ -892,17 +892,17 @@ fn linkWithLLD(self: *Coff, comp: *Compilation) !void {
892892 id_symlink_basename,
893893 &prev_digest_buf,
894894 ) catch |err| blk: {
895 log.debug("COFF LLD new_digest={x} error: {s}", .{ digest, @errorName(err) });
895 log.debug("COFF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
896896 // Handle this as a cache miss.
897897 break :blk prev_digest_buf[0..0];
898898 };
899899 if (mem.eql(u8, prev_digest, &digest)) {
900 log.debug("COFF LLD digest={x} match - skipping invocation", .{digest});
900 log.debug("COFF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
901901 // Hot diggity dog! The output binary is already there.
902902 self.base.lock = man.toOwnedLock();
903903 return;
904904 }
905 log.debug("COFF LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest });
905 log.debug("COFF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
906906
907907 // We are about to change the output file to be different, so we invalidate the build hash now.
908908 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
src/link/Elf.zig+3-3
......@@ -1365,17 +1365,17 @@ fn linkWithLLD(self: *Elf, comp: *Compilation) !void {
13651365 id_symlink_basename,
13661366 &prev_digest_buf,
13671367 ) catch |err| blk: {
1368 log.debug("ELF LLD new_digest={x} error: {s}", .{ digest, @errorName(err) });
1368 log.debug("ELF LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
13691369 // Handle this as a cache miss.
13701370 break :blk prev_digest_buf[0..0];
13711371 };
13721372 if (mem.eql(u8, prev_digest, &digest)) {
1373 log.debug("ELF LLD digest={x} match - skipping invocation", .{digest});
1373 log.debug("ELF LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
13741374 // Hot diggity dog! The output binary is already there.
13751375 self.base.lock = man.toOwnedLock();
13761376 return;
13771377 }
1378 log.debug("ELF LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest });
1378 log.debug("ELF LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
13791379
13801380 // We are about to change the output file to be different, so we invalidate the build hash now.
13811381 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
src/link/MachO.zig+3-3
......@@ -556,17 +556,17 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
556556 id_symlink_basename,
557557 &prev_digest_buf,
558558 ) catch |err| blk: {
559 log.debug("MachO LLD new_digest={x} error: {s}", .{ digest, @errorName(err) });
559 log.debug("MachO LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
560560 // Handle this as a cache miss.
561561 break :blk prev_digest_buf[0..0];
562562 };
563563 if (mem.eql(u8, prev_digest, &digest)) {
564 log.debug("MachO LLD digest={x} match - skipping invocation", .{digest});
564 log.debug("MachO LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
565565 // Hot diggity dog! The output binary is already there.
566566 self.base.lock = man.toOwnedLock();
567567 return;
568568 }
569 log.debug("MachO LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest });
569 log.debug("MachO LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
570570
571571 // We are about to change the output file to be different, so we invalidate the build hash now.
572572 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
src/link/Wasm.zig+3-3
......@@ -391,17 +391,17 @@ fn linkWithLLD(self: *Wasm, comp: *Compilation) !void {
391391 id_symlink_basename,
392392 &prev_digest_buf,
393393 ) catch |err| blk: {
394 log.debug("WASM LLD new_digest={x} error: {s}", .{ digest, @errorName(err) });
394 log.debug("WASM LLD new_digest={s} error: {s}", .{ std.fmt.fmtSliceHexLower(&digest), @errorName(err) });
395395 // Handle this as a cache miss.
396396 break :blk prev_digest_buf[0..0];
397397 };
398398 if (mem.eql(u8, prev_digest, &digest)) {
399 log.debug("WASM LLD digest={x} match - skipping invocation", .{digest});
399 log.debug("WASM LLD digest={s} match - skipping invocation", .{std.fmt.fmtSliceHexLower(&digest)});
400400 // Hot diggity dog! The output binary is already there.
401401 self.base.lock = man.toOwnedLock();
402402 return;
403403 }
404 log.debug("WASM LLD prev_digest={x} new_digest={x}", .{ prev_digest, digest });
404 log.debug("WASM LLD prev_digest={s} new_digest={s}", .{ std.fmt.fmtSliceHexLower(prev_digest), std.fmt.fmtSliceHexLower(&digest) });
405405
406406 // We are about to change the output file to be different, so we invalidate the build hash now.
407407 directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) {
src/zir.zig+5
......@@ -299,6 +299,9 @@ pub const Inst = struct {
299299 xor,
300300 /// Create an optional type '?T'
301301 optional_type,
302 /// Create an optional type '?T'. The operand is a pointer value. The optional type will
303 /// be the type of the pointer element, wrapped in an optional.
304 optional_type_from_ptr_elem,
302305 /// Create a union type.
303306 union_type,
304307 /// ?T => T with safety.
......@@ -397,6 +400,7 @@ pub const Inst = struct {
397400 .mut_slice_type,
398401 .const_slice_type,
399402 .optional_type,
403 .optional_type_from_ptr_elem,
400404 .optional_payload_safe,
401405 .optional_payload_unsafe,
402406 .optional_payload_safe_ptr,
......@@ -597,6 +601,7 @@ pub const Inst = struct {
597601 .typeof,
598602 .xor,
599603 .optional_type,
604 .optional_type_from_ptr_elem,
600605 .optional_payload_safe,
601606 .optional_payload_unsafe,
602607 .optional_payload_safe_ptr,
src/zir_sema.zig+11
......@@ -131,6 +131,7 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
131131 .typeof => return zirTypeof(mod, scope, old_inst.castTag(.typeof).?),
132132 .typeof_peer => return zirTypeofPeer(mod, scope, old_inst.castTag(.typeof_peer).?),
133133 .optional_type => return zirOptionalType(mod, scope, old_inst.castTag(.optional_type).?),
134 .optional_type_from_ptr_elem => return zirOptionalTypeFromPtrElem(mod, scope, old_inst.castTag(.optional_type_from_ptr_elem).?),
134135 .optional_payload_safe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_safe).?, true),
135136 .optional_payload_unsafe => return zirOptionalPayload(mod, scope, old_inst.castTag(.optional_payload_unsafe).?, false),
136137 .optional_payload_safe_ptr => return zirOptionalPayloadPtr(mod, scope, old_inst.castTag(.optional_payload_safe_ptr).?, true),
......@@ -1093,6 +1094,16 @@ fn zirOptionalType(mod: *Module, scope: *Scope, optional: *zir.Inst.UnOp) InnerE
10931094 return mod.constType(scope, optional.base.src, try mod.optionalType(scope, child_type));
10941095}
10951096
1097fn zirOptionalTypeFromPtrElem(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
1098 const tracy = trace(@src());
1099 defer tracy.end();
1100
1101 const ptr = try resolveInst(mod, scope, inst.positionals.operand);
1102 const elem_ty = ptr.ty.elemType();
1103
1104 return mod.constType(scope, inst.base.src, try mod.optionalType(scope, elem_ty));
1105}
1106
10961107fn zirArrayType(mod: *Module, scope: *Scope, array: *zir.Inst.BinOp) InnerError!*Inst {
10971108 const tracy = trace(@src());
10981109 defer tracy.end();
test/stage2/llvm.zig+68
......@@ -132,4 +132,72 @@ pub fn addCases(ctx: *TestContext) !void {
132132 \\}
133133 , "");
134134 }
135
136 {
137 var case = ctx.exeUsingLlvmBackend("optionals", linux_x64);
138
139 case.addCompareOutput(
140 \\fn assert(ok: bool) void {
141 \\ if (!ok) unreachable;
142 \\}
143 \\
144 \\export fn main() c_int {
145 \\ var opt_val: ?i32 = 10;
146 \\ var null_val: ?i32 = null;
147 \\
148 \\ var val1: i32 = opt_val.?;
149 \\ const val1_1: i32 = opt_val.?;
150 \\ var ptr_val1 = &(opt_val.?);
151 \\ const ptr_val1_1 = &(opt_val.?);
152 \\
153 \\ var val2: i32 = null_val orelse 20;
154 \\ const val2_2: i32 = null_val orelse 20;
155 \\
156 \\ var value: i32 = 20;
157 \\ var ptr_val2 = &(null_val orelse value);
158 \\
159 \\ const val3 = opt_val orelse 30;
160 \\ var val3_var = opt_val orelse 30;
161 \\
162 \\ assert(val1 == 10);
163 \\ assert(val1_1 == 10);
164 \\ assert(ptr_val1.* == 10);
165 \\ assert(ptr_val1_1.* == 10);
166 \\
167 \\ assert(val2 == 20);
168 \\ assert(val2_2 == 20);
169 \\ assert(ptr_val2.* == 20);
170 \\
171 \\ assert(val3 == 10);
172 \\ assert(val3_var == 10);
173 \\
174 \\ (null_val orelse val2) = 1234;
175 \\ assert(val2 == 1234);
176 \\
177 \\ (opt_val orelse val2) = 5678;
178 \\ assert(opt_val.? == 5678);
179 \\
180 \\ return 0;
181 \\}
182 , "");
183 }
184
185 {
186 var case = ctx.exeUsingLlvmBackend("for loop", linux_x64);
187
188 case.addCompareOutput(
189 \\fn assert(ok: bool) void {
190 \\ if (!ok) unreachable;
191 \\}
192 \\
193 \\export fn main() c_int {
194 \\ var x: u32 = 0;
195 \\ for ("hello") |_| {
196 \\ x += 1;
197 \\ }
198 \\ assert("hello".len == x);
199 \\ return 0;
200 \\}
201 , "");
202 }
135203}