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 {...@@ -179,6 +179,14 @@ pub const DeclGen = struct {
179 return error.AnalysisFail;179 return error.AnalysisFail;
180 }180 }
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
182 fn renderDeclValue(190 fn renderDeclValue(
183 dg: *DeclGen,191 dg: *DeclGen,
184 writer: anytype,192 writer: anytype,
...@@ -200,14 +208,20 @@ pub const DeclGen = struct {...@@ -200,14 +208,20 @@ pub const DeclGen = struct {
200 return;208 return;
201 }209 }
202210
203 // Determine if we must pointer cast.
204 assert(decl.has_tv);211 assert(decl.has_tv);
205 if (ty.eql(decl.ty)) {212 // We shouldn't cast C function pointers as this is UB (when you call
206 try writer.writeByte('&');213 // them). The analysis until now should ensure that the C function
207 } else {214 // pointers are compatible. If they are not, then there is a bug
208 try writer.writeAll("(");215 // somewhere and we should let the C compiler tell us about it.
209 try dg.renderType(writer, ty);216 if (ty.castPtrToFn() == null) {
210 try writer.writeAll(")&");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 }
211 }225 }
212 try dg.renderDeclName(decl, writer);226 try dg.renderDeclName(decl, writer);
213 }227 }
...@@ -514,7 +528,190 @@ pub const DeclGen = struct {...@@ -514,7 +528,190 @@ pub const DeclGen = struct {
514 try w.writeByte(')');528 try w.writeByte(')');
515 }529 }
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
517 fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void {712 fn renderType(dg: *DeclGen, w: anytype, t: Type) error{ OutOfMemory, AnalysisFail }!void {
713 const target = dg.module.getTarget();
714
518 switch (t.zigTypeTag()) {715 switch (t.zigTypeTag()) {
519 .NoReturn => {716 .NoReturn => {
520 try w.writeAll("zig_noreturn void");717 try w.writeAll("zig_noreturn void");
...@@ -542,7 +739,7 @@ pub const DeclGen = struct {...@@ -542,7 +739,7 @@ pub const DeclGen = struct {
542 .c_longlong => try w.writeAll("long long"),739 .c_longlong => try w.writeAll("long long"),
543 .c_ulonglong => try w.writeAll("unsigned long long"),740 .c_ulonglong => try w.writeAll("unsigned long long"),
544 .int_signed, .int_unsigned => {741 .int_signed, .int_unsigned => {
545 const info = t.intInfo(dg.module.getTarget());742 const info = t.intInfo(target);
546 const sign_prefix = switch (info.signedness) {743 const sign_prefix = switch (info.signedness) {
547 .signed => "",744 .signed => "",
548 .unsigned => "u",745 .unsigned => "u",
...@@ -554,7 +751,6 @@ pub const DeclGen = struct {...@@ -554,7 +751,6 @@ pub const DeclGen = struct {
554 else => unreachable,751 else => unreachable,
555 }752 }
556 },753 },
557
558 .Float => {754 .Float => {
559 switch (t.tag()) {755 switch (t.tag()) {
560 .f32 => try w.writeAll("float"),756 .f32 => try w.writeAll("float"),
...@@ -565,196 +761,71 @@ pub const DeclGen = struct {...@@ -565,196 +761,71 @@ pub const DeclGen = struct {
565 else => unreachable,761 else => unreachable,
566 }762 }
567 },763 },
568
569 .Pointer => {764 .Pointer => {
570 if (t.isSlice()) {765 if (t.isSlice()) {
571 if (dg.typedefs.get(t)) |some| {766 const name = dg.getTypedefName(t) orelse
572 return w.writeAll(some.name);767 try dg.renderSliceTypedef(t);
573 }
574768
575 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);769 return w.writeAll(name);
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;
604 }770 }
771
605 if (t.castPtrToFn()) |fn_ty| {772 if (t.castPtrToFn()) |fn_ty| {
606 const fn_info = fn_ty.fnInfo();773 const name = dg.getTypedefName(t) orelse
607 try dg.renderType(w, fn_info.return_type);774 try dg.renderPtrToFnTypedef(t, fn_ty);
608 try w.writeAll(" (*)(");775
609 const param_len = fn_info.param_types.len;776 return w.writeAll(name);
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;
628 }777 }
629778
630 try dg.renderType(w, t.elemType());779 try dg.renderType(w, t.elemType());
631 try w.writeAll(" *");
632 if (t.isConstPtr()) {780 if (t.isConstPtr()) {
633 try w.writeAll("const ");781 try w.writeAll(" const");
634 }782 }
635 if (t.isVolatilePtr()) {783 if (t.isVolatilePtr()) {
636 try w.writeAll("volatile ");784 try w.writeAll(" volatile");
637 }785 }
786 return w.writeAll(" *");
638 },787 },
639 .Array => {788 .Array => {
789 // We are referencing the array so it will decay to a C pointer.
640 try dg.renderType(w, t.elemType());790 try dg.renderType(w, t.elemType());
641 try w.writeAll(" *");791 return w.writeAll(" *");
642 },792 },
643 .Optional => {793 .Optional => {
644 var opt_buf: Type.Payload.ElemType = undefined;794 var opt_buf: Type.Payload.ElemType = undefined;
645 const child_type = t.optionalChild(&opt_buf);795 const child_type = t.optionalChild(&opt_buf);
646 const target = dg.module.getTarget();796
647 if (t.isPtrLikeOptional()) {797 if (t.isPtrLikeOptional()) {
648 return dg.renderType(w, child_type);798 return dg.renderType(w, child_type);
649 } else if (dg.typedefs.get(t)) |some| {799 }
650 return w.writeAll(some.name);800
651 } else if (child_type.abiSize(target) == 0) {801 if (child_type.abiSize(target) == 0) {
652 return w.writeAll("bool");802 return w.writeAll("bool");
653 }803 }
654804
655 var buffer = std.ArrayList(u8).init(dg.typedefs.allocator);805 const name = dg.getTypedefName(t) orelse
656 defer buffer.deinit();806 try dg.renderOptionalTypedef(t, child_type);
657 const bw = buffer.writer();807
658808 return w.writeAll(name);
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 );
675 },809 },
676 .ErrorSet => {810 .ErrorSet => {
677 comptime std.debug.assert(Type.initTag(.anyerror).abiSize(builtin.target) == 2);811 comptime std.debug.assert(Type.initTag(.anyerror).abiSize(builtin.target) == 2);
678 try w.writeAll("uint16_t");812 return w.writeAll("uint16_t");
679 },813 },
680 .ErrorUnion => {814 .ErrorUnion => {
681 if (dg.typedefs.get(t)) |some| {815 if (t.errorUnionPayload().abiSize(target) == 0) {
682 return w.writeAll(some.name);816 return dg.renderType(w, t.errorUnionSet());
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 });
708 }817 }
709818
710 const rendered = buffer.toOwnedSlice();819 const name = dg.getTypedefName(t) orelse
711 errdefer dg.typedefs.allocator.free(rendered);820 try dg.renderErrorUnionTypedef(t);
712 const name = rendered[name_index .. rendered.len - 2];
713821
714 try dg.typedefs.ensureUnusedCapacity(1);822 return w.writeAll(name);
715 try w.writeAll(name);
716 dg.typedefs.putAssumeCapacityNoClobber(
717 try t.copy(dg.typedefs_arena),
718 .{ .name = name, .rendered = rendered },
719 );
720 },823 },
721 .Struct => {824 .Struct => {
722 if (dg.typedefs.get(t)) |some| {825 const name = dg.getTypedefName(t) orelse
723 return w.writeAll(some.name);826 try dg.renderStructTypedef(t);
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)});
747827
748 const rendered = buffer.toOwnedSlice();828 return w.writeAll(name);
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 );
758 },829 },
759 .Enum => {830 .Enum => {
760 // For enums, we simply use the integer tag type.831 // For enums, we simply use the integer tag type.
...@@ -763,12 +834,17 @@ pub const DeclGen = struct {...@@ -763,12 +834,17 @@ pub const DeclGen = struct {
763834
764 try dg.renderType(w, int_tag_ty);835 try dg.renderType(w, int_tag_ty);
765 },836 },
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
767 .Fn => unreachable, // This is a function body, not a function pointer.847 .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
773 .Null,849 .Null,
774 .Undefined,850 .Undefined,
...@@ -800,100 +876,14 @@ pub const DeclGen = struct {...@@ -800,100 +876,14 @@ pub const DeclGen = struct {
800 render_ty = render_ty.elemType();876 render_ty = render_ty.elemType();
801 }877 }
802878
803 // TODO this is duplicated from the code below and does not handle879 try dg.renderType(w, render_ty);
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 }
852880
853 if (render_ty.castPtrToFn()) |fn_ty| {881 const const_prefix = switch (mutability) {
854 const fn_info = fn_ty.fnInfo();882 .Const => "const ",
855 const ret_ty = fn_info.return_type;883 .Mut => "",
856 if (ret_ty.zigTypeTag() == .NoReturn) {884 };
857 // noreturn attribute is not allowed here.885 try w.print(" {s}", .{const_prefix});
858 try w.writeAll("void");886 try dg.writeCValue(w, name);
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 }
897 try w.writeAll(suffix.items);887 try w.writeAll(suffix.items);
898 }888 }
899889
...@@ -1224,10 +1214,11 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO...@@ -1224,10 +1214,11 @@ fn genBody(f: *Function, body: []const Air.Inst.Index) error{ AnalysisFail, OutO
1224fn airSliceField(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !CValue {1214fn airSliceField(f: *Function, inst: Air.Inst.Index, suffix: []const u8) !CValue {
1225 if (f.liveness.isUnused(inst)) return CValue.none;1215 if (f.liveness.isUnused(inst)) return CValue.none;
12261216
1217 const inst_ty = f.air.typeOfIndex(inst);
1227 const ty_op = f.air.instructions.items(.data)[inst].ty_op;1218 const ty_op = f.air.instructions.items(.data)[inst].ty_op;
1228 const operand = try f.resolveInst(ty_op.operand);1219 const operand = try f.resolveInst(ty_op.operand);
1229 const writer = f.object.writer();1220 const writer = f.object.writer();
1230 const local = try f.allocLocal(Type.initTag(.usize), .Const);1221 const local = try f.allocLocal(inst_ty, .Const);
1231 try writer.writeAll(" = ");1222 try writer.writeAll(" = ");
1232 try f.writeCValue(writer, operand);1223 try f.writeCValue(writer, operand);
1233 try writer.writeAll(suffix);1224 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...@@ -362,7 +362,7 @@ fn flushDecl(self: *C, f: *Flush, decl: *const Module.Decl) FlushDeclError!void
362 const decl_block = self.decl_table.getPtr(decl).?;362 const decl_block = self.decl_table.getPtr(decl).?;
363 const gpa = self.base.allocator;363 const gpa = self.base.allocator;
364364
365 if (decl_block.fwd_decl.items.len != 0) {365 if (decl_block.typedefs.count() != 0) {
366 try f.typedefs.ensureUnusedCapacity(gpa, @intCast(u32, decl_block.typedefs.count()));366 try f.typedefs.ensureUnusedCapacity(gpa, @intCast(u32, decl_block.typedefs.count()));
367 var it = decl_block.typedefs.iterator();367 var it = decl_block.typedefs.iterator();
368 while (it.next()) |new| {368 while (it.next()) |new| {
...@@ -371,6 +371,9 @@ fn flushDecl(self: *C, f: *Flush, decl: *const Module.Decl) FlushDeclError!void...@@ -371,6 +371,9 @@ fn flushDecl(self: *C, f: *Flush, decl: *const Module.Decl) FlushDeclError!void
371 try f.err_typedef_buf.appendSlice(gpa, new.value_ptr.rendered);371 try f.err_typedef_buf.appendSlice(gpa, new.value_ptr.rendered);
372 }372 }
373 }373 }
374 }
375
376 if (decl_block.fwd_decl.items.len != 0) {
374 const buf = decl_block.fwd_decl.items;377 const buf = decl_block.fwd_decl.items;
375 try f.all_buffers.append(gpa, .{378 try f.all_buffers.append(gpa, .{
376 .iov_base = buf.ptr,379 .iov_base = buf.ptr,
test/behavior/basic.zig+42
...@@ -250,3 +250,45 @@ test "volatile load and store" {...@@ -250,3 +250,45 @@ test "volatile load and store" {
250 ptr.* += 1;250 ptr.* += 1;
251 try expect(ptr.* == 1235);251 try expect(ptr.* == 1235);
252}252}
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}