authorgravatar for tom@trives.co.ukThomas Ives <tom@trives.co.uk> 2021-11-02 22:43:51+00:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-10 12:39:47-05:00
log51efd553ae94fbf958974284a2f30091e4e1a2fa
tree84f513a2d2cc57ffd0d1b035dac450d25e35a26c
parent91c3206b45074821749f52043937bf6ed6d9a105

C backend: Improve lowering of Zig types to C types

1. Changed Zig pointers to functions to be typedef'd so then we can treat them the same as other types. 2. Distinguished between const slices (zig_L prefix) and mut slices (zig_M prefix). 3. Changed lowering of Zig "const pointers" (e.g. *const u8) to to C "pointers to const" (e.g. const char *) rather than C "const pointers" (e.g. char * const) 4. Ensured that all typedefs are "linked" even if the decl doesn't require any forward declarations 5. Added test that exercises function pointer type rendering 6. Changed .slice_ptr instruction to allocate pointer local rather than a uintptr_t local

3 files changed, 300 insertions(+), 264 deletions(-)

src/codegen/c.zig+254-263
......@@ -179,6 +179,14 @@ pub const DeclGen = struct {
179179 return error.AnalysisFail;
180180 }
181181
182 fn getTypedefName(dg: *DeclGen, t: Type) ?[]const u8 {
183 if (dg.typedefs.get(t)) |some| {
184 return some.name;
185 } else {
186 return null;
187 }
188 }
189
182190 fn renderDeclValue(
183191 dg: *DeclGen,
184192 writer: anytype,
......@@ -200,14 +208,20 @@ pub const DeclGen = struct {
200208 return;
201209 }
202210
203 // Determine if we must pointer cast.
204211 assert(decl.has_tv);
205 if (ty.eql(decl.ty)) {
206 try writer.writeByte('&');
207 } else {
208 try writer.writeAll("(");
209 try dg.renderType(writer, ty);
210 try writer.writeAll(")&");
212 // We shouldn't cast C function pointers as this is UB (when you call
213 // them). The analysis until now should ensure that the C function
214 // pointers are compatible. If they are not, then there is a bug
215 // somewhere and we should let the C compiler tell us about it.
216 if (ty.castPtrToFn() == null) {
217 // Determine if we must pointer cast.
218 if (ty.eql(decl.ty)) {
219 try writer.writeByte('&');
220 } else {
221 try writer.writeAll("(");
222 try dg.renderType(writer, ty);
223 try writer.writeAll(")&");
224 }
211225 }
212226 try dg.renderDeclName(decl, writer);
213227 }
......@@ -514,7 +528,190 @@ pub const DeclGen = struct {
514528 try w.writeByte(')');
515529 }
516530
531 fn renderPtrToFnTypedef(dg: *DeclGen, t: Type, fn_ty: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
532 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
533 defer buffer.deinit();
534 const bw = buffer.writer();
535
536 const fn_info = fn_ty.fnInfo();
537
538 try bw.writeAll("typedef ");
539 try dg.renderType(bw, fn_info.return_type);
540 try bw.writeAll(" (*");
541
542 const name_start = buffer.items.len;
543 // TODO: typeToCIdentifier truncates to 128 bytes, we probably don't want to do this
544 try bw.print("zig_F_{s})(", .{typeToCIdentifier(t)});
545 const name_end = buffer.items.len - 2;
546
547 const param_len = fn_info.param_types.len;
548 const is_var_args = fn_info.is_var_args;
549 if (param_len == 0 and !is_var_args)
550 try bw.writeAll("void")
551 else {
552 var index: usize = 0;
553 while (index < param_len) : (index += 1) {
554 if (index > 0) {
555 try bw.writeAll(", ");
556 }
557 try dg.renderType(bw, fn_info.param_types[index]);
558 }
559 }
560 if (is_var_args) {
561 if (param_len != 0) try bw.writeAll(", ");
562 try bw.writeAll("...");
563 }
564 try bw.writeAll(");\n");
565
566 const rendered = buffer.toOwnedSlice();
567 errdefer dg.typedefs.allocator.free(rendered);
568 const name = rendered[name_start..name_end];
569
570 try dg.typedefs.ensureUnusedCapacity(1);
571 dg.typedefs.putAssumeCapacityNoClobber(
572 try t.copy(dg.typedefs_arena),
573 .{ .name = name, .rendered = rendered },
574 );
575
576 return name;
577 }
578
579 fn renderSliceTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
580 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
581 defer buffer.deinit();
582 const bw = buffer.writer();
583
584 try bw.writeAll("typedef struct { ");
585 const elem_type = t.elemType();
586 try dg.renderType(bw, elem_type);
587 if (t.isConstPtr()) {
588 try bw.writeAll(" const");
589 }
590 if (t.isVolatilePtr()) {
591 try bw.writeAll(" volatile");
592 }
593 try bw.writeAll(" *");
594 try bw.writeAll("ptr; size_t len; } ");
595 const name_index = buffer.items.len;
596 if (t.isConstPtr()) {
597 try bw.print("zig_L_{s};\n", .{typeToCIdentifier(elem_type)});
598 } else {
599 try bw.print("zig_M_{s};\n", .{typeToCIdentifier(elem_type)});
600 }
601
602 const rendered = buffer.toOwnedSlice();
603 errdefer dg.typedefs.allocator.free(rendered);
604 const name = rendered[name_index .. rendered.len - 2];
605
606 try dg.typedefs.ensureUnusedCapacity(1);
607 dg.typedefs.putAssumeCapacityNoClobber(
608 try t.copy(dg.typedefs_arena),
609 .{ .name = name, .rendered = rendered },
610 );
611
612 return name;
613 }
614
615 fn renderStructTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
616 const struct_obj = t.castTag(.@"struct").?.data; // Handle 0 bit types elsewhere.
617 const fqn = try struct_obj.getFullyQualifiedName(dg.typedefs.allocator);
618 defer dg.typedefs.allocator.free(fqn);
619
620 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
621 defer buffer.deinit();
622
623 try buffer.appendSlice("typedef struct {\n");
624 {
625 var it = struct_obj.fields.iterator();
626 while (it.next()) |entry| {
627 const field_ty = entry.value_ptr.ty;
628 const name: CValue = .{ .bytes = entry.key_ptr.* };
629 try buffer.append(' ');
630 try dg.renderTypeAndName(buffer.writer(), field_ty, name, .Mut);
631 try buffer.appendSlice(";\n");
632 }
633 }
634 try buffer.appendSlice("} ");
635
636 const name_start = buffer.items.len;
637 try buffer.writer().print("zig_S_{s};\n", .{fmtIdent(fqn)});
638
639 const rendered = buffer.toOwnedSlice();
640 errdefer dg.typedefs.allocator.free(rendered);
641 const name = rendered[name_start .. rendered.len - 2];
642
643 try dg.typedefs.ensureUnusedCapacity(1);
644 dg.typedefs.putAssumeCapacityNoClobber(
645 try t.copy(dg.typedefs_arena),
646 .{ .name = name, .rendered = rendered },
647 );
648
649 return name;
650 }
651
652 fn renderErrorUnionTypedef(dg: *DeclGen, t: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
653 const child_type = t.errorUnionPayload();
654 const err_set_type = t.errorUnionSet();
655
656 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
657 defer buffer.deinit();
658 const bw = buffer.writer();
659
660 try bw.writeAll("typedef struct { ");
661 try dg.renderType(bw, child_type);
662 try bw.writeAll(" payload; uint16_t error; } ");
663 const name_index = buffer.items.len;
664 if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| {
665 const func = inf_err_set_payload.data.func;
666 try bw.writeAll("zig_E_");
667 try dg.renderDeclName(func.owner_decl, bw);
668 try bw.writeAll(";\n");
669 } else {
670 try bw.print("zig_E_{s}_{s};\n", .{
671 typeToCIdentifier(err_set_type), typeToCIdentifier(child_type),
672 });
673 }
674
675 const rendered = buffer.toOwnedSlice();
676 errdefer dg.typedefs.allocator.free(rendered);
677 const name = rendered[name_index .. rendered.len - 2];
678
679 try dg.typedefs.ensureUnusedCapacity(1);
680 dg.typedefs.putAssumeCapacityNoClobber(
681 try t.copy(dg.typedefs_arena),
682 .{ .name = name, .rendered = rendered },
683 );
684
685 return name;
686 }
687
688 fn renderOptionalTypedef(dg: *DeclGen, t: Type, child_type: Type) error{ OutOfMemory, AnalysisFail }![]const u8 {
689 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
690 defer buffer.deinit();
691 const bw = buffer.writer();
692
693 try bw.writeAll("typedef struct { ");
694 try dg.renderType(bw, child_type);
695 try bw.writeAll(" payload; bool is_null; } ");
696 const name_index = buffer.items.len;
697 try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type)});
698
699 const rendered = buffer.toOwnedSlice();
700 errdefer dg.typedefs.allocator.free(rendered);
701 const name = rendered[name_index .. rendered.len - 2];
702
703 try dg.typedefs.ensureUnusedCapacity(1);
704 dg.typedefs.putAssumeCapacityNoClobber(
705 try t.copy(dg.typedefs_arena),
706 .{ .name = name, .rendered = rendered },
707 );
708
709 return name;
710 }
711
517712 fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void {
713 const target = dg.module.getTarget();
714
518715 switch (t.zigTypeTag()) {
519716 .NoReturn => {
520717 try w.writeAll("zig_noreturn void");
......@@ -542,7 +739,7 @@ pub const DeclGen = struct {
542739 .c_longlong => try w.writeAll("long long"),
543740 .c_ulonglong => try w.writeAll("unsigned long long"),
544741 .int_signed, .int_unsigned => {
545 const info = t.intInfo(dg.module.getTarget());
742 const info = t.intInfo(target);
546743 const sign_prefix = switch (info.signedness) {
547744 .signed => "",
548745 .unsigned => "u",
......@@ -554,7 +751,6 @@ pub const DeclGen = struct {
554751 else => unreachable,
555752 }
556753 },
557
558754 .Float => {
559755 switch (t.tag()) {
560756 .f32 => try w.writeAll("float"),
......@@ -565,196 +761,71 @@ pub const DeclGen = struct {
565761 else => unreachable,
566762 }
567763 },
568
569764 .Pointer => {
570765 if (t.isSlice()) {
571 if (dg.typedefs.get(t)) |some| {
572 return w.writeAll(some.name);
573 }
766 const name = dg.getTypedefName(t) orelse
767 try dg.renderSliceTypedef(t);
574768
575 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
576 defer buffer.deinit();
577 const bw = buffer.writer();
578
579 try bw.writeAll("typedef struct { ");
580 const elem_type = t.elemType();
581 try dg.renderType(bw, elem_type);
582 try bw.writeAll(" *");
583 // We skip the const qualifier because C's type system
584 // would not allow a local mutable variable which is this slice type
585 // to be overwritten with a new slice type.
586 if (t.isVolatilePtr()) {
587 try bw.writeAll("volatile ");
588 }
589 try bw.writeAll("ptr; size_t len; } ");
590 const name_index = buffer.items.len;
591 try bw.print("zig_L_{s};\n", .{typeToCIdentifier(elem_type)});
592
593 const rendered = buffer.toOwnedSlice();
594 errdefer dg.typedefs.allocator.free(rendered);
595 const name = rendered[name_index .. rendered.len - 2];
596
597 try dg.typedefs.ensureUnusedCapacity(1);
598 try w.writeAll(name);
599 dg.typedefs.putAssumeCapacityNoClobber(
600 try t.copy(dg.typedefs_arena),
601 .{ .name = name, .rendered = rendered },
602 );
603 return;
769 return w.writeAll(name);
604770 }
771
605772 if (t.castPtrToFn()) |fn_ty| {
606 const fn_info = fn_ty.fnInfo();
607 try dg.renderType(w, fn_info.return_type);
608 try w.writeAll(" (*)(");
609 const param_len = fn_info.param_types.len;
610 const is_var_args = fn_info.is_var_args;
611 if (param_len == 0 and !is_var_args)
612 try w.writeAll("void")
613 else {
614 var index: usize = 0;
615 while (index < param_len) : (index += 1) {
616 if (index > 0) {
617 try w.writeAll(", ");
618 }
619 try dg.renderType(w, fn_info.param_types[index]);
620 }
621 }
622 if (is_var_args) {
623 if (param_len != 0) try w.writeAll(", ");
624 try w.writeAll("...");
625 }
626 try w.writeByte(')');
627 return;
773 const name = dg.getTypedefName(t) orelse
774 try dg.renderPtrToFnTypedef(t, fn_ty);
775
776 return w.writeAll(name);
628777 }
629778
630779 try dg.renderType(w, t.elemType());
631 try w.writeAll(" *");
632780 if (t.isConstPtr()) {
633 try w.writeAll("const ");
781 try w.writeAll(" const");
634782 }
635783 if (t.isVolatilePtr()) {
636 try w.writeAll("volatile ");
784 try w.writeAll(" volatile");
637785 }
786 return w.writeAll(" *");
638787 },
639788 .Array => {
789 // We are referencing the array so it will decay to a C pointer.
640790 try dg.renderType(w, t.elemType());
641 try w.writeAll(" *");
791 return w.writeAll(" *");
642792 },
643793 .Optional => {
644794 var opt_buf: Type.Payload.ElemType = undefined;
645795 const child_type = t.optionalChild(&opt_buf);
646 const target = dg.module.getTarget();
796
647797 if (t.isPtrLikeOptional()) {
648798 return dg.renderType(w, child_type);
649 } else if (dg.typedefs.get(t)) |some| {
650 return w.writeAll(some.name);
651 } else if (child_type.abiSize(target) == 0) {
799 }
800
801 if (child_type.abiSize(target) == 0) {
652802 return w.writeAll("bool");
653803 }
654804
655 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
656 defer buffer.deinit();
657 const bw = buffer.writer();
658
659 try bw.writeAll("typedef struct { ");
660 try dg.renderType(bw, child_type);
661 try bw.writeAll(" payload; bool is_null; } ");
662 const name_index = buffer.items.len;
663 try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type)});
664
665 const rendered = buffer.toOwnedSlice();
666 errdefer dg.typedefs.allocator.free(rendered);
667 const name = rendered[name_index .. rendered.len - 2];
668
669 try dg.typedefs.ensureUnusedCapacity(1);
670 try w.writeAll(name);
671 dg.typedefs.putAssumeCapacityNoClobber(
672 try t.copy(dg.typedefs_arena),
673 .{ .name = name, .rendered = rendered },
674 );
805 const name = dg.getTypedefName(t) orelse
806 try dg.renderOptionalTypedef(t, child_type);
807
808 return w.writeAll(name);
675809 },
676810 .ErrorSet => {
677811 comptime std.debug.assert(Type.initTag(.anyerror).abiSize(builtin.target) == 2);
678 try w.writeAll("uint16_t");
812 return w.writeAll("uint16_t");
679813 },
680814 .ErrorUnion => {
681 if (dg.typedefs.get(t)) |some| {
682 return w.writeAll(some.name);
683 }
684 const child_type = t.errorUnionPayload();
685 const err_set_type = t.errorUnionSet();
686
687 if (!child_type.hasCodeGenBits()) {
688 return dg.renderType(w, err_set_type);
689 }
690
691 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
692 defer buffer.deinit();
693 const bw = buffer.writer();
694
695 try bw.writeAll("typedef struct { ");
696 try dg.renderType(bw, child_type);
697 try bw.writeAll(" payload; uint16_t error; } ");
698 const name_index = buffer.items.len;
699 if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| {
700 const func = inf_err_set_payload.data.func;
701 try bw.writeAll("zig_E_");
702 try dg.renderDeclName(func.owner_decl, bw);
703 try bw.writeAll(";\n");
704 } else {
705 try bw.print("zig_E_{s}_{s};\n", .{
706 typeToCIdentifier(err_set_type), typeToCIdentifier(child_type),
707 });
815 if (t.errorUnionPayload().abiSize(target) == 0) {
816 return dg.renderType(w, t.errorUnionSet());
708817 }
709818
710 const rendered = buffer.toOwnedSlice();
711 errdefer dg.typedefs.allocator.free(rendered);
712 const name = rendered[name_index .. rendered.len - 2];
819 const name = dg.getTypedefName(t) orelse
820 try dg.renderErrorUnionTypedef(t);
713821
714 try dg.typedefs.ensureUnusedCapacity(1);
715 try w.writeAll(name);
716 dg.typedefs.putAssumeCapacityNoClobber(
717 try t.copy(dg.typedefs_arena),
718 .{ .name = name, .rendered = rendered },
719 );
822 return w.writeAll(name);
720823 },
721824 .Struct => {
722 if (dg.typedefs.get(t)) |some| {
723 return w.writeAll(some.name);
724 }
725 const struct_obj = t.castTag(.@"struct").?.data; // Handle 0 bit types elsewhere.
726 const fqn = try struct_obj.getFullyQualifiedName(dg.typedefs.allocator);
727 defer dg.typedefs.allocator.free(fqn);
728
729 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);
730 defer buffer.deinit();
731
732 try buffer.appendSlice("typedef struct {\n");
733 {
734 var it = struct_obj.fields.iterator();
735 while (it.next()) |entry| {
736 const field_ty = entry.value_ptr.ty;
737 const name: CValue = .{ .bytes = entry.key_ptr.* };
738 try buffer.append(' ');
739 try dg.renderTypeAndName(buffer.writer(), field_ty, name, .Mut);
740 try buffer.appendSlice(";\n");
741 }
742 }
743 try buffer.appendSlice("} ");
744
745 const name_start = buffer.items.len;
746 try buffer.writer().print("zig_S_{s};\n", .{fmtIdent(fqn)});
825 const name = dg.getTypedefName(t) orelse
826 try dg.renderStructTypedef(t);
747827
748 const rendered = buffer.toOwnedSlice();
749 errdefer dg.typedefs.allocator.free(rendered);
750 const name = rendered[name_start .. rendered.len - 2];
751
752 try dg.typedefs.ensureUnusedCapacity(1);
753 try w.writeAll(name);
754 dg.typedefs.putAssumeCapacityNoClobber(
755 try t.copy(dg.typedefs_arena),
756 .{ .name = name, .rendered = rendered },
757 );
828 return w.writeAll(name);
758829 },
759830 .Enum => {
760831 // For enums, we simply use the integer tag type.
......@@ -763,12 +834,17 @@ pub const DeclGen = struct {
763834
764835 try dg.renderType(w, int_tag_ty);
765836 },
766 .Union => return dg.fail("TODO: C backend: implement type Union", .{}),
837
838 .Union,
839 .Frame,
840 .AnyFrame,
841 .Vector,
842 .Opaque,
843 => |tag| return dg.fail("TODO: C backend: implement value of type {s}", .{
844 @tagName(tag),
845 }),
846
767847 .Fn => unreachable, // This is a function body, not a function pointer.
768 .Opaque => return dg.fail("TODO: C backend: implement type Opaque", .{}),
769 .Frame => return dg.fail("TODO: C backend: implement type Frame", .{}),
770 .AnyFrame => return dg.fail("TODO: C backend: implement type AnyFrame", .{}),
771 .Vector => return dg.fail("TODO: C backend: implement type Vector", .{}),
772848
773849 .Null,
774850 .Undefined,
......@@ -800,100 +876,14 @@ pub const DeclGen = struct {
800876 render_ty = render_ty.elemType();
801877 }
802878
803 // TODO this is duplicated from the code below and does not handle
804 // arbitrary nesting of pointers. This renderTypeAndName function
805 // needs to be reworked by someone who understands C's insane type syntax. That
806 // person might be future me but it is certainly not present me.
807 if (render_ty.zigTypeTag() == .Pointer and
808 render_ty.childType().zigTypeTag() == .Pointer and
809 render_ty.childType().childType().zigTypeTag() == .Fn)
810 {
811 const ptr2_ty = render_ty.childType();
812 const fn_info = ptr2_ty.childType().fnInfo();
813 const ret_ty = fn_info.return_type;
814 if (ret_ty.zigTypeTag() == .NoReturn) {
815 // noreturn attribute is not allowed here.
816 try w.writeAll("void");
817 } else {
818 try dg.renderType(w, ret_ty);
819 }
820 try w.writeAll(" (*");
821 switch (mutability) {
822 .Const => try w.writeAll("const "),
823 .Mut => {},
824 }
825 if (!ptr2_ty.ptrIsMutable()) {
826 try w.writeAll("*const ");
827 } else {
828 try w.writeAll("*");
829 }
830 try dg.writeCValue(w, name);
831 try w.writeAll(")(");
832 const param_len = fn_info.param_types.len;
833 const is_var_args = fn_info.is_var_args;
834 if (param_len == 0 and !is_var_args)
835 try w.writeAll("void")
836 else {
837 var index: usize = 0;
838 while (index < param_len) : (index += 1) {
839 if (index > 0) {
840 try w.writeAll(", ");
841 }
842 try dg.renderType(w, fn_info.param_types[index]);
843 }
844 }
845 if (is_var_args) {
846 if (param_len != 0) try w.writeAll(", ");
847 try w.writeAll("...");
848 }
849 try w.writeByte(')');
850 return;
851 }
879 try dg.renderType(w, render_ty);
852880
853 if (render_ty.castPtrToFn()) |fn_ty| {
854 const fn_info = fn_ty.fnInfo();
855 const ret_ty = fn_info.return_type;
856 if (ret_ty.zigTypeTag() == .NoReturn) {
857 // noreturn attribute is not allowed here.
858 try w.writeAll("void");
859 } else {
860 try dg.renderType(w, ret_ty);
861 }
862 try w.writeAll(" (*");
863 switch (mutability) {
864 .Const => try w.writeAll("const "),
865 .Mut => {},
866 }
867 try dg.writeCValue(w, name);
868 try w.writeAll(")(");
869 const param_len = fn_info.param_types.len;
870 const is_var_args = fn_info.is_var_args;
871 if (param_len == 0 and !is_var_args)
872 try w.writeAll("void")
873 else {
874 var index: usize = 0;
875 while (index < param_len) : (index += 1) {
876 if (index > 0) {
877 try w.writeAll(", ");
878 }
879 try dg.renderType(w, fn_info.param_types[index]);
880 }
881 }
882 if (is_var_args) {
883 if (param_len != 0) try w.writeAll(", ");
884 try w.writeAll("...");
885 }
886 try w.writeByte(')');
887 } else {
888 try dg.renderType(w, render_ty);
889
890 const const_prefix = switch (mutability) {
891 .Const => "const ",
892 .Mut => "",
893 };
894 try w.print(" {s}", .{const_prefix});
895 try dg.writeCValue(w, name);
896 }
881 const const_prefix = switch (mutability) {
882 .Const => "const ",
883 .Mut => "",
884 };
885 try w.print(" {s}", .{const_prefix});
886 try dg.writeCValue(w, name);
897887 try w.writeAll(suffix.items);
898888 }
899889
......@@ -1224,10 +1214,11 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
12241214fn airSliceField(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !CValue {
12251215 if (f.liveness.isUnused(inst)) return CValue.none;
12261216
1217 const inst_ty = f.air.typeOfIndex(inst);
12271218 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
12281219 const operand = try f.resolveInst(ty_op.operand);
12291220 const writer = f.object.writer();
1230 const local = try f.allocLocal(Type.initTag(.usize), .Const);
1221 const local = try f.allocLocal(inst_ty, .Const);
12311222 try writer.writeAll(" = ");
12321223 try f.writeCValue(writer, operand);
12331224 try writer.writeAll(suffix);
src/link/C.zig+4-1
......@@ -362,7 +362,7 @@ fn flushDecl(self: *C, f: *Flush, decl: *const Module.Decl) FlushDeclError!void
362362 const decl_block = self.decl_table.getPtr(decl).?;
363363 const gpa = self.base.allocator;
364364
365 if (decl_block.fwd_decl.items.len != 0) {
365 if (decl_block.typedefs.count() != 0) {
366366 try f.typedefs.ensureUnusedCapacity(gpa, @intCast(u32, decl_block.typedefs.count()));
367367 var it = decl_block.typedefs.iterator();
368368 while (it.next()) |new| {
......@@ -371,6 +371,9 @@ fn flushDecl(self: *C, f: *Flush, decl: *const Module.Decl) FlushDeclError!void
371371 try f.err_typedef_buf.appendSlice(gpa, new.value_ptr.rendered);
372372 }
373373 }
374 }
375
376 if (decl_block.fwd_decl.items.len != 0) {
374377 const buf = decl_block.fwd_decl.items;
375378 try f.all_buffers.append(gpa, .{
376379 .iov_base = buf.ptr,
test/behavior/basic.zig+42
......@@ -250,3 +250,45 @@ test "volatile load and store" {
250250 ptr.* += 1;
251251 try expect(ptr.* == 1235);
252252}
253
254fn fA() []const u8 {
255 return "a";
256}
257fn fB() []const u8 {
258 return "b";
259}
260
261test "call function pointer in struct" {
262 try expect(mem.eql(u8, f3(true), "a"));
263 try expect(mem.eql(u8, f3(false), "b"));
264}
265
266fn f3(x: bool) []const u8 {
267 var wrapper: FnPtrWrapper = .{
268 .fn_ptr = fB,
269 };
270
271 if (x) {
272 wrapper.fn_ptr = fA;
273 }
274
275 return wrapper.fn_ptr();
276}
277
278const FnPtrWrapper = struct {
279 fn_ptr: fn () []const u8,
280};
281
282test "const ptr from var variable" {
283 var x: u64 = undefined;
284 var y: u64 = undefined;
285
286 x = 78;
287 copy(&x, &y);
288
289 try expect(x == y);
290}
291
292fn copy(src: *const u64, dst: *u64) void {
293 dst.* = src.*;
294}