| author | |
| committer | |
| log | b34f994c0ba2d87fce2a3409d6bcfa7a5ebe78ff |
| tree | 436e0ad81e8baddbdcbe5d7148338bcac11a23df |
| parent | 0866fa9d1d46f3c66a4adcaf1d863e762f874c6c |
This commit updates stage2 to enforce the property that the syntax
`fn()void` is a function *body* not a *pointer*. To get a pointer, the
syntax `*const fn()void` is required.
ZIR puts function alignment into the func instruction rather than the
decl because this way it makes it into function types. LLVM backend
respects function alignments.
Struct and Union have methods `fieldSrcLoc` to help look up source
locations of their fields. These trigger full loading, tokenization, and
parsing of source files, so should only be called once it is confirmed
that an error message needs to be printed.
There are some nice new error hints for explaining why a type is
required to be comptime, particularly for structs that contain function
body types.
`Type.requiresComptime` is now moved into Sema because it can fail and
might need to trigger field type resolution. Comptime pointer loading
takes into account types that do not have a well-defined memory layout
and does not try to compute a byte offset for them.
`fn()void` syntax no longer secretly makes a pointer. You get a function
body type, which requires comptime. However a pointer to a function body
can be runtime known (obviously).
Compile errors that report "expected pointer, found ..." are factored
out into convenience functions `checkPtrOperand` and `checkPtrType` and
have a note about function pointers.
Implemented `Value.hash` for functions, enum literals, and undefined values.
stage1 is not updated to this (yet?), so some workarounds and disabled
tests are needed to keep everything working. Should we update stage1 to
these new type semantics? Yes probably because I don't want to add too
much conditional compilation logic in the std lib for the different
backends.24 files changed, 856 insertions(+), 354 deletions(-)
lib/std/builtin.zig+7-1| ... | @@ -730,10 +730,16 @@ pub const CompilerBackend = enum(u64) { | ... | @@ -730,10 +730,16 @@ pub const CompilerBackend = enum(u64) { |
| 730 | /// therefore must be kept in sync with the compiler implementation. | 730 | /// therefore must be kept in sync with the compiler implementation. |
| 731 | pub const TestFn = struct { | 731 | pub const TestFn = struct { |
| 732 | name: []const u8, | 732 | name: []const u8, |
| 733 | func: fn () anyerror!void, | 733 | func: testFnProto, |
| 734 | async_frame_size: ?usize, | 734 | async_frame_size: ?usize, |
| 735 | }; | 735 | }; |
| 736 | 736 | ||
| 737 | /// stage1 is *wrong*. It is not yet updated to support the new function type semantics. | ||
| 738 | const testFnProto = switch (builtin.zig_backend) { | ||
| 739 | .stage1 => fn () anyerror!void, // wrong! | ||
| 740 | else => *const fn () anyerror!void, | ||
| 741 | }; | ||
| 742 | |||
| 737 | /// This function type is used by the Zig language code generation and | 743 | /// This function type is used by the Zig language code generation and |
| 738 | /// therefore must be kept in sync with the compiler implementation. | 744 | /// therefore must be kept in sync with the compiler implementation. |
| 739 | pub const PanicFn = fn ([]const u8, ?*StackTrace) noreturn; | 745 | pub const PanicFn = fn ([]const u8, ?*StackTrace) noreturn; |
src/AstGen.zig+4-6| ... | @@ -3240,7 +3240,8 @@ fn fnDecl( | ... | @@ -3240,7 +3240,8 @@ fn fnDecl( |
| 3240 | const doc_comment_index = try astgen.docCommentAsString(fn_proto.firstToken()); | 3240 | const doc_comment_index = try astgen.docCommentAsString(fn_proto.firstToken()); |
| 3241 | 3241 | ||
| 3242 | const has_section_or_addrspace = fn_proto.ast.section_expr != 0 or fn_proto.ast.addrspace_expr != 0; | 3242 | const has_section_or_addrspace = fn_proto.ast.section_expr != 0 or fn_proto.ast.addrspace_expr != 0; |
| 3243 | wip_members.nextDecl(is_pub, is_export, fn_proto.ast.align_expr != 0, has_section_or_addrspace); | 3243 | // Alignment is passed in the func instruction in this case. |
| 3244 | wip_members.nextDecl(is_pub, is_export, false, has_section_or_addrspace); | ||
| 3244 | 3245 | ||
| 3245 | var params_scope = &fn_gz.base; | 3246 | var params_scope = &fn_gz.base; |
| 3246 | const is_var_args = is_var_args: { | 3247 | const is_var_args = is_var_args: { |
| ... | @@ -3380,7 +3381,7 @@ fn fnDecl( | ... | @@ -3380,7 +3381,7 @@ fn fnDecl( |
| 3380 | .param_block = block_inst, | 3381 | .param_block = block_inst, |
| 3381 | .body_gz = null, | 3382 | .body_gz = null, |
| 3382 | .cc = cc, | 3383 | .cc = cc, |
| 3383 | .align_inst = .none, // passed in the per-decl data | 3384 | .align_inst = align_inst, |
| 3384 | .lib_name = lib_name, | 3385 | .lib_name = lib_name, |
| 3385 | .is_var_args = is_var_args, | 3386 | .is_var_args = is_var_args, |
| 3386 | .is_inferred_error = false, | 3387 | .is_inferred_error = false, |
| ... | @@ -3423,7 +3424,7 @@ fn fnDecl( | ... | @@ -3423,7 +3424,7 @@ fn fnDecl( |
| 3423 | .ret_br = ret_br, | 3424 | .ret_br = ret_br, |
| 3424 | .body_gz = &fn_gz, | 3425 | .body_gz = &fn_gz, |
| 3425 | .cc = cc, | 3426 | .cc = cc, |
| 3426 | .align_inst = .none, // passed in the per-decl data | 3427 | .align_inst = align_inst, |
| 3427 | .lib_name = lib_name, | 3428 | .lib_name = lib_name, |
| 3428 | .is_var_args = is_var_args, | 3429 | .is_var_args = is_var_args, |
| 3429 | .is_inferred_error = is_inferred_error, | 3430 | .is_inferred_error = is_inferred_error, |
| ... | @@ -3449,9 +3450,6 @@ fn fnDecl( | ... | @@ -3449,9 +3450,6 @@ fn fnDecl( |
| 3449 | wip_members.appendToDecl(fn_name_str_index); | 3450 | wip_members.appendToDecl(fn_name_str_index); |
| 3450 | wip_members.appendToDecl(block_inst); | 3451 | wip_members.appendToDecl(block_inst); |
| 3451 | wip_members.appendToDecl(doc_comment_index); | 3452 | wip_members.appendToDecl(doc_comment_index); |
| 3452 | if (align_inst != .none) { | ||
| 3453 | wip_members.appendToDecl(@enumToInt(align_inst)); | ||
| 3454 | } | ||
| 3455 | if (has_section_or_addrspace) { | 3453 | if (has_section_or_addrspace) { |
| 3456 | wip_members.appendToDecl(@enumToInt(section_inst)); | 3454 | wip_members.appendToDecl(@enumToInt(section_inst)); |
| 3457 | wip_members.appendToDecl(@enumToInt(addrspace_inst)); | 3455 | wip_members.appendToDecl(@enumToInt(addrspace_inst)); |
src/Module.zig+117-2| ... | @@ -898,6 +898,45 @@ pub const Struct = struct { | ... | @@ -898,6 +898,45 @@ pub const Struct = struct { |
| 898 | }; | 898 | }; |
| 899 | } | 899 | } |
| 900 | 900 | ||
| 901 | pub fn fieldSrcLoc(s: Struct, gpa: Allocator, query: FieldSrcQuery) SrcLoc { | ||
| 902 | @setCold(true); | ||
| 903 | const tree = s.owner_decl.getFileScope().getTree(gpa) catch |err| { | ||
| 904 | // In this case we emit a warning + a less precise source location. | ||
| 905 | log.warn("unable to load {s}: {s}", .{ | ||
| 906 | s.owner_decl.getFileScope().sub_file_path, @errorName(err), | ||
| 907 | }); | ||
| 908 | return s.srcLoc(); | ||
| 909 | }; | ||
| 910 | const node = s.owner_decl.relativeToNodeIndex(s.node_offset); | ||
| 911 | const node_tags = tree.nodes.items(.tag); | ||
| 912 | const file = s.owner_decl.getFileScope(); | ||
| 913 | switch (node_tags[node]) { | ||
| 914 | .container_decl, | ||
| 915 | .container_decl_trailing, | ||
| 916 | => return queryFieldSrc(tree.*, query, file, tree.containerDecl(node)), | ||
| 917 | .container_decl_two, .container_decl_two_trailing => { | ||
| 918 | var buffer: [2]Ast.Node.Index = undefined; | ||
| 919 | return queryFieldSrc(tree.*, query, file, tree.containerDeclTwo(&buffer, node)); | ||
| 920 | }, | ||
| 921 | .container_decl_arg, | ||
| 922 | .container_decl_arg_trailing, | ||
| 923 | => return queryFieldSrc(tree.*, query, file, tree.containerDeclArg(node)), | ||
| 924 | |||
| 925 | .tagged_union, | ||
| 926 | .tagged_union_trailing, | ||
| 927 | => return queryFieldSrc(tree.*, query, file, tree.taggedUnion(node)), | ||
| 928 | .tagged_union_two, .tagged_union_two_trailing => { | ||
| 929 | var buffer: [2]Ast.Node.Index = undefined; | ||
| 930 | return queryFieldSrc(tree.*, query, file, tree.taggedUnionTwo(&buffer, node)); | ||
| 931 | }, | ||
| 932 | .tagged_union_enum_tag, | ||
| 933 | .tagged_union_enum_tag_trailing, | ||
| 934 | => return queryFieldSrc(tree.*, query, file, tree.taggedUnionEnumTag(node)), | ||
| 935 | |||
| 936 | else => unreachable, | ||
| 937 | } | ||
| 938 | } | ||
| 939 | |||
| 901 | pub fn haveFieldTypes(s: Struct) bool { | 940 | pub fn haveFieldTypes(s: Struct) bool { |
| 902 | return switch (s.status) { | 941 | return switch (s.status) { |
| 903 | .none, | 942 | .none, |
| ... | @@ -1063,6 +1102,33 @@ pub const Union = struct { | ... | @@ -1063,6 +1102,33 @@ pub const Union = struct { |
| 1063 | }; | 1102 | }; |
| 1064 | } | 1103 | } |
| 1065 | 1104 | ||
| 1105 | pub fn fieldSrcLoc(u: Union, gpa: Allocator, query: FieldSrcQuery) SrcLoc { | ||
| 1106 | @setCold(true); | ||
| 1107 | const tree = u.owner_decl.getFileScope().getTree(gpa) catch |err| { | ||
| 1108 | // In this case we emit a warning + a less precise source location. | ||
| 1109 | log.warn("unable to load {s}: {s}", .{ | ||
| 1110 | u.owner_decl.getFileScope().sub_file_path, @errorName(err), | ||
| 1111 | }); | ||
| 1112 | return u.srcLoc(); | ||
| 1113 | }; | ||
| 1114 | const node = u.owner_decl.relativeToNodeIndex(u.node_offset); | ||
| 1115 | const node_tags = tree.nodes.items(.tag); | ||
| 1116 | const file = u.owner_decl.getFileScope(); | ||
| 1117 | switch (node_tags[node]) { | ||
| 1118 | .container_decl, | ||
| 1119 | .container_decl_trailing, | ||
| 1120 | => return queryFieldSrc(tree.*, query, file, tree.containerDecl(node)), | ||
| 1121 | .container_decl_two, .container_decl_two_trailing => { | ||
| 1122 | var buffer: [2]Ast.Node.Index = undefined; | ||
| 1123 | return queryFieldSrc(tree.*, query, file, tree.containerDeclTwo(&buffer, node)); | ||
| 1124 | }, | ||
| 1125 | .container_decl_arg, | ||
| 1126 | .container_decl_arg_trailing, | ||
| 1127 | => return queryFieldSrc(tree.*, query, file, tree.containerDeclArg(node)), | ||
| 1128 | else => unreachable, | ||
| 1129 | } | ||
| 1130 | } | ||
| 1131 | |||
| 1066 | pub fn haveFieldTypes(u: Union) bool { | 1132 | pub fn haveFieldTypes(u: Union) bool { |
| 1067 | return switch (u.status) { | 1133 | return switch (u.status) { |
| 1068 | .none, | 1134 | .none, |
| ... | @@ -4662,8 +4728,8 @@ pub fn createAnonymousDeclFromDeclNamed( | ... | @@ -4662,8 +4728,8 @@ pub fn createAnonymousDeclFromDeclNamed( |
| 4662 | new_decl.src_line = src_decl.src_line; | 4728 | new_decl.src_line = src_decl.src_line; |
| 4663 | new_decl.ty = typed_value.ty; | 4729 | new_decl.ty = typed_value.ty; |
| 4664 | new_decl.val = typed_value.val; | 4730 | new_decl.val = typed_value.val; |
| 4665 | new_decl.align_val = Value.initTag(.null_value); | 4731 | new_decl.align_val = Value.@"null"; |
| 4666 | new_decl.linksection_val = Value.initTag(.null_value); | 4732 | new_decl.linksection_val = Value.@"null"; |
| 4667 | new_decl.has_tv = true; | 4733 | new_decl.has_tv = true; |
| 4668 | new_decl.analysis = .complete; | 4734 | new_decl.analysis = .complete; |
| 4669 | new_decl.generation = mod.generation; | 4735 | new_decl.generation = mod.generation; |
| ... | @@ -4905,6 +4971,55 @@ pub const PeerTypeCandidateSrc = union(enum) { | ... | @@ -4905,6 +4971,55 @@ pub const PeerTypeCandidateSrc = union(enum) { |
| 4905 | } | 4971 | } |
| 4906 | }; | 4972 | }; |
| 4907 | 4973 | ||
| 4974 | const FieldSrcQuery = struct { | ||
| 4975 | index: usize, | ||
| 4976 | range: enum { name, type, value, alignment }, | ||
| 4977 | }; | ||
| 4978 | |||
| 4979 | fn queryFieldSrc( | ||
| 4980 | tree: Ast, | ||
| 4981 | query: FieldSrcQuery, | ||
| 4982 | file_scope: *File, | ||
| 4983 | container_decl: Ast.full.ContainerDecl, | ||
| 4984 | ) SrcLoc { | ||
| 4985 | const node_tags = tree.nodes.items(.tag); | ||
| 4986 | var field_index: usize = 0; | ||
| 4987 | for (container_decl.ast.members) |member_node| { | ||
| 4988 | const field = switch (node_tags[member_node]) { | ||
| 4989 | .container_field_init => tree.containerFieldInit(member_node), | ||
| 4990 | .container_field_align => tree.containerFieldAlign(member_node), | ||
| 4991 | .container_field => tree.containerField(member_node), | ||
| 4992 | else => continue, | ||
| 4993 | }; | ||
| 4994 | if (field_index == query.index) { | ||
| 4995 | return switch (query.range) { | ||
| 4996 | .name => .{ | ||
| 4997 | .file_scope = file_scope, | ||
| 4998 | .parent_decl_node = 0, | ||
| 4999 | .lazy = .{ .token_abs = field.ast.name_token }, | ||
| 5000 | }, | ||
| 5001 | .type => .{ | ||
| 5002 | .file_scope = file_scope, | ||
| 5003 | .parent_decl_node = 0, | ||
| 5004 | .lazy = .{ .node_abs = field.ast.type_expr }, | ||
| 5005 | }, | ||
| 5006 | .value => .{ | ||
| 5007 | .file_scope = file_scope, | ||
| 5008 | .parent_decl_node = 0, | ||
| 5009 | .lazy = .{ .node_abs = field.ast.value_expr }, | ||
| 5010 | }, | ||
| 5011 | .alignment => .{ | ||
| 5012 | .file_scope = file_scope, | ||
| 5013 | .parent_decl_node = 0, | ||
| 5014 | .lazy = .{ .node_abs = field.ast.align_expr }, | ||
| 5015 | }, | ||
| 5016 | }; | ||
| 5017 | } | ||
| 5018 | field_index += 1; | ||
| 5019 | } | ||
| 5020 | unreachable; | ||
| 5021 | } | ||
| 5022 | |||
| 4908 | /// Called from `performAllTheWork`, after all AstGen workers have finished, | 5023 | /// Called from `performAllTheWork`, after all AstGen workers have finished, |
| 4909 | /// and before the main semantic analysis loop begins. | 5024 | /// and before the main semantic analysis loop begins. |
| 4910 | pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { | 5025 | pub fn processOutdatedAndDeletedDecls(mod: *Module) !void { |
src/Sema.zig+473-103| ... | @@ -4147,7 +4147,7 @@ fn analyzeCall( | ... | @@ -4147,7 +4147,7 @@ fn analyzeCall( |
| 4147 | const gpa = sema.gpa; | 4147 | const gpa = sema.gpa; |
| 4148 | 4148 | ||
| 4149 | const is_comptime_call = block.is_comptime or modifier == .compile_time or | 4149 | const is_comptime_call = block.is_comptime or modifier == .compile_time or |
| 4150 | func_ty_info.return_type.requiresComptime(); | 4150 | try sema.typeRequiresComptime(block, func_src, func_ty_info.return_type); |
| 4151 | const is_inline_call = is_comptime_call or modifier == .always_inline or | 4151 | const is_inline_call = is_comptime_call or modifier == .always_inline or |
| 4152 | func_ty_info.cc == .Inline; | 4152 | func_ty_info.cc == .Inline; |
| 4153 | const result: Air.Inst.Ref = if (is_inline_call) res: { | 4153 | const result: Air.Inst.Ref = if (is_inline_call) res: { |
| ... | @@ -4576,7 +4576,7 @@ fn analyzeCall( | ... | @@ -4576,7 +4576,7 @@ fn analyzeCall( |
| 4576 | } | 4576 | } |
| 4577 | } else if (is_anytype) { | 4577 | } else if (is_anytype) { |
| 4578 | const arg_ty = sema.typeOf(arg); | 4578 | const arg_ty = sema.typeOf(arg); |
| 4579 | if (arg_ty.requiresComptime()) { | 4579 | if (try sema.typeRequiresComptime(block, arg_src, arg_ty)) { |
| 4580 | const arg_val = try sema.resolveConstValue(block, arg_src, arg); | 4580 | const arg_val = try sema.resolveConstValue(block, arg_src, arg); |
| 4581 | const child_arg = try child_sema.addConstant(arg_ty, arg_val); | 4581 | const child_arg = try child_sema.addConstant(arg_ty, arg_val); |
| 4582 | child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); | 4582 | child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg); |
| ... | @@ -5430,7 +5430,6 @@ fn funcCommon( | ... | @@ -5430,7 +5430,6 @@ fn funcCommon( |
| 5430 | src_locs: Zir.Inst.Func.SrcLocs, | 5430 | src_locs: Zir.Inst.Func.SrcLocs, |
| 5431 | opt_lib_name: ?[]const u8, | 5431 | opt_lib_name: ?[]const u8, |
| 5432 | ) CompileError!Air.Inst.Ref { | 5432 | ) CompileError!Air.Inst.Ref { |
| 5433 | const src: LazySrcLoc = .{ .node_offset = src_node_offset }; | ||
| 5434 | const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; | 5433 | const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; |
| 5435 | 5434 | ||
| 5436 | // The return type body might be a type expression that depends on generic parameters. | 5435 | // The return type body might be a type expression that depends on generic parameters. |
| ... | @@ -5481,11 +5480,22 @@ fn funcCommon( | ... | @@ -5481,11 +5480,22 @@ fn funcCommon( |
| 5481 | errdefer if (maybe_inferred_error_set_node) |node| sema.gpa.destroy(node); | 5480 | errdefer if (maybe_inferred_error_set_node) |node| sema.gpa.destroy(node); |
| 5482 | // Note: no need to errdefer since this will still be in its default state at the end of the function. | 5481 | // Note: no need to errdefer since this will still be in its default state at the end of the function. |
| 5483 | 5482 | ||
| 5483 | const target = mod.getTarget(); | ||
| 5484 | |||
| 5484 | const fn_ty: Type = fn_ty: { | 5485 | const fn_ty: Type = fn_ty: { |
| 5486 | const alignment: u32 = if (align_val.tag() == .null_value) 0 else a: { | ||
| 5487 | const alignment = @intCast(u32, align_val.toUnsignedInt()); | ||
| 5488 | if (alignment == target_util.defaultFunctionAlignment(target)) { | ||
| 5489 | break :a 0; | ||
| 5490 | } else { | ||
| 5491 | break :a alignment; | ||
| 5492 | } | ||
| 5493 | }; | ||
| 5494 | |||
| 5485 | // Hot path for some common function types. | 5495 | // Hot path for some common function types. |
| 5486 | // TODO can we eliminate some of these Type tag values? seems unnecessarily complicated. | 5496 | // TODO can we eliminate some of these Type tag values? seems unnecessarily complicated. |
| 5487 | if (!is_generic and block.params.items.len == 0 and !var_args and | 5497 | if (!is_generic and block.params.items.len == 0 and !var_args and |
| 5488 | align_val.tag() == .null_value and !inferred_error_set) | 5498 | alignment == 0 and !inferred_error_set) |
| 5489 | { | 5499 | { |
| 5490 | if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { | 5500 | if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { |
| 5491 | break :fn_ty Type.initTag(.fn_noreturn_no_args); | 5501 | break :fn_ty Type.initTag(.fn_noreturn_no_args); |
| ... | @@ -5507,16 +5517,15 @@ fn funcCommon( | ... | @@ -5507,16 +5517,15 @@ fn funcCommon( |
| 5507 | const param_types = try sema.arena.alloc(Type, block.params.items.len); | 5517 | const param_types = try sema.arena.alloc(Type, block.params.items.len); |
| 5508 | const comptime_params = try sema.arena.alloc(bool, block.params.items.len); | 5518 | const comptime_params = try sema.arena.alloc(bool, block.params.items.len); |
| 5509 | for (block.params.items) |param, i| { | 5519 | for (block.params.items) |param, i| { |
| 5520 | const param_src: LazySrcLoc = .{ .node_offset = src_node_offset }; // TODO better src | ||
| 5510 | param_types[i] = param.ty; | 5521 | param_types[i] = param.ty; |
| 5511 | comptime_params[i] = param.is_comptime or param.ty.requiresComptime(); | 5522 | comptime_params[i] = param.is_comptime or |
| 5523 | try sema.typeRequiresComptime(block, param_src, param.ty); | ||
| 5512 | is_generic = is_generic or comptime_params[i] or param.ty.tag() == .generic_poison; | 5524 | is_generic = is_generic or comptime_params[i] or param.ty.tag() == .generic_poison; |
| 5513 | } | 5525 | } |
| 5514 | 5526 | ||
| 5515 | if (align_val.tag() != .null_value) { | 5527 | is_generic = is_generic or |
| 5516 | return sema.fail(block, src, "TODO implement support for function prototypes to have alignment specified", .{}); | 5528 | try sema.typeRequiresComptime(block, ret_ty_src, bare_return_type); |
| 5517 | } | ||
| 5518 | |||
| 5519 | is_generic = is_generic or bare_return_type.requiresComptime(); | ||
| 5520 | 5529 | ||
| 5521 | const return_type = if (!inferred_error_set or bare_return_type.tag() == .generic_poison) | 5530 | const return_type = if (!inferred_error_set or bare_return_type.tag() == .generic_poison) |
| 5522 | bare_return_type | 5531 | bare_return_type |
| ... | @@ -5537,6 +5546,7 @@ fn funcCommon( | ... | @@ -5537,6 +5546,7 @@ fn funcCommon( |
| 5537 | .comptime_params = comptime_params.ptr, | 5546 | .comptime_params = comptime_params.ptr, |
| 5538 | .return_type = return_type, | 5547 | .return_type = return_type, |
| 5539 | .cc = cc, | 5548 | .cc = cc, |
| 5549 | .alignment = alignment, | ||
| 5540 | .is_var_args = var_args, | 5550 | .is_var_args = var_args, |
| 5541 | .is_generic = is_generic, | 5551 | .is_generic = is_generic, |
| 5542 | }); | 5552 | }); |
| ... | @@ -5550,7 +5560,6 @@ fn funcCommon( | ... | @@ -5550,7 +5560,6 @@ fn funcCommon( |
| 5550 | lib_name, @errorName(err), | 5560 | lib_name, @errorName(err), |
| 5551 | }); | 5561 | }); |
| 5552 | }; | 5562 | }; |
| 5553 | const target = mod.getTarget(); | ||
| 5554 | if (target_util.is_libc_lib_name(target, lib_name)) { | 5563 | if (target_util.is_libc_lib_name(target, lib_name)) { |
| 5555 | if (!mod.comp.bin_file.options.link_libc) { | 5564 | if (!mod.comp.bin_file.options.link_libc) { |
| 5556 | return sema.fail( | 5565 | return sema.fail( |
| ... | @@ -5591,12 +5600,7 @@ fn funcCommon( | ... | @@ -5591,12 +5600,7 @@ fn funcCommon( |
| 5591 | } | 5600 | } |
| 5592 | 5601 | ||
| 5593 | if (body_inst == 0) { | 5602 | if (body_inst == 0) { |
| 5594 | const fn_ptr_ty = try Type.ptr(sema.arena, .{ | 5603 | return sema.addType(fn_ty); |
| 5595 | .pointee_type = fn_ty, | ||
| 5596 | .@"addrspace" = .generic, | ||
| 5597 | .mutable = false, | ||
| 5598 | }); | ||
| 5599 | return sema.addType(fn_ptr_ty); | ||
| 5600 | } | 5604 | } |
| 5601 | 5605 | ||
| 5602 | const is_inline = fn_ty.fnCallingConvention() == .Inline; | 5606 | const is_inline = fn_ty.fnCallingConvention() == .Inline; |
| ... | @@ -5632,7 +5636,7 @@ fn zirParam( | ... | @@ -5632,7 +5636,7 @@ fn zirParam( |
| 5632 | sema: *Sema, | 5636 | sema: *Sema, |
| 5633 | block: *Block, | 5637 | block: *Block, |
| 5634 | inst: Zir.Inst.Index, | 5638 | inst: Zir.Inst.Index, |
| 5635 | is_comptime: bool, | 5639 | comptime_syntax: bool, |
| 5636 | ) CompileError!void { | 5640 | ) CompileError!void { |
| 5637 | const inst_data = sema.code.instructions.items(.data)[inst].pl_tok; | 5641 | const inst_data = sema.code.instructions.items(.data)[inst].pl_tok; |
| 5638 | const src = inst_data.src(); | 5642 | const src = inst_data.src(); |
| ... | @@ -5669,7 +5673,7 @@ fn zirParam( | ... | @@ -5669,7 +5673,7 @@ fn zirParam( |
| 5669 | // insert an anytype parameter. | 5673 | // insert an anytype parameter. |
| 5670 | try block.params.append(sema.gpa, .{ | 5674 | try block.params.append(sema.gpa, .{ |
| 5671 | .ty = Type.initTag(.generic_poison), | 5675 | .ty = Type.initTag(.generic_poison), |
| 5672 | .is_comptime = is_comptime, | 5676 | .is_comptime = comptime_syntax, |
| 5673 | }); | 5677 | }); |
| 5674 | try sema.inst_map.putNoClobber(sema.gpa, inst, .generic_poison); | 5678 | try sema.inst_map.putNoClobber(sema.gpa, inst, .generic_poison); |
| 5675 | return; | 5679 | return; |
| ... | @@ -5677,8 +5681,10 @@ fn zirParam( | ... | @@ -5677,8 +5681,10 @@ fn zirParam( |
| 5677 | else => |e| return e, | 5681 | else => |e| return e, |
| 5678 | } | 5682 | } |
| 5679 | }; | 5683 | }; |
| 5684 | const is_comptime = comptime_syntax or | ||
| 5685 | try sema.typeRequiresComptime(block, src, param_ty); | ||
| 5680 | if (sema.inst_map.get(inst)) |arg| { | 5686 | if (sema.inst_map.get(inst)) |arg| { |
| 5681 | if (is_comptime or param_ty.requiresComptime()) { | 5687 | if (is_comptime) { |
| 5682 | // We have a comptime value for this parameter so it should be elided from the | 5688 | // We have a comptime value for this parameter so it should be elided from the |
| 5683 | // function type of the function instruction in this block. | 5689 | // function type of the function instruction in this block. |
| 5684 | const coerced_arg = try sema.coerce(block, param_ty, arg, src); | 5690 | const coerced_arg = try sema.coerce(block, param_ty, arg, src); |
| ... | @@ -5692,7 +5698,7 @@ fn zirParam( | ... | @@ -5692,7 +5698,7 @@ fn zirParam( |
| 5692 | 5698 | ||
| 5693 | try block.params.append(sema.gpa, .{ | 5699 | try block.params.append(sema.gpa, .{ |
| 5694 | .ty = param_ty, | 5700 | .ty = param_ty, |
| 5695 | .is_comptime = is_comptime or param_ty.requiresComptime(), | 5701 | .is_comptime = is_comptime, |
| 5696 | }); | 5702 | }); |
| 5697 | const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison)); | 5703 | const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison)); |
| 5698 | try sema.inst_map.putNoClobber(sema.gpa, inst, result); | 5704 | try sema.inst_map.putNoClobber(sema.gpa, inst, result); |
| ... | @@ -5702,9 +5708,10 @@ fn zirParamAnytype( | ... | @@ -5702,9 +5708,10 @@ fn zirParamAnytype( |
| 5702 | sema: *Sema, | 5708 | sema: *Sema, |
| 5703 | block: *Block, | 5709 | block: *Block, |
| 5704 | inst: Zir.Inst.Index, | 5710 | inst: Zir.Inst.Index, |
| 5705 | is_comptime: bool, | 5711 | comptime_syntax: bool, |
| 5706 | ) CompileError!void { | 5712 | ) CompileError!void { |
| 5707 | const inst_data = sema.code.instructions.items(.data)[inst].str_tok; | 5713 | const inst_data = sema.code.instructions.items(.data)[inst].str_tok; |
| 5714 | const src = inst_data.src(); | ||
| 5708 | const param_name = inst_data.get(sema.code); | 5715 | const param_name = inst_data.get(sema.code); |
| 5709 | 5716 | ||
| 5710 | // TODO check if param_name shadows a Decl. This only needs to be done if | 5717 | // TODO check if param_name shadows a Decl. This only needs to be done if |
| ... | @@ -5713,7 +5720,7 @@ fn zirParamAnytype( | ... | @@ -5713,7 +5720,7 @@ fn zirParamAnytype( |
| 5713 | 5720 | ||
| 5714 | if (sema.inst_map.get(inst)) |air_ref| { | 5721 | if (sema.inst_map.get(inst)) |air_ref| { |
| 5715 | const param_ty = sema.typeOf(air_ref); | 5722 | const param_ty = sema.typeOf(air_ref); |
| 5716 | if (is_comptime or param_ty.requiresComptime()) { | 5723 | if (comptime_syntax or try sema.typeRequiresComptime(block, src, param_ty)) { |
| 5717 | // We have a comptime value for this parameter so it should be elided from the | 5724 | // We have a comptime value for this parameter so it should be elided from the |
| 5718 | // function type of the function instruction in this block. | 5725 | // function type of the function instruction in this block. |
| 5719 | return; | 5726 | return; |
| ... | @@ -5730,7 +5737,7 @@ fn zirParamAnytype( | ... | @@ -5730,7 +5737,7 @@ fn zirParamAnytype( |
| 5730 | 5737 | ||
| 5731 | try block.params.append(sema.gpa, .{ | 5738 | try block.params.append(sema.gpa, .{ |
| 5732 | .ty = Type.initTag(.generic_poison), | 5739 | .ty = Type.initTag(.generic_poison), |
| 5733 | .is_comptime = is_comptime, | 5740 | .is_comptime = comptime_syntax, |
| 5734 | }); | 5741 | }); |
| 5735 | try sema.inst_map.put(sema.gpa, inst, .generic_poison); | 5742 | try sema.inst_map.put(sema.gpa, inst, .generic_poison); |
| 5736 | } | 5743 | } |
| ... | @@ -11118,8 +11125,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai | ... | @@ -11118,8 +11125,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai |
| 11118 | 11125 | ||
| 11119 | const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | 11126 | const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 11120 | const type_res = try sema.resolveType(block, src, extra.lhs); | 11127 | const type_res = try sema.resolveType(block, src, extra.lhs); |
| 11121 | if (type_res.zigTypeTag() != .Pointer) | 11128 | try sema.checkPtrType(block, type_src, type_res); |
| 11122 | return sema.fail(block, type_src, "expected pointer, found '{}'", .{type_res}); | ||
| 11123 | const ptr_align = type_res.ptrAlignment(sema.mod.getTarget()); | 11129 | const ptr_align = type_res.ptrAlignment(sema.mod.getTarget()); |
| 11124 | 11130 | ||
| 11125 | if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| { | 11131 | if (try sema.resolveDefinedValue(block, operand_src, operand_coerced)) |val| { |
| ... | @@ -11176,16 +11182,8 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air | ... | @@ -11176,16 +11182,8 @@ fn zirPtrCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air |
| 11176 | const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); | 11182 | const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs); |
| 11177 | const operand = sema.resolveInst(extra.rhs); | 11183 | const operand = sema.resolveInst(extra.rhs); |
| 11178 | const operand_ty = sema.typeOf(operand); | 11184 | const operand_ty = sema.typeOf(operand); |
| 11179 | if (operand_ty.zigTypeTag() != .Pointer) { | 11185 | try sema.checkPtrType(block, dest_ty_src, dest_ty); |
| 11180 | return sema.fail(block, operand_src, "expected pointer, found {s} type '{}'", .{ | 11186 | try sema.checkPtrOperand(block, operand_src, operand_ty); |
| 11181 | @tagName(operand_ty.zigTypeTag()), operand_ty, | ||
| 11182 | }); | ||
| 11183 | } | ||
| 11184 | if (dest_ty.zigTypeTag() != .Pointer) { | ||
| 11185 | return sema.fail(block, dest_ty_src, "expected pointer, found {s} type '{}'", .{ | ||
| 11186 | @tagName(dest_ty.zigTypeTag()), dest_ty, | ||
| 11187 | }); | ||
| 11188 | } | ||
| 11189 | return sema.coerceCompatiblePtrs(block, dest_ty, operand, operand_src); | 11187 | return sema.coerceCompatiblePtrs(block, dest_ty, operand, operand_src); |
| 11190 | } | 11188 | } |
| 11191 | 11189 | ||
| ... | @@ -11264,7 +11262,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A | ... | @@ -11264,7 +11262,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 11264 | 11262 | ||
| 11265 | // TODO in addition to pointers, this instruction is supposed to work for | 11263 | // TODO in addition to pointers, this instruction is supposed to work for |
| 11266 | // pointer-like optionals and slices. | 11264 | // pointer-like optionals and slices. |
| 11267 | try sema.checkPtrType(block, ptr_src, ptr_ty); | 11265 | try sema.checkPtrOperand(block, ptr_src, ptr_ty); |
| 11268 | 11266 | ||
| 11269 | // TODO compile error if the result pointer is comptime known and would have an | 11267 | // TODO compile error if the result pointer is comptime known and would have an |
| 11270 | // alignment that disagrees with the Decl's alignment. | 11268 | // alignment that disagrees with the Decl's alignment. |
| ... | @@ -11462,6 +11460,34 @@ fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileEr | ... | @@ -11462,6 +11460,34 @@ fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileEr |
| 11462 | } | 11460 | } |
| 11463 | } | 11461 | } |
| 11464 | 11462 | ||
| 11463 | fn checkPtrOperand( | ||
| 11464 | sema: *Sema, | ||
| 11465 | block: *Block, | ||
| 11466 | ty_src: LazySrcLoc, | ||
| 11467 | ty: Type, | ||
| 11468 | ) CompileError!void { | ||
| 11469 | switch (ty.zigTypeTag()) { | ||
| 11470 | .Pointer => {}, | ||
| 11471 | .Fn => { | ||
| 11472 | const msg = msg: { | ||
| 11473 | const msg = try sema.errMsg( | ||
| 11474 | block, | ||
| 11475 | ty_src, | ||
| 11476 | "expected pointer, found {}", | ||
| 11477 | .{ty}, | ||
| 11478 | ); | ||
| 11479 | errdefer msg.destroy(sema.gpa); | ||
| 11480 | |||
| 11481 | try sema.errNote(block, ty_src, msg, "use '&' to obtain a function pointer", .{}); | ||
| 11482 | |||
| 11483 | break :msg msg; | ||
| 11484 | }; | ||
| 11485 | return sema.failWithOwnedErrorMsg(msg); | ||
| 11486 | }, | ||
| 11487 | else => return sema.fail(block, ty_src, "expected pointer, found '{}'", .{ty}), | ||
| 11488 | } | ||
| 11489 | } | ||
| 11490 | |||
| 11465 | fn checkPtrType( | 11491 | fn checkPtrType( |
| 11466 | sema: *Sema, | 11492 | sema: *Sema, |
| 11467 | block: *Block, | 11493 | block: *Block, |
| ... | @@ -11470,6 +11496,22 @@ fn checkPtrType( | ... | @@ -11470,6 +11496,22 @@ fn checkPtrType( |
| 11470 | ) CompileError!void { | 11496 | ) CompileError!void { |
| 11471 | switch (ty.zigTypeTag()) { | 11497 | switch (ty.zigTypeTag()) { |
| 11472 | .Pointer => {}, | 11498 | .Pointer => {}, |
| 11499 | .Fn => { | ||
| 11500 | const msg = msg: { | ||
| 11501 | const msg = try sema.errMsg( | ||
| 11502 | block, | ||
| 11503 | ty_src, | ||
| 11504 | "expected pointer type, found '{}'", | ||
| 11505 | .{ty}, | ||
| 11506 | ); | ||
| 11507 | errdefer msg.destroy(sema.gpa); | ||
| 11508 | |||
| 11509 | try sema.errNote(block, ty_src, msg, "use '*const ' to make a function pointer type", .{}); | ||
| 11510 | |||
| 11511 | break :msg msg; | ||
| 11512 | }; | ||
| 11513 | return sema.failWithOwnedErrorMsg(msg); | ||
| 11514 | }, | ||
| 11473 | else => return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty}), | 11515 | else => return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty}), |
| 11474 | } | 11516 | } |
| 11475 | } | 11517 | } |
| ... | @@ -12139,20 +12181,14 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void | ... | @@ -12139,20 +12181,14 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 12139 | const dest_ptr = sema.resolveInst(extra.dest); | 12181 | const dest_ptr = sema.resolveInst(extra.dest); |
| 12140 | const dest_ptr_ty = sema.typeOf(dest_ptr); | 12182 | const dest_ptr_ty = sema.typeOf(dest_ptr); |
| 12141 | 12183 | ||
| 12142 | if (dest_ptr_ty.zigTypeTag() != .Pointer) { | 12184 | try sema.checkPtrOperand(block, dest_src, dest_ptr_ty); |
| 12143 | return sema.fail(block, dest_src, "expected pointer, found '{}'", .{dest_ptr_ty}); | ||
| 12144 | } | ||
| 12145 | if (dest_ptr_ty.isConstPtr()) { | 12185 | if (dest_ptr_ty.isConstPtr()) { |
| 12146 | return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty}); | 12186 | return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty}); |
| 12147 | } | 12187 | } |
| 12148 | 12188 | ||
| 12149 | const uncasted_src_ptr = sema.resolveInst(extra.source); | 12189 | const uncasted_src_ptr = sema.resolveInst(extra.source); |
| 12150 | const uncasted_src_ptr_ty = sema.typeOf(uncasted_src_ptr); | 12190 | const uncasted_src_ptr_ty = sema.typeOf(uncasted_src_ptr); |
| 12151 | if (uncasted_src_ptr_ty.zigTypeTag() != .Pointer) { | 12191 | try sema.checkPtrOperand(block, src_src, uncasted_src_ptr_ty); |
| 12152 | return sema.fail(block, src_src, "expected pointer, found '{}'", .{ | ||
| 12153 | uncasted_src_ptr_ty, | ||
| 12154 | }); | ||
| 12155 | } | ||
| 12156 | const src_ptr_info = uncasted_src_ptr_ty.ptrInfo().data; | 12192 | const src_ptr_info = uncasted_src_ptr_ty.ptrInfo().data; |
| 12157 | const wanted_src_ptr_ty = try Type.ptr(sema.arena, .{ | 12193 | const wanted_src_ptr_ty = try Type.ptr(sema.arena, .{ |
| 12158 | .pointee_type = dest_ptr_ty.elemType2(), | 12194 | .pointee_type = dest_ptr_ty.elemType2(), |
| ... | @@ -12203,9 +12239,7 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void | ... | @@ -12203,9 +12239,7 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void |
| 12203 | const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; | 12239 | const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node }; |
| 12204 | const dest_ptr = sema.resolveInst(extra.dest); | 12240 | const dest_ptr = sema.resolveInst(extra.dest); |
| 12205 | const dest_ptr_ty = sema.typeOf(dest_ptr); | 12241 | const dest_ptr_ty = sema.typeOf(dest_ptr); |
| 12206 | if (dest_ptr_ty.zigTypeTag() != .Pointer) { | 12242 | try sema.checkPtrOperand(block, dest_src, dest_ptr_ty); |
| 12207 | return sema.fail(block, dest_src, "expected pointer, found '{}'", .{dest_ptr_ty}); | ||
| 12208 | } | ||
| 12209 | if (dest_ptr_ty.isConstPtr()) { | 12243 | if (dest_ptr_ty.isConstPtr()) { |
| 12210 | return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty}); | 12244 | return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty}); |
| 12211 | } | 12245 | } |
| ... | @@ -12487,7 +12521,7 @@ fn zirPrefetch( | ... | @@ -12487,7 +12521,7 @@ fn zirPrefetch( |
| 12487 | const opts_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; | 12521 | const opts_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node }; |
| 12488 | const options_ty = try sema.getBuiltinType(block, opts_src, "PrefetchOptions"); | 12522 | const options_ty = try sema.getBuiltinType(block, opts_src, "PrefetchOptions"); |
| 12489 | const ptr = sema.resolveInst(extra.lhs); | 12523 | const ptr = sema.resolveInst(extra.lhs); |
| 12490 | try sema.checkPtrType(block, ptr_src, sema.typeOf(ptr)); | 12524 | try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr)); |
| 12491 | const options = try sema.coerce(block, options_ty, sema.resolveInst(extra.rhs), opts_src); | 12525 | const options = try sema.coerce(block, options_ty, sema.resolveInst(extra.rhs), opts_src); |
| 12492 | 12526 | ||
| 12493 | const rw = try sema.fieldVal(block, opts_src, options, "rw", opts_src); | 12527 | const rw = try sema.fieldVal(block, opts_src, options, "rw", opts_src); |
| ... | @@ -12568,12 +12602,15 @@ fn validateVarType( | ... | @@ -12568,12 +12602,15 @@ fn validateVarType( |
| 12568 | .Type, | 12602 | .Type, |
| 12569 | .Undefined, | 12603 | .Undefined, |
| 12570 | .Null, | 12604 | .Null, |
| 12605 | .Fn, | ||
| 12571 | => break, | 12606 | => break, |
| 12572 | 12607 | ||
| 12573 | .Pointer => { | 12608 | .Pointer => { |
| 12574 | const elem_ty = ty.childType(); | 12609 | const elem_ty = ty.childType(); |
| 12575 | if (elem_ty.zigTypeTag() == .Opaque) return; | 12610 | switch (elem_ty.zigTypeTag()) { |
| 12576 | ty = elem_ty; | 12611 | .Opaque, .Fn => return, |
| 12612 | else => ty = elem_ty, | ||
| 12613 | } | ||
| 12577 | }, | 12614 | }, |
| 12578 | .Opaque => if (is_extern) return else break, | 12615 | .Opaque => if (is_extern) return else break, |
| 12579 | 12616 | ||
| ... | @@ -12586,9 +12623,9 @@ fn validateVarType( | ... | @@ -12586,9 +12623,9 @@ fn validateVarType( |
| 12586 | 12623 | ||
| 12587 | .ErrorUnion => ty = ty.errorUnionPayload(), | 12624 | .ErrorUnion => ty = ty.errorUnionPayload(), |
| 12588 | 12625 | ||
| 12589 | .Fn, .Struct, .Union => { | 12626 | .Struct, .Union => { |
| 12590 | const resolved_ty = try sema.resolveTypeFields(block, src, ty); | 12627 | const resolved_ty = try sema.resolveTypeFields(block, src, ty); |
| 12591 | if (resolved_ty.requiresComptime()) { | 12628 | if (try sema.typeRequiresComptime(block, src, resolved_ty)) { |
| 12592 | break; | 12629 | break; |
| 12593 | } else { | 12630 | } else { |
| 12594 | return; | 12631 | return; |
| ... | @@ -12596,7 +12633,99 @@ fn validateVarType( | ... | @@ -12596,7 +12633,99 @@ fn validateVarType( |
| 12596 | }, | 12633 | }, |
| 12597 | } else unreachable; // TODO should not need else unreachable | 12634 | } else unreachable; // TODO should not need else unreachable |
| 12598 | 12635 | ||
| 12599 | return sema.fail(block, src, "variable of type '{}' must be const or comptime", .{var_ty}); | 12636 | const msg = msg: { |
| 12637 | const msg = try sema.errMsg(block, src, "variable of type '{}' must be const or comptime", .{var_ty}); | ||
| 12638 | errdefer msg.destroy(sema.gpa); | ||
| 12639 | |||
| 12640 | try sema.explainWhyTypeIsComptime(block, src, msg, src.toSrcLoc(block.src_decl), var_ty); | ||
| 12641 | |||
| 12642 | break :msg msg; | ||
| 12643 | }; | ||
| 12644 | return sema.failWithOwnedErrorMsg(msg); | ||
| 12645 | } | ||
| 12646 | |||
| 12647 | fn explainWhyTypeIsComptime( | ||
| 12648 | sema: *Sema, | ||
| 12649 | block: *Block, | ||
| 12650 | src: LazySrcLoc, | ||
| 12651 | msg: *Module.ErrorMsg, | ||
| 12652 | src_loc: Module.SrcLoc, | ||
| 12653 | ty: Type, | ||
| 12654 | ) CompileError!void { | ||
| 12655 | const mod = sema.mod; | ||
| 12656 | switch (ty.zigTypeTag()) { | ||
| 12657 | .Bool, | ||
| 12658 | .Int, | ||
| 12659 | .Float, | ||
| 12660 | .ErrorSet, | ||
| 12661 | .Enum, | ||
| 12662 | .Frame, | ||
| 12663 | .AnyFrame, | ||
| 12664 | .Void, | ||
| 12665 | => return, | ||
| 12666 | |||
| 12667 | .Fn => { | ||
| 12668 | try mod.errNoteNonLazy(src_loc, msg, "use '*const {}' for a function pointer type", .{ | ||
| 12669 | ty, | ||
| 12670 | }); | ||
| 12671 | }, | ||
| 12672 | |||
| 12673 | .Type => { | ||
| 12674 | try mod.errNoteNonLazy(src_loc, msg, "types are not available at runtime", .{}); | ||
| 12675 | }, | ||
| 12676 | |||
| 12677 | .BoundFn, | ||
| 12678 | .ComptimeFloat, | ||
| 12679 | .ComptimeInt, | ||
| 12680 | .EnumLiteral, | ||
| 12681 | .NoReturn, | ||
| 12682 | .Undefined, | ||
| 12683 | .Null, | ||
| 12684 | .Opaque, | ||
| 12685 | .Optional, | ||
| 12686 | => return, | ||
| 12687 | |||
| 12688 | .Pointer, .Array, .Vector => { | ||
| 12689 | try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.elemType()); | ||
| 12690 | }, | ||
| 12691 | |||
| 12692 | .ErrorUnion => { | ||
| 12693 | try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.errorUnionPayload()); | ||
| 12694 | }, | ||
| 12695 | |||
| 12696 | .Struct => { | ||
| 12697 | if (ty.castTag(.@"struct")) |payload| { | ||
| 12698 | const struct_obj = payload.data; | ||
| 12699 | for (struct_obj.fields.values()) |field, i| { | ||
| 12700 | const field_src_loc = struct_obj.fieldSrcLoc(sema.gpa, .{ | ||
| 12701 | .index = i, | ||
| 12702 | .range = .type, | ||
| 12703 | }); | ||
| 12704 | if (try sema.typeRequiresComptime(block, src, field.ty)) { | ||
| 12705 | try mod.errNoteNonLazy(field_src_loc, msg, "struct requires comptime because of this field", .{}); | ||
| 12706 | try sema.explainWhyTypeIsComptime(block, src, msg, field_src_loc, field.ty); | ||
| 12707 | } | ||
| 12708 | } | ||
| 12709 | } | ||
| 12710 | // TODO tuples | ||
| 12711 | }, | ||
| 12712 | |||
| 12713 | .Union => { | ||
| 12714 | if (ty.cast(Type.Payload.Union)) |payload| { | ||
| 12715 | const union_obj = payload.data; | ||
| 12716 | for (union_obj.fields.values()) |field, i| { | ||
| 12717 | const field_src_loc = union_obj.fieldSrcLoc(sema.gpa, .{ | ||
| 12718 | .index = i, | ||
| 12719 | .range = .type, | ||
| 12720 | }); | ||
| 12721 | if (try sema.typeRequiresComptime(block, src, field.ty)) { | ||
| 12722 | try mod.errNoteNonLazy(field_src_loc, msg, "union requires comptime because of this field", .{}); | ||
| 12723 | try sema.explainWhyTypeIsComptime(block, src, msg, field_src_loc, field.ty); | ||
| 12724 | } | ||
| 12725 | } | ||
| 12726 | } | ||
| 12727 | }, | ||
| 12728 | } | ||
| 12600 | } | 12729 | } |
| 12601 | 12730 | ||
| 12602 | pub const PanicId = enum { | 12731 | pub const PanicId = enum { |
| ... | @@ -13883,6 +14012,10 @@ fn coerce( | ... | @@ -13883,6 +14012,10 @@ fn coerce( |
| 13883 | { | 14012 | { |
| 13884 | return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); | 14013 | return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src); |
| 13885 | } | 14014 | } |
| 14015 | |||
| 14016 | // This will give an extra hint on top of what the bottom of this func would provide. | ||
| 14017 | try sema.checkPtrOperand(block, dest_ty_src, inst_ty); | ||
| 14018 | unreachable; | ||
| 13886 | }, | 14019 | }, |
| 13887 | .Int, .ComptimeInt => switch (inst_ty.zigTypeTag()) { | 14020 | .Int, .ComptimeInt => switch (inst_ty.zigTypeTag()) { |
| 13888 | .Float, .ComptimeFloat => float: { | 14021 | .Float, .ComptimeFloat => float: { |
| ... | @@ -14683,7 +14816,8 @@ const ComptimePtrLoadKit = struct { | ... | @@ -14683,7 +14816,8 @@ const ComptimePtrLoadKit = struct { |
| 14683 | /// The Type of the parent Value. | 14816 | /// The Type of the parent Value. |
| 14684 | ty: Type, | 14817 | ty: Type, |
| 14685 | /// The starting byte offset of `val` from `root_val`. | 14818 | /// The starting byte offset of `val` from `root_val`. |
| 14686 | byte_offset: usize, | 14819 | /// If the type does not have a well-defined memory layout, this is null. |
| 14820 | byte_offset: ?usize, | ||
| 14687 | /// Whether the `root_val` could be mutated by further | 14821 | /// Whether the `root_val` could be mutated by further |
| 14688 | /// semantic analysis and a copy must be performed. | 14822 | /// semantic analysis and a copy must be performed. |
| 14689 | is_mutable: bool, | 14823 | is_mutable: bool, |
| ... | @@ -14738,12 +14872,24 @@ fn beginComptimePtrLoad( | ... | @@ -14738,12 +14872,24 @@ fn beginComptimePtrLoad( |
| 14738 | }); | 14872 | }); |
| 14739 | } | 14873 | } |
| 14740 | const elem_ty = parent.ty.childType(); | 14874 | const elem_ty = parent.ty.childType(); |
| 14741 | const elem_size = elem_ty.abiSize(target); | 14875 | const byte_offset: ?usize = bo: { |
| 14876 | if (try sema.typeRequiresComptime(block, src, elem_ty)) { | ||
| 14877 | break :bo null; | ||
| 14878 | } else { | ||
| 14879 | if (parent.byte_offset) |off| { | ||
| 14880 | try sema.resolveTypeLayout(block, src, elem_ty); | ||
| 14881 | const elem_size = elem_ty.abiSize(target); | ||
| 14882 | break :bo try sema.usizeCast(block, src, off + elem_size * elem_ptr.index); | ||
| 14883 | } else { | ||
| 14884 | break :bo null; | ||
| 14885 | } | ||
| 14886 | } | ||
| 14887 | }; | ||
| 14742 | return ComptimePtrLoadKit{ | 14888 | return ComptimePtrLoadKit{ |
| 14743 | .root_val = parent.root_val, | 14889 | .root_val = parent.root_val, |
| 14744 | .val = try parent.val.elemValue(sema.arena, elem_ptr.index), | 14890 | .val = try parent.val.elemValue(sema.arena, elem_ptr.index), |
| 14745 | .ty = elem_ty, | 14891 | .ty = elem_ty, |
| 14746 | .byte_offset = try sema.usizeCast(block, src, parent.byte_offset + elem_size * elem_ptr.index), | 14892 | .byte_offset = byte_offset, |
| 14747 | .is_mutable = parent.is_mutable, | 14893 | .is_mutable = parent.is_mutable, |
| 14748 | }; | 14894 | }; |
| 14749 | }, | 14895 | }, |
| ... | @@ -14768,13 +14914,24 @@ fn beginComptimePtrLoad( | ... | @@ -14768,13 +14914,24 @@ fn beginComptimePtrLoad( |
| 14768 | const field_ptr = ptr_val.castTag(.field_ptr).?.data; | 14914 | const field_ptr = ptr_val.castTag(.field_ptr).?.data; |
| 14769 | const parent = try beginComptimePtrLoad(sema, block, src, field_ptr.container_ptr); | 14915 | const parent = try beginComptimePtrLoad(sema, block, src, field_ptr.container_ptr); |
| 14770 | const field_index = @intCast(u32, field_ptr.field_index); | 14916 | const field_index = @intCast(u32, field_ptr.field_index); |
| 14771 | try sema.resolveTypeLayout(block, src, parent.ty); | 14917 | const byte_offset: ?usize = bo: { |
| 14772 | const field_offset = parent.ty.structFieldOffset(field_index, target); | 14918 | if (try sema.typeRequiresComptime(block, src, parent.ty)) { |
| 14919 | break :bo null; | ||
| 14920 | } else { | ||
| 14921 | if (parent.byte_offset) |off| { | ||
| 14922 | try sema.resolveTypeLayout(block, src, parent.ty); | ||
| 14923 | const field_offset = parent.ty.structFieldOffset(field_index, target); | ||
| 14924 | break :bo try sema.usizeCast(block, src, off + field_offset); | ||
| 14925 | } else { | ||
| 14926 | break :bo null; | ||
| 14927 | } | ||
| 14928 | } | ||
| 14929 | }; | ||
| 14773 | return ComptimePtrLoadKit{ | 14930 | return ComptimePtrLoadKit{ |
| 14774 | .root_val = parent.root_val, | 14931 | .root_val = parent.root_val, |
| 14775 | .val = try parent.val.fieldValue(sema.arena, field_index), | 14932 | .val = try parent.val.fieldValue(sema.arena, field_index), |
| 14776 | .ty = parent.ty.structFieldType(field_index), | 14933 | .ty = parent.ty.structFieldType(field_index), |
| 14777 | .byte_offset = try sema.usizeCast(block, src, parent.byte_offset + field_offset), | 14934 | .byte_offset = byte_offset, |
| 14778 | .is_mutable = parent.is_mutable, | 14935 | .is_mutable = parent.is_mutable, |
| 14779 | }; | 14936 | }; |
| 14780 | }, | 14937 | }, |
| ... | @@ -14785,7 +14942,7 @@ fn beginComptimePtrLoad( | ... | @@ -14785,7 +14942,7 @@ fn beginComptimePtrLoad( |
| 14785 | .root_val = parent.root_val, | 14942 | .root_val = parent.root_val, |
| 14786 | .val = parent.val.castTag(.eu_payload).?.data, | 14943 | .val = parent.val.castTag(.eu_payload).?.data, |
| 14787 | .ty = parent.ty.errorUnionPayload(), | 14944 | .ty = parent.ty.errorUnionPayload(), |
| 14788 | .byte_offset = undefined, | 14945 | .byte_offset = null, |
| 14789 | .is_mutable = parent.is_mutable, | 14946 | .is_mutable = parent.is_mutable, |
| 14790 | }; | 14947 | }; |
| 14791 | }, | 14948 | }, |
| ... | @@ -14796,7 +14953,7 @@ fn beginComptimePtrLoad( | ... | @@ -14796,7 +14953,7 @@ fn beginComptimePtrLoad( |
| 14796 | .root_val = parent.root_val, | 14953 | .root_val = parent.root_val, |
| 14797 | .val = parent.val.castTag(.opt_payload).?.data, | 14954 | .val = parent.val.castTag(.opt_payload).?.data, |
| 14798 | .ty = try parent.ty.optionalChildAlloc(sema.arena), | 14955 | .ty = try parent.ty.optionalChildAlloc(sema.arena), |
| 14799 | .byte_offset = undefined, | 14956 | .byte_offset = null, |
| 14800 | .is_mutable = parent.is_mutable, | 14957 | .is_mutable = parent.is_mutable, |
| 14801 | }; | 14958 | }; |
| 14802 | }, | 14959 | }, |
| ... | @@ -16090,28 +16247,12 @@ fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) Comp | ... | @@ -16090,28 +16247,12 @@ fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) Comp |
| 16090 | switch (ty.tag()) { | 16247 | switch (ty.tag()) { |
| 16091 | .@"struct" => { | 16248 | .@"struct" => { |
| 16092 | const struct_obj = ty.castTag(.@"struct").?.data; | 16249 | const struct_obj = ty.castTag(.@"struct").?.data; |
| 16093 | switch (struct_obj.status) { | 16250 | try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj); |
| 16094 | .none => {}, | 16251 | return ty; |
| 16095 | .field_types_wip => { | 16252 | }, |
| 16096 | return sema.fail(block, src, "struct {} depends on itself", .{ty}); | 16253 | .@"union", .union_tagged => { |
| 16097 | }, | 16254 | const union_obj = ty.cast(Type.Payload.Union).?.data; |
| 16098 | .have_field_types, | 16255 | try sema.resolveTypeFieldsUnion(block, src, ty, union_obj); |
| 16099 | .have_layout, | ||
| 16100 | .layout_wip, | ||
| 16101 | .fully_resolved_wip, | ||
| 16102 | .fully_resolved, | ||
| 16103 | => return ty, | ||
| 16104 | } | ||
| 16105 | |||
| 16106 | struct_obj.status = .field_types_wip; | ||
| 16107 | try semaStructFields(sema.mod, struct_obj); | ||
| 16108 | |||
| 16109 | if (struct_obj.fields.count() == 0) { | ||
| 16110 | struct_obj.status = .have_layout; | ||
| 16111 | } else { | ||
| 16112 | struct_obj.status = .have_field_types; | ||
| 16113 | } | ||
| 16114 | |||
| 16115 | return ty; | 16256 | return ty; |
| 16116 | }, | 16257 | }, |
| 16117 | .type_info => return sema.resolveBuiltinTypeFields(block, src, "TypeInfo"), | 16258 | .type_info => return sema.resolveBuiltinTypeFields(block, src, "TypeInfo"), |
| ... | @@ -16126,29 +16267,63 @@ fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) Comp | ... | @@ -16126,29 +16267,63 @@ fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) Comp |
| 16126 | .call_options => return sema.resolveBuiltinTypeFields(block, src, "CallOptions"), | 16267 | .call_options => return sema.resolveBuiltinTypeFields(block, src, "CallOptions"), |
| 16127 | .prefetch_options => return sema.resolveBuiltinTypeFields(block, src, "PrefetchOptions"), | 16268 | .prefetch_options => return sema.resolveBuiltinTypeFields(block, src, "PrefetchOptions"), |
| 16128 | 16269 | ||
| 16129 | .@"union", .union_tagged => { | 16270 | else => return ty, |
| 16130 | const union_obj = ty.cast(Type.Payload.Union).?.data; | 16271 | } |
| 16131 | switch (union_obj.status) { | 16272 | } |
| 16132 | .none => {}, | ||
| 16133 | .field_types_wip => { | ||
| 16134 | return sema.fail(block, src, "union {} depends on itself", .{ty}); | ||
| 16135 | }, | ||
| 16136 | .have_field_types, | ||
| 16137 | .have_layout, | ||
| 16138 | .layout_wip, | ||
| 16139 | .fully_resolved_wip, | ||
| 16140 | .fully_resolved, | ||
| 16141 | => return ty, | ||
| 16142 | } | ||
| 16143 | 16273 | ||
| 16144 | union_obj.status = .field_types_wip; | 16274 | fn resolveTypeFieldsStruct( |
| 16145 | try semaUnionFields(sema.mod, union_obj); | 16275 | sema: *Sema, |
| 16146 | union_obj.status = .have_field_types; | 16276 | block: *Block, |
| 16277 | src: LazySrcLoc, | ||
| 16278 | ty: Type, | ||
| 16279 | struct_obj: *Module.Struct, | ||
| 16280 | ) CompileError!void { | ||
| 16281 | switch (struct_obj.status) { | ||
| 16282 | .none => {}, | ||
| 16283 | .field_types_wip => { | ||
| 16284 | return sema.fail(block, src, "struct {} depends on itself", .{ty}); | ||
| 16285 | }, | ||
| 16286 | .have_field_types, | ||
| 16287 | .have_layout, | ||
| 16288 | .layout_wip, | ||
| 16289 | .fully_resolved_wip, | ||
| 16290 | .fully_resolved, | ||
| 16291 | => return, | ||
| 16292 | } | ||
| 16147 | 16293 | ||
| 16148 | return ty; | 16294 | struct_obj.status = .field_types_wip; |
| 16295 | try semaStructFields(sema.mod, struct_obj); | ||
| 16296 | |||
| 16297 | if (struct_obj.fields.count() == 0) { | ||
| 16298 | struct_obj.status = .have_layout; | ||
| 16299 | } else { | ||
| 16300 | struct_obj.status = .have_field_types; | ||
| 16301 | } | ||
| 16302 | } | ||
| 16303 | |||
| 16304 | fn resolveTypeFieldsUnion( | ||
| 16305 | sema: *Sema, | ||
| 16306 | block: *Block, | ||
| 16307 | src: LazySrcLoc, | ||
| 16308 | ty: Type, | ||
| 16309 | union_obj: *Module.Union, | ||
| 16310 | ) CompileError!void { | ||
| 16311 | switch (union_obj.status) { | ||
| 16312 | .none => {}, | ||
| 16313 | .field_types_wip => { | ||
| 16314 | return sema.fail(block, src, "union {} depends on itself", .{ty}); | ||
| 16149 | }, | 16315 | }, |
| 16150 | else => return ty, | 16316 | .have_field_types, |
| 16317 | .have_layout, | ||
| 16318 | .layout_wip, | ||
| 16319 | .fully_resolved_wip, | ||
| 16320 | .fully_resolved, | ||
| 16321 | => return, | ||
| 16151 | } | 16322 | } |
| 16323 | |||
| 16324 | union_obj.status = .field_types_wip; | ||
| 16325 | try semaUnionFields(sema.mod, union_obj); | ||
| 16326 | union_obj.status = .have_field_types; | ||
| 16152 | } | 16327 | } |
| 16153 | 16328 | ||
| 16154 | fn resolveBuiltinTypeFields( | 16329 | fn resolveBuiltinTypeFields( |
| ... | @@ -17295,3 +17470,198 @@ fn typePtrOrOptionalPtrTy( | ... | @@ -17295,3 +17470,198 @@ fn typePtrOrOptionalPtrTy( |
| 17295 | else => return null, | 17470 | else => return null, |
| 17296 | } | 17471 | } |
| 17297 | } | 17472 | } |
| 17473 | |||
| 17474 | /// Anything that reports hasCodeGenBits() false returns false here as well. | ||
| 17475 | /// `generic_poison` will return false. | ||
| 17476 | /// This function returns false negatives when structs and unions are having their | ||
| 17477 | /// field types resolved. | ||
| 17478 | fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool { | ||
| 17479 | return switch (ty.tag()) { | ||
| 17480 | .u1, | ||
| 17481 | .u8, | ||
| 17482 | .i8, | ||
| 17483 | .u16, | ||
| 17484 | .i16, | ||
| 17485 | .u32, | ||
| 17486 | .i32, | ||
| 17487 | .u64, | ||
| 17488 | .i64, | ||
| 17489 | .u128, | ||
| 17490 | .i128, | ||
| 17491 | .usize, | ||
| 17492 | .isize, | ||
| 17493 | .c_short, | ||
| 17494 | .c_ushort, | ||
| 17495 | .c_int, | ||
| 17496 | .c_uint, | ||
| 17497 | .c_long, | ||
| 17498 | .c_ulong, | ||
| 17499 | .c_longlong, | ||
| 17500 | .c_ulonglong, | ||
| 17501 | .c_longdouble, | ||
| 17502 | .f16, | ||
| 17503 | .f32, | ||
| 17504 | .f64, | ||
| 17505 | .f128, | ||
| 17506 | .anyopaque, | ||
| 17507 | .bool, | ||
| 17508 | .void, | ||
| 17509 | .anyerror, | ||
| 17510 | .noreturn, | ||
| 17511 | .@"anyframe", | ||
| 17512 | .@"null", | ||
| 17513 | .@"undefined", | ||
| 17514 | .atomic_order, | ||
| 17515 | .atomic_rmw_op, | ||
| 17516 | .calling_convention, | ||
| 17517 | .address_space, | ||
| 17518 | .float_mode, | ||
| 17519 | .reduce_op, | ||
| 17520 | .call_options, | ||
| 17521 | .prefetch_options, | ||
| 17522 | .export_options, | ||
| 17523 | .extern_options, | ||
| 17524 | .manyptr_u8, | ||
| 17525 | .manyptr_const_u8, | ||
| 17526 | .manyptr_const_u8_sentinel_0, | ||
| 17527 | .const_slice_u8, | ||
| 17528 | .const_slice_u8_sentinel_0, | ||
| 17529 | .anyerror_void_error_union, | ||
| 17530 | .empty_struct_literal, | ||
| 17531 | .empty_struct, | ||
| 17532 | .error_set, | ||
| 17533 | .error_set_single, | ||
| 17534 | .error_set_inferred, | ||
| 17535 | .error_set_merged, | ||
| 17536 | .@"opaque", | ||
| 17537 | .generic_poison, | ||
| 17538 | .array_u8, | ||
| 17539 | .array_u8_sentinel_0, | ||
| 17540 | .int_signed, | ||
| 17541 | .int_unsigned, | ||
| 17542 | .enum_simple, | ||
| 17543 | => false, | ||
| 17544 | |||
| 17545 | .single_const_pointer_to_comptime_int, | ||
| 17546 | .type, | ||
| 17547 | .comptime_int, | ||
| 17548 | .comptime_float, | ||
| 17549 | .enum_literal, | ||
| 17550 | .type_info, | ||
| 17551 | // These are function bodies, not function pointers. | ||
| 17552 | .fn_noreturn_no_args, | ||
| 17553 | .fn_void_no_args, | ||
| 17554 | .fn_naked_noreturn_no_args, | ||
| 17555 | .fn_ccc_void_no_args, | ||
| 17556 | .function, | ||
| 17557 | => true, | ||
| 17558 | |||
| 17559 | .var_args_param => unreachable, | ||
| 17560 | .inferred_alloc_mut => unreachable, | ||
| 17561 | .inferred_alloc_const => unreachable, | ||
| 17562 | .bound_fn => unreachable, | ||
| 17563 | |||
| 17564 | .array, | ||
| 17565 | .array_sentinel, | ||
| 17566 | .vector, | ||
| 17567 | => return sema.typeRequiresComptime(block, src, ty.childType()), | ||
| 17568 | |||
| 17569 | .pointer, | ||
| 17570 | .single_const_pointer, | ||
| 17571 | .single_mut_pointer, | ||
| 17572 | .many_const_pointer, | ||
| 17573 | .many_mut_pointer, | ||
| 17574 | .c_const_pointer, | ||
| 17575 | .c_mut_pointer, | ||
| 17576 | .const_slice, | ||
| 17577 | .mut_slice, | ||
| 17578 | => { | ||
| 17579 | const child_ty = ty.childType(); | ||
| 17580 | if (child_ty.zigTypeTag() == .Fn) { | ||
| 17581 | return false; | ||
| 17582 | } else { | ||
| 17583 | return sema.typeRequiresComptime(block, src, child_ty); | ||
| 17584 | } | ||
| 17585 | }, | ||
| 17586 | |||
| 17587 | .optional, | ||
| 17588 | .optional_single_mut_pointer, | ||
| 17589 | .optional_single_const_pointer, | ||
| 17590 | => { | ||
| 17591 | var buf: Type.Payload.ElemType = undefined; | ||
| 17592 | return sema.typeRequiresComptime(block, src, ty.optionalChild(&buf)); | ||
| 17593 | }, | ||
| 17594 | |||
| 17595 | .tuple => { | ||
| 17596 | const tuple = ty.castTag(.tuple).?.data; | ||
| 17597 | for (tuple.types) |field_ty| { | ||
| 17598 | if (try sema.typeRequiresComptime(block, src, field_ty)) { | ||
| 17599 | return true; | ||
| 17600 | } | ||
| 17601 | } | ||
| 17602 | return false; | ||
| 17603 | }, | ||
| 17604 | |||
| 17605 | .@"struct" => { | ||
| 17606 | const struct_obj = ty.castTag(.@"struct").?.data; | ||
| 17607 | switch (struct_obj.requires_comptime) { | ||
| 17608 | .no, .wip => return false, | ||
| 17609 | .yes => return true, | ||
| 17610 | .unknown => { | ||
| 17611 | if (struct_obj.status == .field_types_wip) | ||
| 17612 | return false; | ||
| 17613 | |||
| 17614 | try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj); | ||
| 17615 | |||
| 17616 | struct_obj.requires_comptime = .wip; | ||
| 17617 | for (struct_obj.fields.values()) |field| { | ||
| 17618 | if (try sema.typeRequiresComptime(block, src, field.ty)) { | ||
| 17619 | struct_obj.requires_comptime = .yes; | ||
| 17620 | return true; | ||
| 17621 | } | ||
| 17622 | } | ||
| 17623 | struct_obj.requires_comptime = .no; | ||
| 17624 | return false; | ||
| 17625 | }, | ||
| 17626 | } | ||
| 17627 | }, | ||
| 17628 | |||
| 17629 | .@"union", .union_tagged => { | ||
| 17630 | const union_obj = ty.cast(Type.Payload.Union).?.data; | ||
| 17631 | switch (union_obj.requires_comptime) { | ||
| 17632 | .no, .wip => return false, | ||
| 17633 | .yes => return true, | ||
| 17634 | .unknown => { | ||
| 17635 | if (union_obj.status == .field_types_wip) | ||
| 17636 | return false; | ||
| 17637 | |||
| 17638 | try sema.resolveTypeFieldsUnion(block, src, ty, union_obj); | ||
| 17639 | |||
| 17640 | union_obj.requires_comptime = .wip; | ||
| 17641 | for (union_obj.fields.values()) |field| { | ||
| 17642 | if (try sema.typeRequiresComptime(block, src, field.ty)) { | ||
| 17643 | union_obj.requires_comptime = .yes; | ||
| 17644 | return true; | ||
| 17645 | } | ||
| 17646 | } | ||
| 17647 | union_obj.requires_comptime = .no; | ||
| 17648 | return false; | ||
| 17649 | }, | ||
| 17650 | } | ||
| 17651 | }, | ||
| 17652 | |||
| 17653 | .error_union => return sema.typeRequiresComptime(block, src, ty.errorUnionPayload()), | ||
| 17654 | .anyframe_T => { | ||
| 17655 | const child_ty = ty.castTag(.anyframe_T).?.data; | ||
| 17656 | return sema.typeRequiresComptime(block, src, child_ty); | ||
| 17657 | }, | ||
| 17658 | .enum_numbered => { | ||
| 17659 | const tag_ty = ty.castTag(.enum_numbered).?.data.tag_ty; | ||
| 17660 | return sema.typeRequiresComptime(block, src, tag_ty); | ||
| 17661 | }, | ||
| 17662 | .enum_full, .enum_nonexhaustive => { | ||
| 17663 | const tag_ty = ty.cast(Type.Payload.EnumFull).?.data.tag_ty; | ||
| 17664 | return sema.typeRequiresComptime(block, src, tag_ty); | ||
| 17665 | }, | ||
| 17666 | }; | ||
| 17667 | } |
src/codegen/llvm.zig+4| ... | @@ -725,6 +725,10 @@ pub const DeclGen = struct { | ... | @@ -725,6 +725,10 @@ pub const DeclGen = struct { |
| 725 | llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target)); | 725 | llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target)); |
| 726 | } | 726 | } |
| 727 | 727 | ||
| 728 | if (fn_info.alignment != 0) { | ||
| 729 | llvm_fn.setAlignment(fn_info.alignment); | ||
| 730 | } | ||
| 731 | |||
| 728 | // Function attributes that are independent of analysis results of the function body. | 732 | // Function attributes that are independent of analysis results of the function body. |
| 729 | dg.addCommonFnAttributes(llvm_fn); | 733 | dg.addCommonFnAttributes(llvm_fn); |
| 730 | 734 |
src/target.zig+9| ... | @@ -637,3 +637,12 @@ pub fn llvmMachineAbi(target: std.Target) ?[:0]const u8 { | ... | @@ -637,3 +637,12 @@ pub fn llvmMachineAbi(target: std.Target) ?[:0]const u8 { |
| 637 | else => return null, | 637 | else => return null, |
| 638 | } | 638 | } |
| 639 | } | 639 | } |
| 640 | |||
| 641 | pub fn defaultFunctionAlignment(target: std.Target) u32 { | ||
| 642 | return switch (target.cpu.arch) { | ||
| 643 | .arm, .armeb => 4, | ||
| 644 | .aarch64, .aarch64_32, .aarch64_be => 4, | ||
| 645 | .riscv64 => 2, | ||
| 646 | else => 1, | ||
| 647 | }; | ||
| 648 | } |
src/type.zig+48-181| ... | @@ -5,6 +5,7 @@ const Allocator = std.mem.Allocator; | ... | @@ -5,6 +5,7 @@ const Allocator = std.mem.Allocator; |
| 5 | const Target = std.Target; | 5 | const Target = std.Target; |
| 6 | const Module = @import("Module.zig"); | 6 | const Module = @import("Module.zig"); |
| 7 | const log = std.log.scoped(.Type); | 7 | const log = std.log.scoped(.Type); |
| 8 | const target_util = @import("target.zig"); | ||
| 8 | 9 | ||
| 9 | const file_struct = @This(); | 10 | const file_struct = @This(); |
| 10 | 11 | ||
| ... | @@ -577,21 +578,36 @@ pub const Type = extern union { | ... | @@ -577,21 +578,36 @@ pub const Type = extern union { |
| 577 | } | 578 | } |
| 578 | }, | 579 | }, |
| 579 | .Fn => { | 580 | .Fn => { |
| 580 | if (!a.fnReturnType().eql(b.fnReturnType())) | 581 | const a_info = a.fnInfo(); |
| 582 | const b_info = b.fnInfo(); | ||
| 583 | |||
| 584 | if (!eql(a_info.return_type, b_info.return_type)) | ||
| 581 | return false; | 585 | return false; |
| 582 | if (a.fnCallingConvention() != b.fnCallingConvention()) | 586 | |
| 587 | if (a_info.cc != b_info.cc) | ||
| 583 | return false; | 588 | return false; |
| 584 | const a_param_len = a.fnParamLen(); | 589 | |
| 585 | const b_param_len = b.fnParamLen(); | 590 | if (a_info.param_types.len != b_info.param_types.len) |
| 586 | if (a_param_len != b_param_len) | ||
| 587 | return false; | 591 | return false; |
| 588 | var i: usize = 0; | 592 | |
| 589 | while (i < a_param_len) : (i += 1) { | 593 | for (a_info.param_types) |a_param_ty, i| { |
| 590 | if (!a.fnParamType(i).eql(b.fnParamType(i))) | 594 | const b_param_ty = b_info.param_types[i]; |
| 595 | if (!eql(a_param_ty, b_param_ty)) | ||
| 596 | return false; | ||
| 597 | |||
| 598 | if (a_info.comptime_params[i] != b_info.comptime_params[i]) | ||
| 591 | return false; | 599 | return false; |
| 592 | } | 600 | } |
| 593 | if (a.fnIsVarArgs() != b.fnIsVarArgs()) | 601 | |
| 602 | if (a_info.alignment != b_info.alignment) | ||
| 603 | return false; | ||
| 604 | |||
| 605 | if (a_info.is_var_args != b_info.is_var_args) | ||
| 594 | return false; | 606 | return false; |
| 607 | |||
| 608 | if (a_info.is_generic != b_info.is_generic) | ||
| 609 | return false; | ||
| 610 | |||
| 595 | return true; | 611 | return true; |
| 596 | }, | 612 | }, |
| 597 | .Optional => { | 613 | .Optional => { |
| ... | @@ -686,6 +702,7 @@ pub const Type = extern union { | ... | @@ -686,6 +702,7 @@ pub const Type = extern union { |
| 686 | return false; | 702 | return false; |
| 687 | }, | 703 | }, |
| 688 | .Float => return a.tag() == b.tag(), | 704 | .Float => return a.tag() == b.tag(), |
| 705 | |||
| 689 | .BoundFn, | 706 | .BoundFn, |
| 690 | .Frame, | 707 | .Frame, |
| 691 | => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }), | 708 | => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }), |
| ... | @@ -937,6 +954,7 @@ pub const Type = extern union { | ... | @@ -937,6 +954,7 @@ pub const Type = extern union { |
| 937 | .return_type = try payload.return_type.copy(allocator), | 954 | .return_type = try payload.return_type.copy(allocator), |
| 938 | .param_types = param_types, | 955 | .param_types = param_types, |
| 939 | .cc = payload.cc, | 956 | .cc = payload.cc, |
| 957 | .alignment = payload.alignment, | ||
| 940 | .is_var_args = payload.is_var_args, | 958 | .is_var_args = payload.is_var_args, |
| 941 | .is_generic = payload.is_generic, | 959 | .is_generic = payload.is_generic, |
| 942 | .comptime_params = comptime_params.ptr, | 960 | .comptime_params = comptime_params.ptr, |
| ... | @@ -1114,9 +1132,15 @@ pub const Type = extern union { | ... | @@ -1114,9 +1132,15 @@ pub const Type = extern union { |
| 1114 | } | 1132 | } |
| 1115 | try writer.writeAll("..."); | 1133 | try writer.writeAll("..."); |
| 1116 | } | 1134 | } |
| 1117 | try writer.writeAll(") callconv(."); | ||
| 1118 | try writer.writeAll(@tagName(payload.cc)); | ||
| 1119 | try writer.writeAll(") "); | 1135 | try writer.writeAll(") "); |
| 1136 | if (payload.cc != .Unspecified) { | ||
| 1137 | try writer.writeAll("callconv(."); | ||
| 1138 | try writer.writeAll(@tagName(payload.cc)); | ||
| 1139 | try writer.writeAll(") "); | ||
| 1140 | } | ||
| 1141 | if (payload.alignment != 0) { | ||
| 1142 | try writer.print("align({d}) ", .{payload.alignment}); | ||
| 1143 | } | ||
| 1120 | ty = payload.return_type; | 1144 | ty = payload.return_type; |
| 1121 | continue; | 1145 | continue; |
| 1122 | }, | 1146 | }, |
| ... | @@ -1423,170 +1447,6 @@ pub const Type = extern union { | ... | @@ -1423,170 +1447,6 @@ pub const Type = extern union { |
| 1423 | } | 1447 | } |
| 1424 | } | 1448 | } |
| 1425 | 1449 | ||
| 1426 | /// Anything that reports hasCodeGenBits() false returns false here as well. | ||
| 1427 | /// `generic_poison` will return false. | ||
| 1428 | pub fn requiresComptime(ty: Type) bool { | ||
| 1429 | return switch (ty.tag()) { | ||
| 1430 | .u1, | ||
| 1431 | .u8, | ||
| 1432 | .i8, | ||
| 1433 | .u16, | ||
| 1434 | .i16, | ||
| 1435 | .u32, | ||
| 1436 | .i32, | ||
| 1437 | .u64, | ||
| 1438 | .i64, | ||
| 1439 | .u128, | ||
| 1440 | .i128, | ||
| 1441 | .usize, | ||
| 1442 | .isize, | ||
| 1443 | .c_short, | ||
| 1444 | .c_ushort, | ||
| 1445 | .c_int, | ||
| 1446 | .c_uint, | ||
| 1447 | .c_long, | ||
| 1448 | .c_ulong, | ||
| 1449 | .c_longlong, | ||
| 1450 | .c_ulonglong, | ||
| 1451 | .c_longdouble, | ||
| 1452 | .f16, | ||
| 1453 | .f32, | ||
| 1454 | .f64, | ||
| 1455 | .f128, | ||
| 1456 | .anyopaque, | ||
| 1457 | .bool, | ||
| 1458 | .void, | ||
| 1459 | .anyerror, | ||
| 1460 | .noreturn, | ||
| 1461 | .@"anyframe", | ||
| 1462 | .@"null", | ||
| 1463 | .@"undefined", | ||
| 1464 | .atomic_order, | ||
| 1465 | .atomic_rmw_op, | ||
| 1466 | .calling_convention, | ||
| 1467 | .address_space, | ||
| 1468 | .float_mode, | ||
| 1469 | .reduce_op, | ||
| 1470 | .call_options, | ||
| 1471 | .prefetch_options, | ||
| 1472 | .export_options, | ||
| 1473 | .extern_options, | ||
| 1474 | .manyptr_u8, | ||
| 1475 | .manyptr_const_u8, | ||
| 1476 | .manyptr_const_u8_sentinel_0, | ||
| 1477 | .fn_noreturn_no_args, | ||
| 1478 | .fn_void_no_args, | ||
| 1479 | .fn_naked_noreturn_no_args, | ||
| 1480 | .fn_ccc_void_no_args, | ||
| 1481 | .const_slice_u8, | ||
| 1482 | .const_slice_u8_sentinel_0, | ||
| 1483 | .anyerror_void_error_union, | ||
| 1484 | .empty_struct_literal, | ||
| 1485 | .function, | ||
| 1486 | .empty_struct, | ||
| 1487 | .error_set, | ||
| 1488 | .error_set_single, | ||
| 1489 | .error_set_inferred, | ||
| 1490 | .error_set_merged, | ||
| 1491 | .@"opaque", | ||
| 1492 | .generic_poison, | ||
| 1493 | .array_u8, | ||
| 1494 | .array_u8_sentinel_0, | ||
| 1495 | .int_signed, | ||
| 1496 | .int_unsigned, | ||
| 1497 | .enum_simple, | ||
| 1498 | => false, | ||
| 1499 | |||
| 1500 | .single_const_pointer_to_comptime_int, | ||
| 1501 | .type, | ||
| 1502 | .comptime_int, | ||
| 1503 | .comptime_float, | ||
| 1504 | .enum_literal, | ||
| 1505 | .type_info, | ||
| 1506 | => true, | ||
| 1507 | |||
| 1508 | .var_args_param => unreachable, | ||
| 1509 | .inferred_alloc_mut => unreachable, | ||
| 1510 | .inferred_alloc_const => unreachable, | ||
| 1511 | .bound_fn => unreachable, | ||
| 1512 | |||
| 1513 | .array, | ||
| 1514 | .array_sentinel, | ||
| 1515 | .vector, | ||
| 1516 | .pointer, | ||
| 1517 | .single_const_pointer, | ||
| 1518 | .single_mut_pointer, | ||
| 1519 | .many_const_pointer, | ||
| 1520 | .many_mut_pointer, | ||
| 1521 | .c_const_pointer, | ||
| 1522 | .c_mut_pointer, | ||
| 1523 | .const_slice, | ||
| 1524 | .mut_slice, | ||
| 1525 | => return requiresComptime(childType(ty)), | ||
| 1526 | |||
| 1527 | .optional, | ||
| 1528 | .optional_single_mut_pointer, | ||
| 1529 | .optional_single_const_pointer, | ||
| 1530 | => { | ||
| 1531 | var buf: Payload.ElemType = undefined; | ||
| 1532 | return requiresComptime(optionalChild(ty, &buf)); | ||
| 1533 | }, | ||
| 1534 | |||
| 1535 | .tuple => { | ||
| 1536 | const tuple = ty.castTag(.tuple).?.data; | ||
| 1537 | for (tuple.types) |field_ty| { | ||
| 1538 | if (requiresComptime(field_ty)) { | ||
| 1539 | return true; | ||
| 1540 | } | ||
| 1541 | } | ||
| 1542 | return false; | ||
| 1543 | }, | ||
| 1544 | |||
| 1545 | .@"struct" => { | ||
| 1546 | const struct_obj = ty.castTag(.@"struct").?.data; | ||
| 1547 | switch (struct_obj.requires_comptime) { | ||
| 1548 | .no, .wip => return false, | ||
| 1549 | .yes => return true, | ||
| 1550 | .unknown => { | ||
| 1551 | struct_obj.requires_comptime = .wip; | ||
| 1552 | for (struct_obj.fields.values()) |field| { | ||
| 1553 | if (requiresComptime(field.ty)) { | ||
| 1554 | struct_obj.requires_comptime = .yes; | ||
| 1555 | return true; | ||
| 1556 | } | ||
| 1557 | } | ||
| 1558 | struct_obj.requires_comptime = .no; | ||
| 1559 | return false; | ||
| 1560 | }, | ||
| 1561 | } | ||
| 1562 | }, | ||
| 1563 | |||
| 1564 | .@"union", .union_tagged => { | ||
| 1565 | const union_obj = ty.cast(Payload.Union).?.data; | ||
| 1566 | switch (union_obj.requires_comptime) { | ||
| 1567 | .no, .wip => return false, | ||
| 1568 | .yes => return true, | ||
| 1569 | .unknown => { | ||
| 1570 | union_obj.requires_comptime = .wip; | ||
| 1571 | for (union_obj.fields.values()) |field| { | ||
| 1572 | if (requiresComptime(field.ty)) { | ||
| 1573 | union_obj.requires_comptime = .yes; | ||
| 1574 | return true; | ||
| 1575 | } | ||
| 1576 | } | ||
| 1577 | union_obj.requires_comptime = .no; | ||
| 1578 | return false; | ||
| 1579 | }, | ||
| 1580 | } | ||
| 1581 | }, | ||
| 1582 | |||
| 1583 | .error_union => return requiresComptime(errorUnionPayload(ty)), | ||
| 1584 | .anyframe_T => return ty.castTag(.anyframe_T).?.data.requiresComptime(), | ||
| 1585 | .enum_numbered => return ty.castTag(.enum_numbered).?.data.tag_ty.requiresComptime(), | ||
| 1586 | .enum_full, .enum_nonexhaustive => return ty.cast(Payload.EnumFull).?.data.tag_ty.requiresComptime(), | ||
| 1587 | }; | ||
| 1588 | } | ||
| 1589 | |||
| 1590 | pub fn toValue(self: Type, allocator: Allocator) Allocator.Error!Value { | 1450 | pub fn toValue(self: Type, allocator: Allocator) Allocator.Error!Value { |
| 1591 | switch (self.tag()) { | 1451 | switch (self.tag()) { |
| 1592 | .u1 => return Value.initTag(.u1_type), | 1452 | .u1 => return Value.initTag(.u1_type), |
| ... | @@ -1918,12 +1778,13 @@ pub const Type = extern union { | ... | @@ -1918,12 +1778,13 @@ pub const Type = extern union { |
| 1918 | .fn_void_no_args, // represents machine code; not a pointer | 1778 | .fn_void_no_args, // represents machine code; not a pointer |
| 1919 | .fn_naked_noreturn_no_args, // represents machine code; not a pointer | 1779 | .fn_naked_noreturn_no_args, // represents machine code; not a pointer |
| 1920 | .fn_ccc_void_no_args, // represents machine code; not a pointer | 1780 | .fn_ccc_void_no_args, // represents machine code; not a pointer |
| 1921 | .function, // represents machine code; not a pointer | 1781 | => return target_util.defaultFunctionAlignment(target), |
| 1922 | => return switch (target.cpu.arch) { | 1782 | |
| 1923 | .arm, .armeb => 4, | 1783 | // represents machine code; not a pointer |
| 1924 | .aarch64, .aarch64_32, .aarch64_be => 4, | 1784 | .function => { |
| 1925 | .riscv64 => 2, | 1785 | const alignment = self.castTag(.function).?.data.alignment; |
| 1926 | else => 1, | 1786 | if (alignment != 0) return alignment; |
| 1787 | return target_util.defaultFunctionAlignment(target); | ||
| 1927 | }, | 1788 | }, |
| 1928 | 1789 | ||
| 1929 | .i16, .u16 => return 2, | 1790 | .i16, .u16 => return 2, |
| ... | @@ -3424,6 +3285,7 @@ pub const Type = extern union { | ... | @@ -3424,6 +3285,7 @@ pub const Type = extern union { |
| 3424 | .comptime_params = undefined, | 3285 | .comptime_params = undefined, |
| 3425 | .return_type = initTag(.noreturn), | 3286 | .return_type = initTag(.noreturn), |
| 3426 | .cc = .Unspecified, | 3287 | .cc = .Unspecified, |
| 3288 | .alignment = 0, | ||
| 3427 | .is_var_args = false, | 3289 | .is_var_args = false, |
| 3428 | .is_generic = false, | 3290 | .is_generic = false, |
| 3429 | }, | 3291 | }, |
| ... | @@ -3432,6 +3294,7 @@ pub const Type = extern union { | ... | @@ -3432,6 +3294,7 @@ pub const Type = extern union { |
| 3432 | .comptime_params = undefined, | 3294 | .comptime_params = undefined, |
| 3433 | .return_type = initTag(.void), | 3295 | .return_type = initTag(.void), |
| 3434 | .cc = .Unspecified, | 3296 | .cc = .Unspecified, |
| 3297 | .alignment = 0, | ||
| 3435 | .is_var_args = false, | 3298 | .is_var_args = false, |
| 3436 | .is_generic = false, | 3299 | .is_generic = false, |
| 3437 | }, | 3300 | }, |
| ... | @@ -3440,6 +3303,7 @@ pub const Type = extern union { | ... | @@ -3440,6 +3303,7 @@ pub const Type = extern union { |
| 3440 | .comptime_params = undefined, | 3303 | .comptime_params = undefined, |
| 3441 | .return_type = initTag(.noreturn), | 3304 | .return_type = initTag(.noreturn), |
| 3442 | .cc = .Naked, | 3305 | .cc = .Naked, |
| 3306 | .alignment = 0, | ||
| 3443 | .is_var_args = false, | 3307 | .is_var_args = false, |
| 3444 | .is_generic = false, | 3308 | .is_generic = false, |
| 3445 | }, | 3309 | }, |
| ... | @@ -3448,6 +3312,7 @@ pub const Type = extern union { | ... | @@ -3448,6 +3312,7 @@ pub const Type = extern union { |
| 3448 | .comptime_params = undefined, | 3312 | .comptime_params = undefined, |
| 3449 | .return_type = initTag(.void), | 3313 | .return_type = initTag(.void), |
| 3450 | .cc = .C, | 3314 | .cc = .C, |
| 3315 | .alignment = 0, | ||
| 3451 | .is_var_args = false, | 3316 | .is_var_args = false, |
| 3452 | .is_generic = false, | 3317 | .is_generic = false, |
| 3453 | }, | 3318 | }, |
| ... | @@ -4572,6 +4437,8 @@ pub const Type = extern union { | ... | @@ -4572,6 +4437,8 @@ pub const Type = extern union { |
| 4572 | param_types: []Type, | 4437 | param_types: []Type, |
| 4573 | comptime_params: [*]bool, | 4438 | comptime_params: [*]bool, |
| 4574 | return_type: Type, | 4439 | return_type: Type, |
| 4440 | /// If zero use default target function code alignment. | ||
| 4441 | alignment: u32, | ||
| 4575 | cc: std.builtin.CallingConvention, | 4442 | cc: std.builtin.CallingConvention, |
| 4576 | is_var_args: bool, | 4443 | is_var_args: bool, |
| 4577 | is_generic: bool, | 4444 | is_generic: bool, |
src/value.zig+10-2| ... | @@ -1520,6 +1520,11 @@ pub const Value = extern union { | ... | @@ -1520,6 +1520,11 @@ pub const Value = extern union { |
| 1520 | } | 1520 | } |
| 1521 | return true; | 1521 | return true; |
| 1522 | }, | 1522 | }, |
| 1523 | .function => { | ||
| 1524 | const a_payload = a.castTag(.function).?.data; | ||
| 1525 | const b_payload = b.castTag(.function).?.data; | ||
| 1526 | return a_payload == b_payload; | ||
| 1527 | }, | ||
| 1523 | else => {}, | 1528 | else => {}, |
| 1524 | } | 1529 | } |
| 1525 | } else if (a_tag == .null_value or b_tag == .null_value) { | 1530 | } else if (a_tag == .null_value or b_tag == .null_value) { |
| ... | @@ -1573,6 +1578,7 @@ pub const Value = extern union { | ... | @@ -1573,6 +1578,7 @@ pub const Value = extern union { |
| 1573 | pub fn hash(val: Value, ty: Type, hasher: *std.hash.Wyhash) void { | 1578 | pub fn hash(val: Value, ty: Type, hasher: *std.hash.Wyhash) void { |
| 1574 | const zig_ty_tag = ty.zigTypeTag(); | 1579 | const zig_ty_tag = ty.zigTypeTag(); |
| 1575 | std.hash.autoHash(hasher, zig_ty_tag); | 1580 | std.hash.autoHash(hasher, zig_ty_tag); |
| 1581 | if (val.isUndef()) return; | ||
| 1576 | 1582 | ||
| 1577 | switch (zig_ty_tag) { | 1583 | switch (zig_ty_tag) { |
| 1578 | .BoundFn => unreachable, // TODO remove this from the language | 1584 | .BoundFn => unreachable, // TODO remove this from the language |
| ... | @@ -1694,7 +1700,8 @@ pub const Value = extern union { | ... | @@ -1694,7 +1700,8 @@ pub const Value = extern union { |
| 1694 | union_obj.val.hash(active_field_ty, hasher); | 1700 | union_obj.val.hash(active_field_ty, hasher); |
| 1695 | }, | 1701 | }, |
| 1696 | .Fn => { | 1702 | .Fn => { |
| 1697 | @panic("TODO implement hashing function values"); | 1703 | const func = val.castTag(.function).?.data; |
| 1704 | return std.hash.autoHash(hasher, func.owner_decl); | ||
| 1698 | }, | 1705 | }, |
| 1699 | .Frame => { | 1706 | .Frame => { |
| 1700 | @panic("TODO implement hashing frame values"); | 1707 | @panic("TODO implement hashing frame values"); |
| ... | @@ -1703,7 +1710,8 @@ pub const Value = extern union { | ... | @@ -1703,7 +1710,8 @@ pub const Value = extern union { |
| 1703 | @panic("TODO implement hashing anyframe values"); | 1710 | @panic("TODO implement hashing anyframe values"); |
| 1704 | }, | 1711 | }, |
| 1705 | .EnumLiteral => { | 1712 | .EnumLiteral => { |
| 1706 | @panic("TODO implement hashing enum literal values"); | 1713 | const bytes = val.castTag(.enum_literal).?.data; |
| 1714 | hasher.update(bytes); | ||
| 1707 | }, | 1715 | }, |
| 1708 | } | 1716 | } |
| 1709 | } | 1717 | } |
test/behavior.zig+9-12| ... | @@ -2,22 +2,23 @@ const builtin = @import("builtin"); | ... | @@ -2,22 +2,23 @@ const builtin = @import("builtin"); |
| 2 | 2 | ||
| 3 | test { | 3 | test { |
| 4 | // Tests that pass for stage1, llvm backend, C backend, wasm backend, arm backend and x86_64 backend. | 4 | // Tests that pass for stage1, llvm backend, C backend, wasm backend, arm backend and x86_64 backend. |
| 5 | _ = @import("behavior/align.zig"); | ||
| 6 | _ = @import("behavior/array.zig"); | ||
| 7 | _ = @import("behavior/bool.zig"); | ||
| 8 | _ = @import("behavior/bugs/655.zig"); | ||
| 9 | _ = @import("behavior/bugs/679.zig"); | ||
| 5 | _ = @import("behavior/bugs/1111.zig"); | 10 | _ = @import("behavior/bugs/1111.zig"); |
| 6 | _ = @import("behavior/bugs/2346.zig"); | 11 | _ = @import("behavior/bugs/2346.zig"); |
| 7 | _ = @import("behavior/slice_sentinel_comptime.zig"); | ||
| 8 | _ = @import("behavior/bugs/679.zig"); | ||
| 9 | _ = @import("behavior/bugs/6850.zig"); | 12 | _ = @import("behavior/bugs/6850.zig"); |
| 13 | _ = @import("behavior/cast.zig"); | ||
| 14 | _ = @import("behavior/comptime_memory.zig"); | ||
| 10 | _ = @import("behavior/fn_in_struct_in_comptime.zig"); | 15 | _ = @import("behavior/fn_in_struct_in_comptime.zig"); |
| 11 | _ = @import("behavior/hasdecl.zig"); | 16 | _ = @import("behavior/hasdecl.zig"); |
| 12 | _ = @import("behavior/hasfield.zig"); | 17 | _ = @import("behavior/hasfield.zig"); |
| 13 | _ = @import("behavior/prefetch.zig"); | 18 | _ = @import("behavior/prefetch.zig"); |
| 14 | _ = @import("behavior/pub_enum.zig"); | 19 | _ = @import("behavior/pub_enum.zig"); |
| 20 | _ = @import("behavior/slice_sentinel_comptime.zig"); | ||
| 15 | _ = @import("behavior/type.zig"); | 21 | _ = @import("behavior/type.zig"); |
| 16 | _ = @import("behavior/bugs/655.zig"); | ||
| 17 | _ = @import("behavior/bool.zig"); | ||
| 18 | _ = @import("behavior/align.zig"); | ||
| 19 | _ = @import("behavior/array.zig"); | ||
| 20 | _ = @import("behavior/cast.zig"); | ||
| 21 | 22 | ||
| 22 | if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_x86_64) { | 23 | if (builtin.zig_backend != .stage2_arm and builtin.zig_backend != .stage2_x86_64) { |
| 23 | // Tests that pass for stage1, llvm backend, C backend, wasm backend. | 24 | // Tests that pass for stage1, llvm backend, C backend, wasm backend. |
| ... | @@ -113,11 +114,7 @@ test { | ... | @@ -113,11 +114,7 @@ test { |
| 113 | _ = @import("behavior/switch.zig"); | 114 | _ = @import("behavior/switch.zig"); |
| 114 | _ = @import("behavior/widening.zig"); | 115 | _ = @import("behavior/widening.zig"); |
| 115 | 116 | ||
| 116 | if (builtin.zig_backend != .stage1) { | 117 | if (builtin.zig_backend == .stage1) { |
| 117 | // When all comptime_memory.zig tests pass, #9646 can be closed. | ||
| 118 | // _ = @import("behavior/comptime_memory.zig"); | ||
| 119 | _ = @import("behavior/slice_stage2.zig"); | ||
| 120 | } else { | ||
| 121 | // Tests that only pass for the stage1 backend. | 118 | // Tests that only pass for the stage1 backend. |
| 122 | _ = @import("behavior/align_stage1.zig"); | 119 | _ = @import("behavior/align_stage1.zig"); |
| 123 | if (builtin.os.tag != .wasi) { | 120 | if (builtin.os.tag != .wasi) { |
test/behavior/align.zig+20| ... | @@ -181,3 +181,23 @@ test "page aligned array on stack" { | ... | @@ -181,3 +181,23 @@ test "page aligned array on stack" { |
| 181 | try expect(number1 == 42); | 181 | try expect(number1 == 42); |
| 182 | try expect(number2 == 43); | 182 | try expect(number2 == 43); |
| 183 | } | 183 | } |
| 184 | |||
| 185 | fn derp() align(@sizeOf(usize) * 2) i32 { | ||
| 186 | return 1234; | ||
| 187 | } | ||
| 188 | fn noop1() align(1) void {} | ||
| 189 | fn noop4() align(4) void {} | ||
| 190 | |||
| 191 | test "function alignment" { | ||
| 192 | // function alignment is a compile error on wasm32/wasm64 | ||
| 193 | if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest; | ||
| 194 | |||
| 195 | if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; | ||
| 196 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; | ||
| 197 | |||
| 198 | try expect(derp() == 1234); | ||
| 199 | try expect(@TypeOf(noop1) == fn () align(1) void); | ||
| 200 | try expect(@TypeOf(noop4) == fn () align(4) void); | ||
| 201 | noop1(); | ||
| 202 | noop4(); | ||
| 203 | } |
test/behavior/align_stage1.zig-17| ... | @@ -3,23 +3,6 @@ const expect = std.testing.expect; | ... | @@ -3,23 +3,6 @@ const expect = std.testing.expect; |
| 3 | const builtin = @import("builtin"); | 3 | const builtin = @import("builtin"); |
| 4 | const native_arch = builtin.target.cpu.arch; | 4 | const native_arch = builtin.target.cpu.arch; |
| 5 | 5 | ||
| 6 | fn derp() align(@sizeOf(usize) * 2) i32 { | ||
| 7 | return 1234; | ||
| 8 | } | ||
| 9 | fn noop1() align(1) void {} | ||
| 10 | fn noop4() align(4) void {} | ||
| 11 | |||
| 12 | test "function alignment" { | ||
| 13 | // function alignment is a compile error on wasm32/wasm64 | ||
| 14 | if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest; | ||
| 15 | |||
| 16 | try expect(derp() == 1234); | ||
| 17 | try expect(@TypeOf(noop1) == fn () align(1) void); | ||
| 18 | try expect(@TypeOf(noop4) == fn () align(4) void); | ||
| 19 | noop1(); | ||
| 20 | noop4(); | ||
| 21 | } | ||
| 22 | |||
| 23 | test "implicitly decreasing fn alignment" { | 6 | test "implicitly decreasing fn alignment" { |
| 24 | // function alignment is a compile error on wasm32/wasm64 | 7 | // function alignment is a compile error on wasm32/wasm64 |
| 25 | if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest; | 8 | if (native_arch == .wasm32 or native_arch == .wasm64) return error.SkipZigTest; |
test/behavior/basic.zig+3-1| ... | @@ -259,6 +259,8 @@ fn fB() []const u8 { | ... | @@ -259,6 +259,8 @@ fn fB() []const u8 { |
| 259 | } | 259 | } |
| 260 | 260 | ||
| 261 | test "call function pointer in struct" { | 261 | test "call function pointer in struct" { |
| 262 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 263 | |||
| 262 | try expect(mem.eql(u8, f3(true), "a")); | 264 | try expect(mem.eql(u8, f3(true), "a")); |
| 263 | try expect(mem.eql(u8, f3(false), "b")); | 265 | try expect(mem.eql(u8, f3(false), "b")); |
| 264 | } | 266 | } |
| ... | @@ -276,7 +278,7 @@ fn f3(x: bool) []const u8 { | ... | @@ -276,7 +278,7 @@ fn f3(x: bool) []const u8 { |
| 276 | } | 278 | } |
| 277 | 279 | ||
| 278 | const FnPtrWrapper = struct { | 280 | const FnPtrWrapper = struct { |
| 279 | fn_ptr: fn () []const u8, | 281 | fn_ptr: *const fn () []const u8, |
| 280 | }; | 282 | }; |
| 281 | 283 | ||
| 282 | test "const ptr from var variable" { | 284 | test "const ptr from var variable" { |
test/behavior/basic_llvm.zig+3-1| ... | @@ -205,9 +205,11 @@ test "multiline string literal is null terminated" { | ... | @@ -205,9 +205,11 @@ test "multiline string literal is null terminated" { |
| 205 | } | 205 | } |
| 206 | 206 | ||
| 207 | test "self reference through fn ptr field" { | 207 | test "self reference through fn ptr field" { |
| 208 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 209 | |||
| 208 | const S = struct { | 210 | const S = struct { |
| 209 | const A = struct { | 211 | const A = struct { |
| 210 | f: fn (A) u8, | 212 | f: *const fn (A) u8, |
| 211 | }; | 213 | }; |
| 212 | 214 | ||
| 213 | fn foo(a: A) u8 { | 215 | fn foo(a: A) u8 { |
test/behavior/bugs/1500.zig+1-1| ... | @@ -2,7 +2,7 @@ const A = struct { | ... | @@ -2,7 +2,7 @@ const A = struct { |
| 2 | b: B, | 2 | b: B, |
| 3 | }; | 3 | }; |
| 4 | 4 | ||
| 5 | const B = fn (A) void; | 5 | const B = *const fn (A) void; |
| 6 | 6 | ||
| 7 | test "allow these dependencies" { | 7 | test "allow these dependencies" { |
| 8 | var a: A = undefined; | 8 | var a: A = undefined; |
test/behavior/bugs/3112.zig+4-1| ... | @@ -1,9 +1,10 @@ | ... | @@ -1,9 +1,10 @@ |
| 1 | const builtin = @import("builtin"); | ||
| 1 | const std = @import("std"); | 2 | const std = @import("std"); |
| 2 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 3 | 4 | ||
| 4 | const State = struct { | 5 | const State = struct { |
| 5 | const Self = @This(); | 6 | const Self = @This(); |
| 6 | enter: fn (previous: ?Self) void, | 7 | enter: *const fn (previous: ?Self) void, |
| 7 | }; | 8 | }; |
| 8 | 9 | ||
| 9 | fn prev(p: ?State) void { | 10 | fn prev(p: ?State) void { |
| ... | @@ -11,6 +12,8 @@ fn prev(p: ?State) void { | ... | @@ -11,6 +12,8 @@ fn prev(p: ?State) void { |
| 11 | } | 12 | } |
| 12 | 13 | ||
| 13 | test "zig test crash" { | 14 | test "zig test crash" { |
| 15 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 16 | |||
| 14 | var global: State = undefined; | 17 | var global: State = undefined; |
| 15 | global.enter = prev; | 18 | global.enter = prev; |
| 16 | global.enter(null); | 19 | global.enter(null); |
test/behavior/cast_llvm.zig+4-2| ... | @@ -47,12 +47,14 @@ fn incrementVoidPtrArray(array: ?*anyopaque, len: usize) void { | ... | @@ -47,12 +47,14 @@ fn incrementVoidPtrArray(array: ?*anyopaque, len: usize) void { |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | test "compile time int to ptr of function" { | 49 | test "compile time int to ptr of function" { |
| 50 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 50 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) return error.SkipZigTest; // TODO | 51 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) return error.SkipZigTest; // TODO |
| 52 | |||
| 51 | try foobar(FUNCTION_CONSTANT); | 53 | try foobar(FUNCTION_CONSTANT); |
| 52 | } | 54 | } |
| 53 | 55 | ||
| 54 | pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize)); | 56 | pub const FUNCTION_CONSTANT = @intToPtr(PFN_void, maxInt(usize)); |
| 55 | pub const PFN_void = fn (*anyopaque) callconv(.C) void; | 57 | pub const PFN_void = *const fn (*anyopaque) callconv(.C) void; |
| 56 | 58 | ||
| 57 | fn foobar(func: PFN_void) !void { | 59 | fn foobar(func: PFN_void) !void { |
| 58 | try std.testing.expect(@ptrToInt(func) == maxInt(usize)); | 60 | try std.testing.expect(@ptrToInt(func) == maxInt(usize)); |
| ... | @@ -154,7 +156,7 @@ test "implicit cast *[0]T to E![]const u8" { | ... | @@ -154,7 +156,7 @@ test "implicit cast *[0]T to E![]const u8" { |
| 154 | 156 | ||
| 155 | var global_array: [4]u8 = undefined; | 157 | var global_array: [4]u8 = undefined; |
| 156 | test "cast from array reference to fn" { | 158 | test "cast from array reference to fn" { |
| 157 | const f = @ptrCast(fn () callconv(.C) void, &global_array); | 159 | const f = @ptrCast(*const fn () callconv(.C) void, &global_array); |
| 158 | try expect(@ptrToInt(f) == @ptrToInt(&global_array)); | 160 | try expect(@ptrToInt(f) == @ptrToInt(&global_array)); |
| 159 | } | 161 | } |
| 160 | 162 |
test/behavior/comptime_memory.zig+98-1| ... | @@ -1,8 +1,15 @@ | ... | @@ -1,8 +1,15 @@ |
| 1 | const endian = @import("builtin").cpu.arch.endian(); | 1 | const builtin = @import("builtin"); |
| 2 | const endian = builtin.cpu.arch.endian(); | ||
| 2 | const testing = @import("std").testing; | 3 | const testing = @import("std").testing; |
| 3 | const ptr_size = @sizeOf(usize); | 4 | const ptr_size = @sizeOf(usize); |
| 4 | 5 | ||
| 5 | test "type pun signed and unsigned as single pointer" { | 6 | test "type pun signed and unsigned as single pointer" { |
| 7 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 8 | if (builtin.zig_backend != .stage1) { | ||
| 9 | // TODO https://github.com/ziglang/zig/issues/9646 | ||
| 10 | return error.SkipZigTest; | ||
| 11 | } | ||
| 12 | |||
| 6 | comptime { | 13 | comptime { |
| 7 | var x: u32 = 0; | 14 | var x: u32 = 0; |
| 8 | const y = @ptrCast(*i32, &x); | 15 | const y = @ptrCast(*i32, &x); |
| ... | @@ -12,6 +19,12 @@ test "type pun signed and unsigned as single pointer" { | ... | @@ -12,6 +19,12 @@ test "type pun signed and unsigned as single pointer" { |
| 12 | } | 19 | } |
| 13 | 20 | ||
| 14 | test "type pun signed and unsigned as many pointer" { | 21 | test "type pun signed and unsigned as many pointer" { |
| 22 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 23 | if (builtin.zig_backend != .stage1) { | ||
| 24 | // TODO https://github.com/ziglang/zig/issues/9646 | ||
| 25 | return error.SkipZigTest; | ||
| 26 | } | ||
| 27 | |||
| 15 | comptime { | 28 | comptime { |
| 16 | var x: u32 = 0; | 29 | var x: u32 = 0; |
| 17 | const y = @ptrCast([*]i32, &x); | 30 | const y = @ptrCast([*]i32, &x); |
| ... | @@ -21,6 +34,12 @@ test "type pun signed and unsigned as many pointer" { | ... | @@ -21,6 +34,12 @@ test "type pun signed and unsigned as many pointer" { |
| 21 | } | 34 | } |
| 22 | 35 | ||
| 23 | test "type pun signed and unsigned as array pointer" { | 36 | test "type pun signed and unsigned as array pointer" { |
| 37 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 38 | if (builtin.zig_backend != .stage1) { | ||
| 39 | // TODO https://github.com/ziglang/zig/issues/9646 | ||
| 40 | return error.SkipZigTest; | ||
| 41 | } | ||
| 42 | |||
| 24 | comptime { | 43 | comptime { |
| 25 | var x: u32 = 0; | 44 | var x: u32 = 0; |
| 26 | const y = @ptrCast(*[1]i32, &x); | 45 | const y = @ptrCast(*[1]i32, &x); |
| ... | @@ -30,6 +49,12 @@ test "type pun signed and unsigned as array pointer" { | ... | @@ -30,6 +49,12 @@ test "type pun signed and unsigned as array pointer" { |
| 30 | } | 49 | } |
| 31 | 50 | ||
| 32 | test "type pun signed and unsigned as offset many pointer" { | 51 | test "type pun signed and unsigned as offset many pointer" { |
| 52 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 53 | if (builtin.zig_backend != .stage1) { | ||
| 54 | // TODO https://github.com/ziglang/zig/issues/9646 | ||
| 55 | return error.SkipZigTest; | ||
| 56 | } | ||
| 57 | |||
| 33 | comptime { | 58 | comptime { |
| 34 | var x: u32 = 0; | 59 | var x: u32 = 0; |
| 35 | var y = @ptrCast([*]i32, &x); | 60 | var y = @ptrCast([*]i32, &x); |
| ... | @@ -40,6 +65,12 @@ test "type pun signed and unsigned as offset many pointer" { | ... | @@ -40,6 +65,12 @@ test "type pun signed and unsigned as offset many pointer" { |
| 40 | } | 65 | } |
| 41 | 66 | ||
| 42 | test "type pun signed and unsigned as array pointer" { | 67 | test "type pun signed and unsigned as array pointer" { |
| 68 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 69 | if (builtin.zig_backend != .stage1) { | ||
| 70 | // TODO https://github.com/ziglang/zig/issues/9646 | ||
| 71 | return error.SkipZigTest; | ||
| 72 | } | ||
| 73 | |||
| 43 | comptime { | 74 | comptime { |
| 44 | var x: u32 = 0; | 75 | var x: u32 = 0; |
| 45 | const y = @ptrCast([*]i32, &x) - 10; | 76 | const y = @ptrCast([*]i32, &x) - 10; |
| ... | @@ -50,6 +81,12 @@ test "type pun signed and unsigned as array pointer" { | ... | @@ -50,6 +81,12 @@ test "type pun signed and unsigned as array pointer" { |
| 50 | } | 81 | } |
| 51 | 82 | ||
| 52 | test "type pun value and struct" { | 83 | test "type pun value and struct" { |
| 84 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 85 | if (builtin.zig_backend != .stage1) { | ||
| 86 | // TODO https://github.com/ziglang/zig/issues/9646 | ||
| 87 | return error.SkipZigTest; | ||
| 88 | } | ||
| 89 | |||
| 53 | comptime { | 90 | comptime { |
| 54 | const StructOfU32 = extern struct { x: u32 }; | 91 | const StructOfU32 = extern struct { x: u32 }; |
| 55 | var inst: StructOfU32 = .{ .x = 0 }; | 92 | var inst: StructOfU32 = .{ .x = 0 }; |
| ... | @@ -64,6 +101,12 @@ fn bigToNativeEndian(comptime T: type, v: T) T { | ... | @@ -64,6 +101,12 @@ fn bigToNativeEndian(comptime T: type, v: T) T { |
| 64 | return if (endian == .Big) v else @byteSwap(T, v); | 101 | return if (endian == .Big) v else @byteSwap(T, v); |
| 65 | } | 102 | } |
| 66 | test "type pun endianness" { | 103 | test "type pun endianness" { |
| 104 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 105 | if (builtin.zig_backend != .stage1) { | ||
| 106 | // TODO https://github.com/ziglang/zig/issues/9646 | ||
| 107 | return error.SkipZigTest; | ||
| 108 | } | ||
| 109 | |||
| 67 | comptime { | 110 | comptime { |
| 68 | const StructOfBytes = extern struct { x: [4]u8 }; | 111 | const StructOfBytes = extern struct { x: [4]u8 }; |
| 69 | var inst: StructOfBytes = .{ .x = [4]u8{ 0, 0, 0, 0 } }; | 112 | var inst: StructOfBytes = .{ .x = [4]u8{ 0, 0, 0, 0 } }; |
| ... | @@ -155,6 +198,12 @@ fn doTypePunBitsTest(as_bits: *Bits) !void { | ... | @@ -155,6 +198,12 @@ fn doTypePunBitsTest(as_bits: *Bits) !void { |
| 155 | } | 198 | } |
| 156 | 199 | ||
| 157 | test "type pun bits" { | 200 | test "type pun bits" { |
| 201 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 202 | if (builtin.zig_backend != .stage1) { | ||
| 203 | // TODO https://github.com/ziglang/zig/issues/9646 | ||
| 204 | return error.SkipZigTest; | ||
| 205 | } | ||
| 206 | |||
| 158 | comptime { | 207 | comptime { |
| 159 | var v: u32 = undefined; | 208 | var v: u32 = undefined; |
| 160 | try doTypePunBitsTest(@ptrCast(*Bits, &v)); | 209 | try doTypePunBitsTest(@ptrCast(*Bits, &v)); |
| ... | @@ -167,6 +216,12 @@ const imports = struct { | ... | @@ -167,6 +216,12 @@ const imports = struct { |
| 167 | 216 | ||
| 168 | // Make sure lazy values work on their own, before getting into more complex tests | 217 | // Make sure lazy values work on their own, before getting into more complex tests |
| 169 | test "basic pointer preservation" { | 218 | test "basic pointer preservation" { |
| 219 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 220 | if (builtin.zig_backend != .stage1) { | ||
| 221 | // TODO https://github.com/ziglang/zig/issues/9646 | ||
| 222 | return error.SkipZigTest; | ||
| 223 | } | ||
| 224 | |||
| 170 | comptime { | 225 | comptime { |
| 171 | const lazy_address = @ptrToInt(&imports.global_u32); | 226 | const lazy_address = @ptrToInt(&imports.global_u32); |
| 172 | try testing.expectEqual(@ptrToInt(&imports.global_u32), lazy_address); | 227 | try testing.expectEqual(@ptrToInt(&imports.global_u32), lazy_address); |
| ... | @@ -175,6 +230,12 @@ test "basic pointer preservation" { | ... | @@ -175,6 +230,12 @@ test "basic pointer preservation" { |
| 175 | } | 230 | } |
| 176 | 231 | ||
| 177 | test "byte copy preserves linker value" { | 232 | test "byte copy preserves linker value" { |
| 233 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 234 | if (builtin.zig_backend != .stage1) { | ||
| 235 | // TODO https://github.com/ziglang/zig/issues/9646 | ||
| 236 | return error.SkipZigTest; | ||
| 237 | } | ||
| 238 | |||
| 178 | const ct_value = comptime blk: { | 239 | const ct_value = comptime blk: { |
| 179 | const lazy = &imports.global_u32; | 240 | const lazy = &imports.global_u32; |
| 180 | var result: *u32 = undefined; | 241 | var result: *u32 = undefined; |
| ... | @@ -193,6 +254,12 @@ test "byte copy preserves linker value" { | ... | @@ -193,6 +254,12 @@ test "byte copy preserves linker value" { |
| 193 | } | 254 | } |
| 194 | 255 | ||
| 195 | test "unordered byte copy preserves linker value" { | 256 | test "unordered byte copy preserves linker value" { |
| 257 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 258 | if (builtin.zig_backend != .stage1) { | ||
| 259 | // TODO https://github.com/ziglang/zig/issues/9646 | ||
| 260 | return error.SkipZigTest; | ||
| 261 | } | ||
| 262 | |||
| 196 | const ct_value = comptime blk: { | 263 | const ct_value = comptime blk: { |
| 197 | const lazy = &imports.global_u32; | 264 | const lazy = &imports.global_u32; |
| 198 | var result: *u32 = undefined; | 265 | var result: *u32 = undefined; |
| ... | @@ -212,6 +279,12 @@ test "unordered byte copy preserves linker value" { | ... | @@ -212,6 +279,12 @@ test "unordered byte copy preserves linker value" { |
| 212 | } | 279 | } |
| 213 | 280 | ||
| 214 | test "shuffle chunks of linker value" { | 281 | test "shuffle chunks of linker value" { |
| 282 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 283 | if (builtin.zig_backend != .stage1) { | ||
| 284 | // TODO https://github.com/ziglang/zig/issues/9646 | ||
| 285 | return error.SkipZigTest; | ||
| 286 | } | ||
| 287 | |||
| 215 | const lazy_address = @ptrToInt(&imports.global_u32); | 288 | const lazy_address = @ptrToInt(&imports.global_u32); |
| 216 | const shuffled1_rt = shuffle(lazy_address, Bits, ShuffledBits); | 289 | const shuffled1_rt = shuffle(lazy_address, Bits, ShuffledBits); |
| 217 | const unshuffled1_rt = shuffle(shuffled1_rt, ShuffledBits, Bits); | 290 | const unshuffled1_rt = shuffle(shuffled1_rt, ShuffledBits, Bits); |
| ... | @@ -225,6 +298,12 @@ test "shuffle chunks of linker value" { | ... | @@ -225,6 +298,12 @@ test "shuffle chunks of linker value" { |
| 225 | } | 298 | } |
| 226 | 299 | ||
| 227 | test "dance on linker values" { | 300 | test "dance on linker values" { |
| 301 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 302 | if (builtin.zig_backend != .stage1) { | ||
| 303 | // TODO https://github.com/ziglang/zig/issues/9646 | ||
| 304 | return error.SkipZigTest; | ||
| 305 | } | ||
| 306 | |||
| 228 | comptime { | 307 | comptime { |
| 229 | var arr: [2]usize = undefined; | 308 | var arr: [2]usize = undefined; |
| 230 | arr[0] = @ptrToInt(&imports.global_u32); | 309 | arr[0] = @ptrToInt(&imports.global_u32); |
| ... | @@ -251,6 +330,12 @@ test "dance on linker values" { | ... | @@ -251,6 +330,12 @@ test "dance on linker values" { |
| 251 | } | 330 | } |
| 252 | 331 | ||
| 253 | test "offset array ptr by element size" { | 332 | test "offset array ptr by element size" { |
| 333 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 334 | if (builtin.zig_backend != .stage1) { | ||
| 335 | // TODO https://github.com/ziglang/zig/issues/9646 | ||
| 336 | return error.SkipZigTest; | ||
| 337 | } | ||
| 338 | |||
| 254 | comptime { | 339 | comptime { |
| 255 | const VirtualStruct = struct { x: u32 }; | 340 | const VirtualStruct = struct { x: u32 }; |
| 256 | var arr: [4]VirtualStruct = .{ | 341 | var arr: [4]VirtualStruct = .{ |
| ... | @@ -273,6 +358,12 @@ test "offset array ptr by element size" { | ... | @@ -273,6 +358,12 @@ test "offset array ptr by element size" { |
| 273 | } | 358 | } |
| 274 | 359 | ||
| 275 | test "offset instance by field size" { | 360 | test "offset instance by field size" { |
| 361 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 362 | if (builtin.zig_backend != .stage1) { | ||
| 363 | // TODO https://github.com/ziglang/zig/issues/9646 | ||
| 364 | return error.SkipZigTest; | ||
| 365 | } | ||
| 366 | |||
| 276 | comptime { | 367 | comptime { |
| 277 | const VirtualStruct = struct { x: u32, y: u32, z: u32, w: u32 }; | 368 | const VirtualStruct = struct { x: u32, y: u32, z: u32, w: u32 }; |
| 278 | var inst = VirtualStruct{ .x = 0, .y = 1, .z = 2, .w = 3 }; | 369 | var inst = VirtualStruct{ .x = 0, .y = 1, .z = 2, .w = 3 }; |
| ... | @@ -293,6 +384,12 @@ test "offset instance by field size" { | ... | @@ -293,6 +384,12 @@ test "offset instance by field size" { |
| 293 | } | 384 | } |
| 294 | 385 | ||
| 295 | test "offset field ptr by enclosing array element size" { | 386 | test "offset field ptr by enclosing array element size" { |
| 387 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 388 | if (builtin.zig_backend != .stage1) { | ||
| 389 | // TODO https://github.com/ziglang/zig/issues/9646 | ||
| 390 | return error.SkipZigTest; | ||
| 391 | } | ||
| 392 | |||
| 296 | comptime { | 393 | comptime { |
| 297 | const VirtualStruct = struct { x: u32 }; | 394 | const VirtualStruct = struct { x: u32 }; |
| 298 | var arr: [4]VirtualStruct = .{ | 395 | var arr: [4]VirtualStruct = .{ |
test/behavior/error.zig+1| ... | @@ -1,3 +1,4 @@ | ... | @@ -1,3 +1,4 @@ |
| 1 | const builtin = @import("builtin"); | ||
| 1 | const std = @import("std"); | 2 | const std = @import("std"); |
| 2 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 3 | const expectError = std.testing.expectError; | 4 | const expectError = std.testing.expectError; |
test/behavior/fn.zig+9-3| ... | @@ -57,7 +57,7 @@ test "assign inline fn to const variable" { | ... | @@ -57,7 +57,7 @@ test "assign inline fn to const variable" { |
| 57 | 57 | ||
| 58 | inline fn inlineFn() void {} | 58 | inline fn inlineFn() void {} |
| 59 | 59 | ||
| 60 | fn outer(y: u32) fn (u32) u32 { | 60 | fn outer(y: u32) *const fn (u32) u32 { |
| 61 | const Y = @TypeOf(y); | 61 | const Y = @TypeOf(y); |
| 62 | const st = struct { | 62 | const st = struct { |
| 63 | fn get(z: u32) u32 { | 63 | fn get(z: u32) u32 { |
| ... | @@ -68,6 +68,8 @@ fn outer(y: u32) fn (u32) u32 { | ... | @@ -68,6 +68,8 @@ fn outer(y: u32) fn (u32) u32 { |
| 68 | } | 68 | } |
| 69 | 69 | ||
| 70 | test "return inner function which references comptime variable of outer function" { | 70 | test "return inner function which references comptime variable of outer function" { |
| 71 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 72 | |||
| 71 | var func = outer(10); | 73 | var func = outer(10); |
| 72 | try expect(func(3) == 7); | 74 | try expect(func(3) == 7); |
| 73 | } | 75 | } |
| ... | @@ -92,6 +94,8 @@ test "discard the result of a function that returns a struct" { | ... | @@ -92,6 +94,8 @@ test "discard the result of a function that returns a struct" { |
| 92 | } | 94 | } |
| 93 | 95 | ||
| 94 | test "inline function call that calls optional function pointer, return pointer at callsite interacts correctly with callsite return type" { | 96 | test "inline function call that calls optional function pointer, return pointer at callsite interacts correctly with callsite return type" { |
| 97 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 98 | |||
| 95 | const S = struct { | 99 | const S = struct { |
| 96 | field: u32, | 100 | field: u32, |
| 97 | 101 | ||
| ... | @@ -113,7 +117,7 @@ test "inline function call that calls optional function pointer, return pointer | ... | @@ -113,7 +117,7 @@ test "inline function call that calls optional function pointer, return pointer |
| 113 | return bar2.?(); | 117 | return bar2.?(); |
| 114 | } | 118 | } |
| 115 | 119 | ||
| 116 | var bar2: ?fn () u32 = null; | 120 | var bar2: ?*const fn () u32 = null; |
| 117 | 121 | ||
| 118 | fn actualFn() u32 { | 122 | fn actualFn() u32 { |
| 119 | return 1234; | 123 | return 1234; |
| ... | @@ -135,8 +139,10 @@ fn fnWithUnreachable() noreturn { | ... | @@ -135,8 +139,10 @@ fn fnWithUnreachable() noreturn { |
| 135 | } | 139 | } |
| 136 | 140 | ||
| 137 | test "extern struct with stdcallcc fn pointer" { | 141 | test "extern struct with stdcallcc fn pointer" { |
| 142 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 143 | |||
| 138 | const S = extern struct { | 144 | const S = extern struct { |
| 139 | ptr: fn () callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32, | 145 | ptr: *const fn () callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32, |
| 140 | 146 | ||
| 141 | fn foo() callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32 { | 147 | fn foo() callconv(if (builtin.target.cpu.arch == .i386) .Stdcall else .C) i32 { |
| 142 | return 1234; | 148 | return 1234; |
test/behavior/inttoptr.zig+7-5| ... | @@ -1,14 +1,16 @@ | ... | @@ -1,14 +1,16 @@ |
| 1 | const builtin = @import("builtin"); | 1 | const builtin = @import("builtin"); |
| 2 | 2 | ||
| 3 | test "casting random address to function pointer" { | 3 | test "casting integer address to function pointer" { |
| 4 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 4 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) return error.SkipZigTest; // TODO | 5 | if (builtin.zig_backend == .stage2_llvm and builtin.cpu.arch == .aarch64) return error.SkipZigTest; // TODO |
| 5 | randomAddressToFunction(); | 6 | |
| 6 | comptime randomAddressToFunction(); | 7 | addressToFunction(); |
| 8 | comptime addressToFunction(); | ||
| 7 | } | 9 | } |
| 8 | 10 | ||
| 9 | fn randomAddressToFunction() void { | 11 | fn addressToFunction() void { |
| 10 | var addr: usize = 0xdeadbeef; | 12 | var addr: usize = 0xdeadbeef; |
| 11 | _ = @intToPtr(fn () void, addr); | 13 | _ = @intToPtr(*const fn () void, addr); |
| 12 | } | 14 | } |
| 13 | 15 | ||
| 14 | test "mutate through ptr initialized with constant intToPtr value" { | 16 | test "mutate through ptr initialized with constant intToPtr value" { |
test/behavior/member_func.zig+8-2| ... | @@ -1,8 +1,10 @@ | ... | @@ -1,8 +1,10 @@ |
| 1 | const expect = @import("std").testing.expect; | 1 | const builtin = @import("builtin"); |
| 2 | const std = @import("std"); | ||
| 3 | const expect = std.testing.expect; | ||
| 2 | 4 | ||
| 3 | const HasFuncs = struct { | 5 | const HasFuncs = struct { |
| 4 | state: u32, | 6 | state: u32, |
| 5 | func_field: fn (u32) u32, | 7 | func_field: *const fn (u32) u32, |
| 6 | 8 | ||
| 7 | fn inc(self: *HasFuncs) void { | 9 | fn inc(self: *HasFuncs) void { |
| 8 | self.state += 1; | 10 | self.state += 1; |
| ... | @@ -25,6 +27,8 @@ const HasFuncs = struct { | ... | @@ -25,6 +27,8 @@ const HasFuncs = struct { |
| 25 | }; | 27 | }; |
| 26 | 28 | ||
| 27 | test "standard field calls" { | 29 | test "standard field calls" { |
| 30 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 31 | |||
| 28 | try expect(HasFuncs.one(0) == 1); | 32 | try expect(HasFuncs.one(0) == 1); |
| 29 | try expect(HasFuncs.two(0) == 2); | 33 | try expect(HasFuncs.two(0) == 2); |
| 30 | 34 | ||
| ... | @@ -64,6 +68,8 @@ test "standard field calls" { | ... | @@ -64,6 +68,8 @@ test "standard field calls" { |
| 64 | } | 68 | } |
| 65 | 69 | ||
| 66 | test "@field field calls" { | 70 | test "@field field calls" { |
| 71 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 72 | |||
| 67 | try expect(@field(HasFuncs, "one")(0) == 1); | 73 | try expect(@field(HasFuncs, "one")(0) == 1); |
| 68 | try expect(@field(HasFuncs, "two")(0) == 2); | 74 | try expect(@field(HasFuncs, "two")(0) == 2); |
| 69 | 75 |
test/behavior/slice.zig+13| ... | @@ -1,3 +1,4 @@ | ... | @@ -1,3 +1,4 @@ |
| 1 | const builtin = @import("builtin"); | ||
| 1 | const std = @import("std"); | 2 | const std = @import("std"); |
| 2 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 3 | const expectEqualSlices = std.testing.expectEqualSlices; | 4 | const expectEqualSlices = std.testing.expectEqualSlices; |
| ... | @@ -166,3 +167,15 @@ test "slicing zero length array" { | ... | @@ -166,3 +167,15 @@ test "slicing zero length array" { |
| 166 | try expect(mem.eql(u8, s1, "")); | 167 | try expect(mem.eql(u8, s1, "")); |
| 167 | try expect(mem.eql(u32, s2, &[_]u32{})); | 168 | try expect(mem.eql(u32, s2, &[_]u32{})); |
| 168 | } | 169 | } |
| 170 | |||
| 171 | const x = @intToPtr([*]i32, 0x1000)[0..0x500]; | ||
| 172 | const y = x[0x100..]; | ||
| 173 | test "compile time slice of pointer to hard coded address" { | ||
| 174 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 175 | |||
| 176 | try expect(@ptrToInt(x) == 0x1000); | ||
| 177 | try expect(x.len == 0x500); | ||
| 178 | |||
| 179 | try expect(@ptrToInt(y) == 0x1400); | ||
| 180 | try expect(y.len == 0x400); | ||
| 181 | } |
test/behavior/slice_stage2.zig deleted-12| ... | @@ -1,12 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const expect = std.testing.expect; | ||
| 3 | |||
| 4 | const x = @intToPtr([*]i32, 0x1000)[0..0x500]; | ||
| 5 | const y = x[0x100..]; | ||
| 6 | test "compile time slice of pointer to hard coded address" { | ||
| 7 | try expect(@ptrToInt(x) == 0x1000); | ||
| 8 | try expect(x.len == 0x500); | ||
| 9 | |||
| 10 | try expect(@ptrToInt(y) == 0x1400); | ||
| 11 | try expect(y.len == 0x400); | ||
| 12 | } | ||
test/behavior/union.zig+4-1| ... | @@ -1,3 +1,4 @@ | ... | @@ -1,3 +1,4 @@ |
| 1 | const builtin = @import("builtin"); | ||
| 1 | const std = @import("std"); | 2 | const std = @import("std"); |
| 2 | const expect = std.testing.expect; | 3 | const expect = std.testing.expect; |
| 3 | const expectEqual = std.testing.expectEqual; | 4 | const expectEqual = std.testing.expectEqual; |
| ... | @@ -166,8 +167,10 @@ test "union with specified enum tag" { | ... | @@ -166,8 +167,10 @@ test "union with specified enum tag" { |
| 166 | } | 167 | } |
| 167 | 168 | ||
| 168 | test "packed union generates correctly aligned LLVM type" { | 169 | test "packed union generates correctly aligned LLVM type" { |
| 170 | if (builtin.zig_backend == .stage1) return error.SkipZigTest; | ||
| 171 | |||
| 169 | const U = packed union { | 172 | const U = packed union { |
| 170 | f1: fn () error{TestUnexpectedResult}!void, | 173 | f1: *const fn () error{TestUnexpectedResult}!void, |
| 171 | f2: u32, | 174 | f2: u32, |
| 172 | }; | 175 | }; |
| 173 | var foo = [_]U{ | 176 | var foo = [_]U{ |