aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/AstGen.zig10
-rw-r--r--src/Module.zig119
-rw-r--r--src/Sema.zig576
-rw-r--r--src/codegen/llvm.zig4
-rw-r--r--src/target.zig9
-rw-r--r--src/type.zig229
-rw-r--r--src/value.zig12
7 files changed, 665 insertions, 294 deletions
diff --git a/src/AstGen.zig b/src/AstGen.zig
index 114cd5f505..d75ae82e0a 100644
--- a/src/AstGen.zig
+++ b/src/AstGen.zig
@@ -3240,7 +3240,8 @@ fn fnDecl(
const doc_comment_index = try astgen.docCommentAsString(fn_proto.firstToken());
const has_section_or_addrspace = fn_proto.ast.section_expr != 0 or fn_proto.ast.addrspace_expr != 0;
- wip_members.nextDecl(is_pub, is_export, fn_proto.ast.align_expr != 0, has_section_or_addrspace);
+ // Alignment is passed in the func instruction in this case.
+ wip_members.nextDecl(is_pub, is_export, false, has_section_or_addrspace);
var params_scope = &fn_gz.base;
const is_var_args = is_var_args: {
@@ -3380,7 +3381,7 @@ fn fnDecl(
.param_block = block_inst,
.body_gz = null,
.cc = cc,
- .align_inst = .none, // passed in the per-decl data
+ .align_inst = align_inst,
.lib_name = lib_name,
.is_var_args = is_var_args,
.is_inferred_error = false,
@@ -3423,7 +3424,7 @@ fn fnDecl(
.ret_br = ret_br,
.body_gz = &fn_gz,
.cc = cc,
- .align_inst = .none, // passed in the per-decl data
+ .align_inst = align_inst,
.lib_name = lib_name,
.is_var_args = is_var_args,
.is_inferred_error = is_inferred_error,
@@ -3449,9 +3450,6 @@ fn fnDecl(
wip_members.appendToDecl(fn_name_str_index);
wip_members.appendToDecl(block_inst);
wip_members.appendToDecl(doc_comment_index);
- if (align_inst != .none) {
- wip_members.appendToDecl(@enumToInt(align_inst));
- }
if (has_section_or_addrspace) {
wip_members.appendToDecl(@enumToInt(section_inst));
wip_members.appendToDecl(@enumToInt(addrspace_inst));
diff --git a/src/Module.zig b/src/Module.zig
index 3892b35aea..88469a794a 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -898,6 +898,45 @@ pub const Struct = struct {
};
}
+ pub fn fieldSrcLoc(s: Struct, gpa: Allocator, query: FieldSrcQuery) SrcLoc {
+ @setCold(true);
+ const tree = s.owner_decl.getFileScope().getTree(gpa) catch |err| {
+ // In this case we emit a warning + a less precise source location.
+ log.warn("unable to load {s}: {s}", .{
+ s.owner_decl.getFileScope().sub_file_path, @errorName(err),
+ });
+ return s.srcLoc();
+ };
+ const node = s.owner_decl.relativeToNodeIndex(s.node_offset);
+ const node_tags = tree.nodes.items(.tag);
+ const file = s.owner_decl.getFileScope();
+ switch (node_tags[node]) {
+ .container_decl,
+ .container_decl_trailing,
+ => return queryFieldSrc(tree.*, query, file, tree.containerDecl(node)),
+ .container_decl_two, .container_decl_two_trailing => {
+ var buffer: [2]Ast.Node.Index = undefined;
+ return queryFieldSrc(tree.*, query, file, tree.containerDeclTwo(&buffer, node));
+ },
+ .container_decl_arg,
+ .container_decl_arg_trailing,
+ => return queryFieldSrc(tree.*, query, file, tree.containerDeclArg(node)),
+
+ .tagged_union,
+ .tagged_union_trailing,
+ => return queryFieldSrc(tree.*, query, file, tree.taggedUnion(node)),
+ .tagged_union_two, .tagged_union_two_trailing => {
+ var buffer: [2]Ast.Node.Index = undefined;
+ return queryFieldSrc(tree.*, query, file, tree.taggedUnionTwo(&buffer, node));
+ },
+ .tagged_union_enum_tag,
+ .tagged_union_enum_tag_trailing,
+ => return queryFieldSrc(tree.*, query, file, tree.taggedUnionEnumTag(node)),
+
+ else => unreachable,
+ }
+ }
+
pub fn haveFieldTypes(s: Struct) bool {
return switch (s.status) {
.none,
@@ -1063,6 +1102,33 @@ pub const Union = struct {
};
}
+ pub fn fieldSrcLoc(u: Union, gpa: Allocator, query: FieldSrcQuery) SrcLoc {
+ @setCold(true);
+ const tree = u.owner_decl.getFileScope().getTree(gpa) catch |err| {
+ // In this case we emit a warning + a less precise source location.
+ log.warn("unable to load {s}: {s}", .{
+ u.owner_decl.getFileScope().sub_file_path, @errorName(err),
+ });
+ return u.srcLoc();
+ };
+ const node = u.owner_decl.relativeToNodeIndex(u.node_offset);
+ const node_tags = tree.nodes.items(.tag);
+ const file = u.owner_decl.getFileScope();
+ switch (node_tags[node]) {
+ .container_decl,
+ .container_decl_trailing,
+ => return queryFieldSrc(tree.*, query, file, tree.containerDecl(node)),
+ .container_decl_two, .container_decl_two_trailing => {
+ var buffer: [2]Ast.Node.Index = undefined;
+ return queryFieldSrc(tree.*, query, file, tree.containerDeclTwo(&buffer, node));
+ },
+ .container_decl_arg,
+ .container_decl_arg_trailing,
+ => return queryFieldSrc(tree.*, query, file, tree.containerDeclArg(node)),
+ else => unreachable,
+ }
+ }
+
pub fn haveFieldTypes(u: Union) bool {
return switch (u.status) {
.none,
@@ -4662,8 +4728,8 @@ pub fn createAnonymousDeclFromDeclNamed(
new_decl.src_line = src_decl.src_line;
new_decl.ty = typed_value.ty;
new_decl.val = typed_value.val;
- new_decl.align_val = Value.initTag(.null_value);
- new_decl.linksection_val = Value.initTag(.null_value);
+ new_decl.align_val = Value.@"null";
+ new_decl.linksection_val = Value.@"null";
new_decl.has_tv = true;
new_decl.analysis = .complete;
new_decl.generation = mod.generation;
@@ -4905,6 +4971,55 @@ pub const PeerTypeCandidateSrc = union(enum) {
}
};
+const FieldSrcQuery = struct {
+ index: usize,
+ range: enum { name, type, value, alignment },
+};
+
+fn queryFieldSrc(
+ tree: Ast,
+ query: FieldSrcQuery,
+ file_scope: *File,
+ container_decl: Ast.full.ContainerDecl,
+) SrcLoc {
+ const node_tags = tree.nodes.items(.tag);
+ var field_index: usize = 0;
+ for (container_decl.ast.members) |member_node| {
+ const field = switch (node_tags[member_node]) {
+ .container_field_init => tree.containerFieldInit(member_node),
+ .container_field_align => tree.containerFieldAlign(member_node),
+ .container_field => tree.containerField(member_node),
+ else => continue,
+ };
+ if (field_index == query.index) {
+ return switch (query.range) {
+ .name => .{
+ .file_scope = file_scope,
+ .parent_decl_node = 0,
+ .lazy = .{ .token_abs = field.ast.name_token },
+ },
+ .type => .{
+ .file_scope = file_scope,
+ .parent_decl_node = 0,
+ .lazy = .{ .node_abs = field.ast.type_expr },
+ },
+ .value => .{
+ .file_scope = file_scope,
+ .parent_decl_node = 0,
+ .lazy = .{ .node_abs = field.ast.value_expr },
+ },
+ .alignment => .{
+ .file_scope = file_scope,
+ .parent_decl_node = 0,
+ .lazy = .{ .node_abs = field.ast.align_expr },
+ },
+ };
+ }
+ field_index += 1;
+ }
+ unreachable;
+}
+
/// Called from `performAllTheWork`, after all AstGen workers have finished,
/// and before the main semantic analysis loop begins.
pub fn processOutdatedAndDeletedDecls(mod: *Module) !void {
diff --git a/src/Sema.zig b/src/Sema.zig
index 6a2085cf3f..7da547da3d 100644
--- a/src/Sema.zig
+++ b/src/Sema.zig
@@ -4147,7 +4147,7 @@ fn analyzeCall(
const gpa = sema.gpa;
const is_comptime_call = block.is_comptime or modifier == .compile_time or
- func_ty_info.return_type.requiresComptime();
+ try sema.typeRequiresComptime(block, func_src, func_ty_info.return_type);
const is_inline_call = is_comptime_call or modifier == .always_inline or
func_ty_info.cc == .Inline;
const result: Air.Inst.Ref = if (is_inline_call) res: {
@@ -4576,7 +4576,7 @@ fn analyzeCall(
}
} else if (is_anytype) {
const arg_ty = sema.typeOf(arg);
- if (arg_ty.requiresComptime()) {
+ if (try sema.typeRequiresComptime(block, arg_src, arg_ty)) {
const arg_val = try sema.resolveConstValue(block, arg_src, arg);
const child_arg = try child_sema.addConstant(arg_ty, arg_val);
child_sema.inst_map.putAssumeCapacityNoClobber(inst, child_arg);
@@ -5430,7 +5430,6 @@ fn funcCommon(
src_locs: Zir.Inst.Func.SrcLocs,
opt_lib_name: ?[]const u8,
) CompileError!Air.Inst.Ref {
- const src: LazySrcLoc = .{ .node_offset = src_node_offset };
const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset };
// The return type body might be a type expression that depends on generic parameters.
@@ -5481,11 +5480,22 @@ fn funcCommon(
errdefer if (maybe_inferred_error_set_node) |node| sema.gpa.destroy(node);
// Note: no need to errdefer since this will still be in its default state at the end of the function.
+ const target = mod.getTarget();
+
const fn_ty: Type = fn_ty: {
+ const alignment: u32 = if (align_val.tag() == .null_value) 0 else a: {
+ const alignment = @intCast(u32, align_val.toUnsignedInt());
+ if (alignment == target_util.defaultFunctionAlignment(target)) {
+ break :a 0;
+ } else {
+ break :a alignment;
+ }
+ };
+
// Hot path for some common function types.
// TODO can we eliminate some of these Type tag values? seems unnecessarily complicated.
if (!is_generic and block.params.items.len == 0 and !var_args and
- align_val.tag() == .null_value and !inferred_error_set)
+ alignment == 0 and !inferred_error_set)
{
if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) {
break :fn_ty Type.initTag(.fn_noreturn_no_args);
@@ -5507,16 +5517,15 @@ fn funcCommon(
const param_types = try sema.arena.alloc(Type, block.params.items.len);
const comptime_params = try sema.arena.alloc(bool, block.params.items.len);
for (block.params.items) |param, i| {
+ const param_src: LazySrcLoc = .{ .node_offset = src_node_offset }; // TODO better src
param_types[i] = param.ty;
- comptime_params[i] = param.is_comptime or param.ty.requiresComptime();
+ comptime_params[i] = param.is_comptime or
+ try sema.typeRequiresComptime(block, param_src, param.ty);
is_generic = is_generic or comptime_params[i] or param.ty.tag() == .generic_poison;
}
- if (align_val.tag() != .null_value) {
- return sema.fail(block, src, "TODO implement support for function prototypes to have alignment specified", .{});
- }
-
- is_generic = is_generic or bare_return_type.requiresComptime();
+ is_generic = is_generic or
+ try sema.typeRequiresComptime(block, ret_ty_src, bare_return_type);
const return_type = if (!inferred_error_set or bare_return_type.tag() == .generic_poison)
bare_return_type
@@ -5537,6 +5546,7 @@ fn funcCommon(
.comptime_params = comptime_params.ptr,
.return_type = return_type,
.cc = cc,
+ .alignment = alignment,
.is_var_args = var_args,
.is_generic = is_generic,
});
@@ -5550,7 +5560,6 @@ fn funcCommon(
lib_name, @errorName(err),
});
};
- const target = mod.getTarget();
if (target_util.is_libc_lib_name(target, lib_name)) {
if (!mod.comp.bin_file.options.link_libc) {
return sema.fail(
@@ -5591,12 +5600,7 @@ fn funcCommon(
}
if (body_inst == 0) {
- const fn_ptr_ty = try Type.ptr(sema.arena, .{
- .pointee_type = fn_ty,
- .@"addrspace" = .generic,
- .mutable = false,
- });
- return sema.addType(fn_ptr_ty);
+ return sema.addType(fn_ty);
}
const is_inline = fn_ty.fnCallingConvention() == .Inline;
@@ -5632,7 +5636,7 @@ fn zirParam(
sema: *Sema,
block: *Block,
inst: Zir.Inst.Index,
- is_comptime: bool,
+ comptime_syntax: bool,
) CompileError!void {
const inst_data = sema.code.instructions.items(.data)[inst].pl_tok;
const src = inst_data.src();
@@ -5669,7 +5673,7 @@ fn zirParam(
// insert an anytype parameter.
try block.params.append(sema.gpa, .{
.ty = Type.initTag(.generic_poison),
- .is_comptime = is_comptime,
+ .is_comptime = comptime_syntax,
});
try sema.inst_map.putNoClobber(sema.gpa, inst, .generic_poison);
return;
@@ -5677,8 +5681,10 @@ fn zirParam(
else => |e| return e,
}
};
+ const is_comptime = comptime_syntax or
+ try sema.typeRequiresComptime(block, src, param_ty);
if (sema.inst_map.get(inst)) |arg| {
- if (is_comptime or param_ty.requiresComptime()) {
+ if (is_comptime) {
// We have a comptime value for this parameter so it should be elided from the
// function type of the function instruction in this block.
const coerced_arg = try sema.coerce(block, param_ty, arg, src);
@@ -5692,7 +5698,7 @@ fn zirParam(
try block.params.append(sema.gpa, .{
.ty = param_ty,
- .is_comptime = is_comptime or param_ty.requiresComptime(),
+ .is_comptime = is_comptime,
});
const result = try sema.addConstant(param_ty, Value.initTag(.generic_poison));
try sema.inst_map.putNoClobber(sema.gpa, inst, result);
@@ -5702,9 +5708,10 @@ fn zirParamAnytype(
sema: *Sema,
block: *Block,
inst: Zir.Inst.Index,
- is_comptime: bool,
+ comptime_syntax: bool,
) CompileError!void {
const inst_data = sema.code.instructions.items(.data)[inst].str_tok;
+ const src = inst_data.src();
const param_name = inst_data.get(sema.code);
// TODO check if param_name shadows a Decl. This only needs to be done if
@@ -5713,7 +5720,7 @@ fn zirParamAnytype(
if (sema.inst_map.get(inst)) |air_ref| {
const param_ty = sema.typeOf(air_ref);
- if (is_comptime or param_ty.requiresComptime()) {
+ if (comptime_syntax or try sema.typeRequiresComptime(block, src, param_ty)) {
// We have a comptime value for this parameter so it should be elided from the
// function type of the function instruction in this block.
return;
@@ -5730,7 +5737,7 @@ fn zirParamAnytype(
try block.params.append(sema.gpa, .{
.ty = Type.initTag(.generic_poison),
- .is_comptime = is_comptime,
+ .is_comptime = comptime_syntax,
});
try sema.inst_map.put(sema.gpa, inst, .generic_poison);
}
@@ -11118,8 +11125,7 @@ fn zirIntToPtr(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
const type_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
const type_res = try sema.resolveType(block, src, extra.lhs);
- if (type_res.zigTypeTag() != .Pointer)
- return sema.fail(block, type_src, "expected pointer, found '{}'", .{type_res});
+ try sema.checkPtrType(block, type_src, type_res);
const ptr_align = type_res.ptrAlignment(sema.mod.getTarget());
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
const dest_ty = try sema.resolveType(block, dest_ty_src, extra.lhs);
const operand = sema.resolveInst(extra.rhs);
const operand_ty = sema.typeOf(operand);
- if (operand_ty.zigTypeTag() != .Pointer) {
- return sema.fail(block, operand_src, "expected pointer, found {s} type '{}'", .{
- @tagName(operand_ty.zigTypeTag()), operand_ty,
- });
- }
- if (dest_ty.zigTypeTag() != .Pointer) {
- return sema.fail(block, dest_ty_src, "expected pointer, found {s} type '{}'", .{
- @tagName(dest_ty.zigTypeTag()), dest_ty,
- });
- }
+ try sema.checkPtrType(block, dest_ty_src, dest_ty);
+ try sema.checkPtrOperand(block, operand_src, operand_ty);
return sema.coerceCompatiblePtrs(block, dest_ty, operand, operand_src);
}
@@ -11264,7 +11262,7 @@ fn zirAlignCast(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
// TODO in addition to pointers, this instruction is supposed to work for
// pointer-like optionals and slices.
- try sema.checkPtrType(block, ptr_src, ptr_ty);
+ try sema.checkPtrOperand(block, ptr_src, ptr_ty);
// TODO compile error if the result pointer is comptime known and would have an
// alignment that disagrees with the Decl's alignment.
@@ -11462,6 +11460,34 @@ fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileEr
}
}
+fn checkPtrOperand(
+ sema: *Sema,
+ block: *Block,
+ ty_src: LazySrcLoc,
+ ty: Type,
+) CompileError!void {
+ switch (ty.zigTypeTag()) {
+ .Pointer => {},
+ .Fn => {
+ const msg = msg: {
+ const msg = try sema.errMsg(
+ block,
+ ty_src,
+ "expected pointer, found {}",
+ .{ty},
+ );
+ errdefer msg.destroy(sema.gpa);
+
+ try sema.errNote(block, ty_src, msg, "use '&' to obtain a function pointer", .{});
+
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(msg);
+ },
+ else => return sema.fail(block, ty_src, "expected pointer, found '{}'", .{ty}),
+ }
+}
+
fn checkPtrType(
sema: *Sema,
block: *Block,
@@ -11470,6 +11496,22 @@ fn checkPtrType(
) CompileError!void {
switch (ty.zigTypeTag()) {
.Pointer => {},
+ .Fn => {
+ const msg = msg: {
+ const msg = try sema.errMsg(
+ block,
+ ty_src,
+ "expected pointer type, found '{}'",
+ .{ty},
+ );
+ errdefer msg.destroy(sema.gpa);
+
+ try sema.errNote(block, ty_src, msg, "use '*const ' to make a function pointer type", .{});
+
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(msg);
+ },
else => return sema.fail(block, ty_src, "expected pointer type, found '{}'", .{ty}),
}
}
@@ -12139,20 +12181,14 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
const dest_ptr = sema.resolveInst(extra.dest);
const dest_ptr_ty = sema.typeOf(dest_ptr);
- if (dest_ptr_ty.zigTypeTag() != .Pointer) {
- return sema.fail(block, dest_src, "expected pointer, found '{}'", .{dest_ptr_ty});
- }
+ try sema.checkPtrOperand(block, dest_src, dest_ptr_ty);
if (dest_ptr_ty.isConstPtr()) {
return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty});
}
const uncasted_src_ptr = sema.resolveInst(extra.source);
const uncasted_src_ptr_ty = sema.typeOf(uncasted_src_ptr);
- if (uncasted_src_ptr_ty.zigTypeTag() != .Pointer) {
- return sema.fail(block, src_src, "expected pointer, found '{}'", .{
- uncasted_src_ptr_ty,
- });
- }
+ try sema.checkPtrOperand(block, src_src, uncasted_src_ptr_ty);
const src_ptr_info = uncasted_src_ptr_ty.ptrInfo().data;
const wanted_src_ptr_ty = try Type.ptr(sema.arena, .{
.pointee_type = dest_ptr_ty.elemType2(),
@@ -12203,9 +12239,7 @@ fn zirMemset(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void
const len_src: LazySrcLoc = .{ .node_offset_builtin_call_arg2 = inst_data.src_node };
const dest_ptr = sema.resolveInst(extra.dest);
const dest_ptr_ty = sema.typeOf(dest_ptr);
- if (dest_ptr_ty.zigTypeTag() != .Pointer) {
- return sema.fail(block, dest_src, "expected pointer, found '{}'", .{dest_ptr_ty});
- }
+ try sema.checkPtrOperand(block, dest_src, dest_ptr_ty);
if (dest_ptr_ty.isConstPtr()) {
return sema.fail(block, dest_src, "cannot store through const pointer '{}'", .{dest_ptr_ty});
}
@@ -12487,7 +12521,7 @@ fn zirPrefetch(
const opts_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = extra.node };
const options_ty = try sema.getBuiltinType(block, opts_src, "PrefetchOptions");
const ptr = sema.resolveInst(extra.lhs);
- try sema.checkPtrType(block, ptr_src, sema.typeOf(ptr));
+ try sema.checkPtrOperand(block, ptr_src, sema.typeOf(ptr));
const options = try sema.coerce(block, options_ty, sema.resolveInst(extra.rhs), opts_src);
const rw = try sema.fieldVal(block, opts_src, options, "rw", opts_src);
@@ -12568,12 +12602,15 @@ fn validateVarType(
.Type,
.Undefined,
.Null,
+ .Fn,
=> break,
.Pointer => {
const elem_ty = ty.childType();
- if (elem_ty.zigTypeTag() == .Opaque) return;
- ty = elem_ty;
+ switch (elem_ty.zigTypeTag()) {
+ .Opaque, .Fn => return,
+ else => ty = elem_ty,
+ }
},
.Opaque => if (is_extern) return else break,
@@ -12586,9 +12623,9 @@ fn validateVarType(
.ErrorUnion => ty = ty.errorUnionPayload(),
- .Fn, .Struct, .Union => {
+ .Struct, .Union => {
const resolved_ty = try sema.resolveTypeFields(block, src, ty);
- if (resolved_ty.requiresComptime()) {
+ if (try sema.typeRequiresComptime(block, src, resolved_ty)) {
break;
} else {
return;
@@ -12596,7 +12633,99 @@ fn validateVarType(
},
} else unreachable; // TODO should not need else unreachable
- return sema.fail(block, src, "variable of type '{}' must be const or comptime", .{var_ty});
+ const msg = msg: {
+ const msg = try sema.errMsg(block, src, "variable of type '{}' must be const or comptime", .{var_ty});
+ errdefer msg.destroy(sema.gpa);
+
+ try sema.explainWhyTypeIsComptime(block, src, msg, src.toSrcLoc(block.src_decl), var_ty);
+
+ break :msg msg;
+ };
+ return sema.failWithOwnedErrorMsg(msg);
+}
+
+fn explainWhyTypeIsComptime(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ msg: *Module.ErrorMsg,
+ src_loc: Module.SrcLoc,
+ ty: Type,
+) CompileError!void {
+ const mod = sema.mod;
+ switch (ty.zigTypeTag()) {
+ .Bool,
+ .Int,
+ .Float,
+ .ErrorSet,
+ .Enum,
+ .Frame,
+ .AnyFrame,
+ .Void,
+ => return,
+
+ .Fn => {
+ try mod.errNoteNonLazy(src_loc, msg, "use '*const {}' for a function pointer type", .{
+ ty,
+ });
+ },
+
+ .Type => {
+ try mod.errNoteNonLazy(src_loc, msg, "types are not available at runtime", .{});
+ },
+
+ .BoundFn,
+ .ComptimeFloat,
+ .ComptimeInt,
+ .EnumLiteral,
+ .NoReturn,
+ .Undefined,
+ .Null,
+ .Opaque,
+ .Optional,
+ => return,
+
+ .Pointer, .Array, .Vector => {
+ try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.elemType());
+ },
+
+ .ErrorUnion => {
+ try sema.explainWhyTypeIsComptime(block, src, msg, src_loc, ty.errorUnionPayload());
+ },
+
+ .Struct => {
+ if (ty.castTag(.@"struct")) |payload| {
+ const struct_obj = payload.data;
+ for (struct_obj.fields.values()) |field, i| {
+ const field_src_loc = struct_obj.fieldSrcLoc(sema.gpa, .{
+ .index = i,
+ .range = .type,
+ });
+ if (try sema.typeRequiresComptime(block, src, field.ty)) {
+ try mod.errNoteNonLazy(field_src_loc, msg, "struct requires comptime because of this field", .{});
+ try sema.explainWhyTypeIsComptime(block, src, msg, field_src_loc, field.ty);
+ }
+ }
+ }
+ // TODO tuples
+ },
+
+ .Union => {
+ if (ty.cast(Type.Payload.Union)) |payload| {
+ const union_obj = payload.data;
+ for (union_obj.fields.values()) |field, i| {
+ const field_src_loc = union_obj.fieldSrcLoc(sema.gpa, .{
+ .index = i,
+ .range = .type,
+ });
+ if (try sema.typeRequiresComptime(block, src, field.ty)) {
+ try mod.errNoteNonLazy(field_src_loc, msg, "union requires comptime because of this field", .{});
+ try sema.explainWhyTypeIsComptime(block, src, msg, field_src_loc, field.ty);
+ }
+ }
+ }
+ },
+ }
}
pub const PanicId = enum {
@@ -13883,6 +14012,10 @@ fn coerce(
{
return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
}
+
+ // This will give an extra hint on top of what the bottom of this func would provide.
+ try sema.checkPtrOperand(block, dest_ty_src, inst_ty);
+ unreachable;
},
.Int, .ComptimeInt => switch (inst_ty.zigTypeTag()) {
.Float, .ComptimeFloat => float: {
@@ -14683,7 +14816,8 @@ const ComptimePtrLoadKit = struct {
/// The Type of the parent Value.
ty: Type,
/// The starting byte offset of `val` from `root_val`.
- byte_offset: usize,
+ /// If the type does not have a well-defined memory layout, this is null.
+ byte_offset: ?usize,
/// Whether the `root_val` could be mutated by further
/// semantic analysis and a copy must be performed.
is_mutable: bool,
@@ -14738,12 +14872,24 @@ fn beginComptimePtrLoad(
});
}
const elem_ty = parent.ty.childType();
- const elem_size = elem_ty.abiSize(target);
+ const byte_offset: ?usize = bo: {
+ if (try sema.typeRequiresComptime(block, src, elem_ty)) {
+ break :bo null;
+ } else {
+ if (parent.byte_offset) |off| {
+ try sema.resolveTypeLayout(block, src, elem_ty);
+ const elem_size = elem_ty.abiSize(target);
+ break :bo try sema.usizeCast(block, src, off + elem_size * elem_ptr.index);
+ } else {
+ break :bo null;
+ }
+ }
+ };
return ComptimePtrLoadKit{
.root_val = parent.root_val,
.val = try parent.val.elemValue(sema.arena, elem_ptr.index),
.ty = elem_ty,
- .byte_offset = try sema.usizeCast(block, src, parent.byte_offset + elem_size * elem_ptr.index),
+ .byte_offset = byte_offset,
.is_mutable = parent.is_mutable,
};
},
@@ -14768,13 +14914,24 @@ fn beginComptimePtrLoad(
const field_ptr = ptr_val.castTag(.field_ptr).?.data;
const parent = try beginComptimePtrLoad(sema, block, src, field_ptr.container_ptr);
const field_index = @intCast(u32, field_ptr.field_index);
- try sema.resolveTypeLayout(block, src, parent.ty);
- const field_offset = parent.ty.structFieldOffset(field_index, target);
+ const byte_offset: ?usize = bo: {
+ if (try sema.typeRequiresComptime(block, src, parent.ty)) {
+ break :bo null;
+ } else {
+ if (parent.byte_offset) |off| {
+ try sema.resolveTypeLayout(block, src, parent.ty);
+ const field_offset = parent.ty.structFieldOffset(field_index, target);
+ break :bo try sema.usizeCast(block, src, off + field_offset);
+ } else {
+ break :bo null;
+ }
+ }
+ };
return ComptimePtrLoadKit{
.root_val = parent.root_val,
.val = try parent.val.fieldValue(sema.arena, field_index),
.ty = parent.ty.structFieldType(field_index),
- .byte_offset = try sema.usizeCast(block, src, parent.byte_offset + field_offset),
+ .byte_offset = byte_offset,
.is_mutable = parent.is_mutable,
};
},
@@ -14785,7 +14942,7 @@ fn beginComptimePtrLoad(
.root_val = parent.root_val,
.val = parent.val.castTag(.eu_payload).?.data,
.ty = parent.ty.errorUnionPayload(),
- .byte_offset = undefined,
+ .byte_offset = null,
.is_mutable = parent.is_mutable,
};
},
@@ -14796,7 +14953,7 @@ fn beginComptimePtrLoad(
.root_val = parent.root_val,
.val = parent.val.castTag(.opt_payload).?.data,
.ty = try parent.ty.optionalChildAlloc(sema.arena),
- .byte_offset = undefined,
+ .byte_offset = null,
.is_mutable = parent.is_mutable,
};
},
@@ -16090,28 +16247,12 @@ fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) Comp
switch (ty.tag()) {
.@"struct" => {
const struct_obj = ty.castTag(.@"struct").?.data;
- switch (struct_obj.status) {
- .none => {},
- .field_types_wip => {
- return sema.fail(block, src, "struct {} depends on itself", .{ty});
- },
- .have_field_types,
- .have_layout,
- .layout_wip,
- .fully_resolved_wip,
- .fully_resolved,
- => return ty,
- }
-
- struct_obj.status = .field_types_wip;
- try semaStructFields(sema.mod, struct_obj);
-
- if (struct_obj.fields.count() == 0) {
- struct_obj.status = .have_layout;
- } else {
- struct_obj.status = .have_field_types;
- }
-
+ try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj);
+ return ty;
+ },
+ .@"union", .union_tagged => {
+ const union_obj = ty.cast(Type.Payload.Union).?.data;
+ try sema.resolveTypeFieldsUnion(block, src, ty, union_obj);
return ty;
},
.type_info => return sema.resolveBuiltinTypeFields(block, src, "TypeInfo"),
@@ -16126,29 +16267,63 @@ fn resolveTypeFields(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) Comp
.call_options => return sema.resolveBuiltinTypeFields(block, src, "CallOptions"),
.prefetch_options => return sema.resolveBuiltinTypeFields(block, src, "PrefetchOptions"),
- .@"union", .union_tagged => {
- const union_obj = ty.cast(Type.Payload.Union).?.data;
- switch (union_obj.status) {
- .none => {},
- .field_types_wip => {
- return sema.fail(block, src, "union {} depends on itself", .{ty});
- },
- .have_field_types,
- .have_layout,
- .layout_wip,
- .fully_resolved_wip,
- .fully_resolved,
- => return ty,
- }
+ else => return ty,
+ }
+}
- union_obj.status = .field_types_wip;
- try semaUnionFields(sema.mod, union_obj);
- union_obj.status = .have_field_types;
+fn resolveTypeFieldsStruct(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ ty: Type,
+ struct_obj: *Module.Struct,
+) CompileError!void {
+ switch (struct_obj.status) {
+ .none => {},
+ .field_types_wip => {
+ return sema.fail(block, src, "struct {} depends on itself", .{ty});
+ },
+ .have_field_types,
+ .have_layout,
+ .layout_wip,
+ .fully_resolved_wip,
+ .fully_resolved,
+ => return,
+ }
- return ty;
+ struct_obj.status = .field_types_wip;
+ try semaStructFields(sema.mod, struct_obj);
+
+ if (struct_obj.fields.count() == 0) {
+ struct_obj.status = .have_layout;
+ } else {
+ struct_obj.status = .have_field_types;
+ }
+}
+
+fn resolveTypeFieldsUnion(
+ sema: *Sema,
+ block: *Block,
+ src: LazySrcLoc,
+ ty: Type,
+ union_obj: *Module.Union,
+) CompileError!void {
+ switch (union_obj.status) {
+ .none => {},
+ .field_types_wip => {
+ return sema.fail(block, src, "union {} depends on itself", .{ty});
},
- else => return ty,
+ .have_field_types,
+ .have_layout,
+ .layout_wip,
+ .fully_resolved_wip,
+ .fully_resolved,
+ => return,
}
+
+ union_obj.status = .field_types_wip;
+ try semaUnionFields(sema.mod, union_obj);
+ union_obj.status = .have_field_types;
}
fn resolveBuiltinTypeFields(
@@ -17295,3 +17470,198 @@ fn typePtrOrOptionalPtrTy(
else => return null,
}
}
+
+/// Anything that reports hasCodeGenBits() false returns false here as well.
+/// `generic_poison` will return false.
+/// This function returns false negatives when structs and unions are having their
+/// field types resolved.
+fn typeRequiresComptime(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileError!bool {
+ return switch (ty.tag()) {
+ .u1,
+ .u8,
+ .i8,
+ .u16,
+ .i16,
+ .u32,
+ .i32,
+ .u64,
+ .i64,
+ .u128,
+ .i128,
+ .usize,
+ .isize,
+ .c_short,
+ .c_ushort,
+ .c_int,
+ .c_uint,
+ .c_long,
+ .c_ulong,
+ .c_longlong,
+ .c_ulonglong,
+ .c_longdouble,
+ .f16,
+ .f32,
+ .f64,
+ .f128,
+ .anyopaque,
+ .bool,
+ .void,
+ .anyerror,
+ .noreturn,
+ .@"anyframe",
+ .@"null",
+ .@"undefined",
+ .atomic_order,
+ .atomic_rmw_op,
+ .calling_convention,
+ .address_space,
+ .float_mode,
+ .reduce_op,
+ .call_options,
+ .prefetch_options,
+ .export_options,
+ .extern_options,
+ .manyptr_u8,
+ .manyptr_const_u8,
+ .manyptr_const_u8_sentinel_0,
+ .const_slice_u8,
+ .const_slice_u8_sentinel_0,
+ .anyerror_void_error_union,
+ .empty_struct_literal,
+ .empty_struct,
+ .error_set,
+ .error_set_single,
+ .error_set_inferred,
+ .error_set_merged,
+ .@"opaque",
+ .generic_poison,
+ .array_u8,
+ .array_u8_sentinel_0,
+ .int_signed,
+ .int_unsigned,
+ .enum_simple,
+ => false,
+
+ .single_const_pointer_to_comptime_int,
+ .type,
+ .comptime_int,
+ .comptime_float,
+ .enum_literal,
+ .type_info,
+ // These are function bodies, not function pointers.
+ .fn_noreturn_no_args,
+ .fn_void_no_args,
+ .fn_naked_noreturn_no_args,
+ .fn_ccc_void_no_args,
+ .function,
+ => true,
+
+ .var_args_param => unreachable,
+ .inferred_alloc_mut => unreachable,
+ .inferred_alloc_const => unreachable,
+ .bound_fn => unreachable,
+
+ .array,
+ .array_sentinel,
+ .vector,
+ => return sema.typeRequiresComptime(block, src, ty.childType()),
+
+ .pointer,
+ .single_const_pointer,
+ .single_mut_pointer,
+ .many_const_pointer,
+ .many_mut_pointer,
+ .c_const_pointer,
+ .c_mut_pointer,
+ .const_slice,
+ .mut_slice,
+ => {
+ const child_ty = ty.childType();
+ if (child_ty.zigTypeTag() == .Fn) {
+ return false;
+ } else {
+ return sema.typeRequiresComptime(block, src, child_ty);
+ }
+ },
+
+ .optional,
+ .optional_single_mut_pointer,
+ .optional_single_const_pointer,
+ => {
+ var buf: Type.Payload.ElemType = undefined;
+ return sema.typeRequiresComptime(block, src, ty.optionalChild(&buf));
+ },
+
+ .tuple => {
+ const tuple = ty.castTag(.tuple).?.data;
+ for (tuple.types) |field_ty| {
+ if (try sema.typeRequiresComptime(block, src, field_ty)) {
+ return true;
+ }
+ }
+ return false;
+ },
+
+ .@"struct" => {
+ const struct_obj = ty.castTag(.@"struct").?.data;
+ switch (struct_obj.requires_comptime) {
+ .no, .wip => return false,
+ .yes => return true,
+ .unknown => {
+ if (struct_obj.status == .field_types_wip)
+ return false;
+
+ try sema.resolveTypeFieldsStruct(block, src, ty, struct_obj);
+
+ struct_obj.requires_comptime = .wip;
+ for (struct_obj.fields.values()) |field| {
+ if (try sema.typeRequiresComptime(block, src, field.ty)) {
+ struct_obj.requires_comptime = .yes;
+ return true;
+ }
+ }
+ struct_obj.requires_comptime = .no;
+ return false;
+ },
+ }
+ },
+
+ .@"union", .union_tagged => {
+ const union_obj = ty.cast(Type.Payload.Union).?.data;
+ switch (union_obj.requires_comptime) {
+ .no, .wip => return false,
+ .yes => return true,
+ .unknown => {
+ if (union_obj.status == .field_types_wip)
+ return false;
+
+ try sema.resolveTypeFieldsUnion(block, src, ty, union_obj);
+
+ union_obj.requires_comptime = .wip;
+ for (union_obj.fields.values()) |field| {
+ if (try sema.typeRequiresComptime(block, src, field.ty)) {
+ union_obj.requires_comptime = .yes;
+ return true;
+ }
+ }
+ union_obj.requires_comptime = .no;
+ return false;
+ },
+ }
+ },
+
+ .error_union => return sema.typeRequiresComptime(block, src, ty.errorUnionPayload()),
+ .anyframe_T => {
+ const child_ty = ty.castTag(.anyframe_T).?.data;
+ return sema.typeRequiresComptime(block, src, child_ty);
+ },
+ .enum_numbered => {
+ const tag_ty = ty.castTag(.enum_numbered).?.data.tag_ty;
+ return sema.typeRequiresComptime(block, src, tag_ty);
+ },
+ .enum_full, .enum_nonexhaustive => {
+ const tag_ty = ty.cast(Type.Payload.EnumFull).?.data.tag_ty;
+ return sema.typeRequiresComptime(block, src, tag_ty);
+ },
+ };
+}
diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig
index 00733dd34b..763f72dc10 100644
--- a/src/codegen/llvm.zig
+++ b/src/codegen/llvm.zig
@@ -725,6 +725,10 @@ pub const DeclGen = struct {
llvm_fn.setFunctionCallConv(toLlvmCallConv(fn_info.cc, target));
}
+ if (fn_info.alignment != 0) {
+ llvm_fn.setAlignment(fn_info.alignment);
+ }
+
// Function attributes that are independent of analysis results of the function body.
dg.addCommonFnAttributes(llvm_fn);
diff --git a/src/target.zig b/src/target.zig
index 8a95e756bf..dc29129e42 100644
--- a/src/target.zig
+++ b/src/target.zig
@@ -637,3 +637,12 @@ pub fn llvmMachineAbi(target: std.Target) ?[:0]const u8 {
else => return null,
}
}
+
+pub fn defaultFunctionAlignment(target: std.Target) u32 {
+ return switch (target.cpu.arch) {
+ .arm, .armeb => 4,
+ .aarch64, .aarch64_32, .aarch64_be => 4,
+ .riscv64 => 2,
+ else => 1,
+ };
+}
diff --git a/src/type.zig b/src/type.zig
index 1db6ceeb41..8f9b9d9164 100644
--- a/src/type.zig
+++ b/src/type.zig
@@ -5,6 +5,7 @@ const Allocator = std.mem.Allocator;
const Target = std.Target;
const Module = @import("Module.zig");
const log = std.log.scoped(.Type);
+const target_util = @import("target.zig");
const file_struct = @This();
@@ -577,21 +578,36 @@ pub const Type = extern union {
}
},
.Fn => {
- if (!a.fnReturnType().eql(b.fnReturnType()))
+ const a_info = a.fnInfo();
+ const b_info = b.fnInfo();
+
+ if (!eql(a_info.return_type, b_info.return_type))
return false;
- if (a.fnCallingConvention() != b.fnCallingConvention())
+
+ if (a_info.cc != b_info.cc)
return false;
- const a_param_len = a.fnParamLen();
- const b_param_len = b.fnParamLen();
- if (a_param_len != b_param_len)
+
+ if (a_info.param_types.len != b_info.param_types.len)
return false;
- var i: usize = 0;
- while (i < a_param_len) : (i += 1) {
- if (!a.fnParamType(i).eql(b.fnParamType(i)))
+
+ for (a_info.param_types) |a_param_ty, i| {
+ const b_param_ty = b_info.param_types[i];
+ if (!eql(a_param_ty, b_param_ty))
+ return false;
+
+ if (a_info.comptime_params[i] != b_info.comptime_params[i])
return false;
}
- if (a.fnIsVarArgs() != b.fnIsVarArgs())
+
+ if (a_info.alignment != b_info.alignment)
+ return false;
+
+ if (a_info.is_var_args != b_info.is_var_args)
return false;
+
+ if (a_info.is_generic != b_info.is_generic)
+ return false;
+
return true;
},
.Optional => {
@@ -686,6 +702,7 @@ pub const Type = extern union {
return false;
},
.Float => return a.tag() == b.tag(),
+
.BoundFn,
.Frame,
=> std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }),
@@ -937,6 +954,7 @@ pub const Type = extern union {
.return_type = try payload.return_type.copy(allocator),
.param_types = param_types,
.cc = payload.cc,
+ .alignment = payload.alignment,
.is_var_args = payload.is_var_args,
.is_generic = payload.is_generic,
.comptime_params = comptime_params.ptr,
@@ -1114,9 +1132,15 @@ pub const Type = extern union {
}
try writer.writeAll("...");
}
- try writer.writeAll(") callconv(.");
- try writer.writeAll(@tagName(payload.cc));
try writer.writeAll(") ");
+ if (payload.cc != .Unspecified) {
+ try writer.writeAll("callconv(.");
+ try writer.writeAll(@tagName(payload.cc));
+ try writer.writeAll(") ");
+ }
+ if (payload.alignment != 0) {
+ try writer.print("align({d}) ", .{payload.alignment});
+ }
ty = payload.return_type;
continue;
},
@@ -1423,170 +1447,6 @@ pub const Type = extern union {
}
}
- /// Anything that reports hasCodeGenBits() false returns false here as well.
- /// `generic_poison` will return false.
- pub fn requiresComptime(ty: Type) bool {
- return switch (ty.tag()) {
- .u1,
- .u8,
- .i8,
- .u16,
- .i16,
- .u32,
- .i32,
- .u64,
- .i64,
- .u128,
- .i128,
- .usize,
- .isize,
- .c_short,
- .c_ushort,
- .c_int,
- .c_uint,
- .c_long,
- .c_ulong,
- .c_longlong,
- .c_ulonglong,
- .c_longdouble,
- .f16,
- .f32,
- .f64,
- .f128,
- .anyopaque,
- .bool,
- .void,
- .anyerror,
- .noreturn,
- .@"anyframe",
- .@"null",
- .@"undefined",
- .atomic_order,
- .atomic_rmw_op,
- .calling_convention,
- .address_space,
- .float_mode,
- .reduce_op,
- .call_options,
- .prefetch_options,
- .export_options,
- .extern_options,
- .manyptr_u8,
- .manyptr_const_u8,
- .manyptr_const_u8_sentinel_0,
- .fn_noreturn_no_args,
- .fn_void_no_args,
- .fn_naked_noreturn_no_args,
- .fn_ccc_void_no_args,
- .const_slice_u8,
- .const_slice_u8_sentinel_0,
- .anyerror_void_error_union,
- .empty_struct_literal,
- .function,
- .empty_struct,
- .error_set,
- .error_set_single,
- .error_set_inferred,
- .error_set_merged,
- .@"opaque",
- .generic_poison,
- .array_u8,
- .array_u8_sentinel_0,
- .int_signed,
- .int_unsigned,
- .enum_simple,
- => false,
-
- .single_const_pointer_to_comptime_int,
- .type,
- .comptime_int,
- .comptime_float,
- .enum_literal,
- .type_info,
- => true,
-
- .var_args_param => unreachable,
- .inferred_alloc_mut => unreachable,
- .inferred_alloc_const => unreachable,
- .bound_fn => unreachable,
-
- .array,
- .array_sentinel,
- .vector,
- .pointer,
- .single_const_pointer,
- .single_mut_pointer,
- .many_const_pointer,
- .many_mut_pointer,
- .c_const_pointer,
- .c_mut_pointer,
- .const_slice,
- .mut_slice,
- => return requiresComptime(childType(ty)),
-
- .optional,
- .optional_single_mut_pointer,
- .optional_single_const_pointer,
- => {
- var buf: Payload.ElemType = undefined;
- return requiresComptime(optionalChild(ty, &buf));
- },
-
- .tuple => {
- const tuple = ty.castTag(.tuple).?.data;
- for (tuple.types) |field_ty| {
- if (requiresComptime(field_ty)) {
- return true;
- }
- }
- return false;
- },
-
- .@"struct" => {
- const struct_obj = ty.castTag(.@"struct").?.data;
- switch (struct_obj.requires_comptime) {
- .no, .wip => return false,
- .yes => return true,
- .unknown => {
- struct_obj.requires_comptime = .wip;
- for (struct_obj.fields.values()) |field| {
- if (requiresComptime(field.ty)) {
- struct_obj.requires_comptime = .yes;
- return true;
- }
- }
- struct_obj.requires_comptime = .no;
- return false;
- },
- }
- },
-
- .@"union", .union_tagged => {
- const union_obj = ty.cast(Payload.Union).?.data;
- switch (union_obj.requires_comptime) {
- .no, .wip => return false,
- .yes => return true,
- .unknown => {
- union_obj.requires_comptime = .wip;
- for (union_obj.fields.values()) |field| {
- if (requiresComptime(field.ty)) {
- union_obj.requires_comptime = .yes;
- return true;
- }
- }
- union_obj.requires_comptime = .no;
- return false;
- },
- }
- },
-
- .error_union => return requiresComptime(errorUnionPayload(ty)),
- .anyframe_T => return ty.castTag(.anyframe_T).?.data.requiresComptime(),
- .enum_numbered => return ty.castTag(.enum_numbered).?.data.tag_ty.requiresComptime(),
- .enum_full, .enum_nonexhaustive => return ty.cast(Payload.EnumFull).?.data.tag_ty.requiresComptime(),
- };
- }
-
pub fn toValue(self: Type, allocator: Allocator) Allocator.Error!Value {
switch (self.tag()) {
.u1 => return Value.initTag(.u1_type),
@@ -1918,12 +1778,13 @@ pub const Type = extern union {
.fn_void_no_args, // represents machine code; not a pointer
.fn_naked_noreturn_no_args, // represents machine code; not a pointer
.fn_ccc_void_no_args, // represents machine code; not a pointer
- .function, // represents machine code; not a pointer
- => return switch (target.cpu.arch) {
- .arm, .armeb => 4,
- .aarch64, .aarch64_32, .aarch64_be => 4,
- .riscv64 => 2,
- else => 1,
+ => return target_util.defaultFunctionAlignment(target),
+
+ // represents machine code; not a pointer
+ .function => {
+ const alignment = self.castTag(.function).?.data.alignment;
+ if (alignment != 0) return alignment;
+ return target_util.defaultFunctionAlignment(target);
},
.i16, .u16 => return 2,
@@ -3424,6 +3285,7 @@ pub const Type = extern union {
.comptime_params = undefined,
.return_type = initTag(.noreturn),
.cc = .Unspecified,
+ .alignment = 0,
.is_var_args = false,
.is_generic = false,
},
@@ -3432,6 +3294,7 @@ pub const Type = extern union {
.comptime_params = undefined,
.return_type = initTag(.void),
.cc = .Unspecified,
+ .alignment = 0,
.is_var_args = false,
.is_generic = false,
},
@@ -3440,6 +3303,7 @@ pub const Type = extern union {
.comptime_params = undefined,
.return_type = initTag(.noreturn),
.cc = .Naked,
+ .alignment = 0,
.is_var_args = false,
.is_generic = false,
},
@@ -3448,6 +3312,7 @@ pub const Type = extern union {
.comptime_params = undefined,
.return_type = initTag(.void),
.cc = .C,
+ .alignment = 0,
.is_var_args = false,
.is_generic = false,
},
@@ -4572,6 +4437,8 @@ pub const Type = extern union {
param_types: []Type,
comptime_params: [*]bool,
return_type: Type,
+ /// If zero use default target function code alignment.
+ alignment: u32,
cc: std.builtin.CallingConvention,
is_var_args: bool,
is_generic: bool,
diff --git a/src/value.zig b/src/value.zig
index c043bc9364..2c177f3e93 100644
--- a/src/value.zig
+++ b/src/value.zig
@@ -1520,6 +1520,11 @@ pub const Value = extern union {
}
return true;
},
+ .function => {
+ const a_payload = a.castTag(.function).?.data;
+ const b_payload = b.castTag(.function).?.data;
+ return a_payload == b_payload;
+ },
else => {},
}
} else if (a_tag == .null_value or b_tag == .null_value) {
@@ -1573,6 +1578,7 @@ pub const Value = extern union {
pub fn hash(val: Value, ty: Type, hasher: *std.hash.Wyhash) void {
const zig_ty_tag = ty.zigTypeTag();
std.hash.autoHash(hasher, zig_ty_tag);
+ if (val.isUndef()) return;
switch (zig_ty_tag) {
.BoundFn => unreachable, // TODO remove this from the language
@@ -1694,7 +1700,8 @@ pub const Value = extern union {
union_obj.val.hash(active_field_ty, hasher);
},
.Fn => {
- @panic("TODO implement hashing function values");
+ const func = val.castTag(.function).?.data;
+ return std.hash.autoHash(hasher, func.owner_decl);
},
.Frame => {
@panic("TODO implement hashing frame values");
@@ -1703,7 +1710,8 @@ pub const Value = extern union {
@panic("TODO implement hashing anyframe values");
},
.EnumLiteral => {
- @panic("TODO implement hashing enum literal values");
+ const bytes = val.castTag(.enum_literal).?.data;
+ hasher.update(bytes);
},
}
}