mirror of
https://codeberg.org/ziglang/zig.git
synced 2026-04-27 19:09:47 +03:00
Merge remote-tracking branch 'origin/master' into llvm15
This commit is contained in:
+1
-1
@@ -670,7 +670,7 @@ set(ZIG_STAGE2_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/target/powerpc.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/target/riscv.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/target/sparc.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/target/systemz.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/target/s390x.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/target/wasm.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/target/x86.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/Thread.zig"
|
||||
|
||||
@@ -238,7 +238,15 @@ pub fn build(b: *Builder) !void {
|
||||
exe_options.addOption([:0]const u8, "version", try b.allocator.dupeZ(u8, version));
|
||||
|
||||
if (enable_llvm) {
|
||||
const cmake_cfg = if (static_llvm) null else findAndParseConfigH(b, config_h_path_option);
|
||||
const cmake_cfg = if (static_llvm) null else blk: {
|
||||
if (findConfigH(b, config_h_path_option)) |config_h_path| {
|
||||
const file_contents = fs.cwd().readFileAlloc(b.allocator, config_h_path, max_config_h_bytes) catch unreachable;
|
||||
break :blk parseConfigH(b, file_contents);
|
||||
} else {
|
||||
std.log.warn("config.h could not be located automatically. Consider providing it explicitly via \"-Dconfig_h\"", .{});
|
||||
break :blk null;
|
||||
}
|
||||
};
|
||||
|
||||
if (is_stage1) {
|
||||
const softfloat = b.addStaticLibrary("softfloat", null);
|
||||
@@ -565,13 +573,17 @@ fn addCmakeCfgOptionsToExe(
|
||||
exe.linkLibCpp();
|
||||
} else {
|
||||
const need_cpp_includes = true;
|
||||
const lib_suffix = switch (cfg.llvm_linkage) {
|
||||
.static => exe.target.staticLibSuffix()[1..],
|
||||
.dynamic => exe.target.dynamicLibSuffix()[1..],
|
||||
};
|
||||
|
||||
// System -lc++ must be used because in this code path we are attempting to link
|
||||
// against system-provided LLVM, Clang, LLD.
|
||||
if (exe.target.getOsTag() == .linux) {
|
||||
// First we try to static link against gcc libstdc++. If that doesn't work,
|
||||
// we fall back to -lc++ and cross our fingers.
|
||||
addCxxKnownPath(b, cfg, exe, "libstdc++.a", "", need_cpp_includes) catch |err| switch (err) {
|
||||
// First we try to link against gcc libstdc++. If that doesn't work, we fall
|
||||
// back to -lc++ and cross our fingers.
|
||||
addCxxKnownPath(b, cfg, exe, b.fmt("libstdc++.{s}", .{lib_suffix}), "", need_cpp_includes) catch |err| switch (err) {
|
||||
error.RequiredLibraryNotFound => {
|
||||
exe.linkSystemLibrary("c++");
|
||||
},
|
||||
@@ -579,11 +591,11 @@ fn addCmakeCfgOptionsToExe(
|
||||
};
|
||||
exe.linkSystemLibrary("unwind");
|
||||
} else if (exe.target.isFreeBSD()) {
|
||||
try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
|
||||
try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{lib_suffix}), null, need_cpp_includes);
|
||||
exe.linkSystemLibrary("pthread");
|
||||
} else if (exe.target.getOsTag() == .openbsd) {
|
||||
try addCxxKnownPath(b, cfg, exe, "libc++.a", null, need_cpp_includes);
|
||||
try addCxxKnownPath(b, cfg, exe, "libc++abi.a", null, need_cpp_includes);
|
||||
try addCxxKnownPath(b, cfg, exe, b.fmt("libc++.{s}", .{lib_suffix}), null, need_cpp_includes);
|
||||
try addCxxKnownPath(b, cfg, exe, b.fmt("libc++abi.{s}", .{lib_suffix}), null, need_cpp_includes);
|
||||
} else if (exe.target.isDarwin()) {
|
||||
exe.linkSystemLibrary("c++");
|
||||
}
|
||||
@@ -689,31 +701,53 @@ const CMakeConfig = struct {
|
||||
|
||||
const max_config_h_bytes = 1 * 1024 * 1024;
|
||||
|
||||
fn findAndParseConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?CMakeConfig {
|
||||
const config_h_text: []const u8 = if (config_h_path_option) |config_h_path| blk: {
|
||||
break :blk fs.cwd().readFileAlloc(b.allocator, config_h_path, max_config_h_bytes) catch unreachable;
|
||||
} else blk: {
|
||||
// TODO this should stop looking for config.h once it detects we hit the
|
||||
// zig source root directory.
|
||||
var check_dir = fs.path.dirname(b.zig_exe).?;
|
||||
while (true) {
|
||||
var dir = fs.cwd().openDir(check_dir, .{}) catch unreachable;
|
||||
defer dir.close();
|
||||
fn findConfigH(b: *Builder, config_h_path_option: ?[]const u8) ?[]const u8 {
|
||||
if (config_h_path_option) |path| {
|
||||
var config_h_or_err = fs.cwd().openFile(path, .{});
|
||||
if (config_h_or_err) |*file| {
|
||||
file.close();
|
||||
return path;
|
||||
} else |_| {
|
||||
std.log.err("Could not open provided config.h: \"{s}\"", .{path});
|
||||
std.os.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
break :blk dir.readFileAlloc(b.allocator, "config.h", max_config_h_bytes) catch |err| switch (err) {
|
||||
error.FileNotFound => {
|
||||
const new_check_dir = fs.path.dirname(check_dir);
|
||||
if (new_check_dir == null or mem.eql(u8, new_check_dir.?, check_dir)) {
|
||||
return null;
|
||||
}
|
||||
check_dir = new_check_dir.?;
|
||||
continue;
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
} else unreachable; // TODO should not need `else unreachable`.
|
||||
};
|
||||
var check_dir = fs.path.dirname(b.zig_exe).?;
|
||||
while (true) {
|
||||
var dir = fs.cwd().openDir(check_dir, .{}) catch unreachable;
|
||||
defer dir.close();
|
||||
|
||||
// Check if config.h is present in dir
|
||||
var config_h_or_err = dir.openFile("config.h", .{});
|
||||
if (config_h_or_err) |*file| {
|
||||
file.close();
|
||||
return fs.path.join(
|
||||
b.allocator,
|
||||
&[_][]const u8{ check_dir, "config.h" },
|
||||
) catch unreachable;
|
||||
} else |e| switch (e) {
|
||||
error.FileNotFound => {},
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
// Check if we reached the source root by looking for .git, and bail if so
|
||||
var git_dir_or_err = dir.openDir(".git", .{});
|
||||
if (git_dir_or_err) |*git_dir| {
|
||||
git_dir.close();
|
||||
return null;
|
||||
} else |_| {}
|
||||
|
||||
// Otherwise, continue search in the parent directory
|
||||
const new_check_dir = fs.path.dirname(check_dir);
|
||||
if (new_check_dir == null or mem.eql(u8, new_check_dir.?, check_dir)) {
|
||||
return null;
|
||||
}
|
||||
check_dir = new_check_dir.?;
|
||||
} else unreachable; // TODO should not need `else unreachable`.
|
||||
}
|
||||
|
||||
fn parseConfigH(b: *Builder, config_h_text: []const u8) ?CMakeConfig {
|
||||
var ctx: CMakeConfig = .{
|
||||
.llvm_linkage = undefined,
|
||||
.cmake_binary_dir = undefined,
|
||||
|
||||
@@ -63,8 +63,7 @@ stage3/bin/zig build test-translate-c -fqemu -fwasmtime -Denable-llvm
|
||||
stage3/bin/zig build test-run-translated-c -fqemu -fwasmtime -Denable-llvm
|
||||
stage3/bin/zig build test-standalone -fqemu -fwasmtime -Denable-llvm
|
||||
stage3/bin/zig build test-cli -fqemu -fwasmtime -Denable-llvm
|
||||
# https://github.com/ziglang/zig/issues/12144
|
||||
stage3/bin/zig build test-cases -fqemu -fwasmtime
|
||||
stage3/bin/zig build test-cases -fqemu -fwasmtime -Dstatic-llvm -Dtarget=native-native-musl --search-prefix "$DEPS_LOCAL"
|
||||
stage3/bin/zig build test-link -fqemu -fwasmtime -Denable-llvm
|
||||
|
||||
$STAGE1_ZIG build test-stack-traces -fqemu -fwasmtime
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
|
||||
if(ZIG_USE_LLVM_CONFIG)
|
||||
set(LLVM_CONFIG_ERROR_MESSAGES "")
|
||||
while(1)
|
||||
unset(LLVM_CONFIG_EXE CACHE)
|
||||
find_program(LLVM_CONFIG_EXE
|
||||
@@ -21,7 +22,8 @@ if(ZIG_USE_LLVM_CONFIG)
|
||||
"C:/Libraries/llvm-15.0.0/bin")
|
||||
|
||||
if ("${LLVM_CONFIG_EXE}" STREQUAL "LLVM_CONFIG_EXE-NOTFOUND")
|
||||
if (DEFINED LLVM_CONFIG_ERROR_MESSAGE)
|
||||
if (NOT LLVM_CONFIG_ERROR_MESSAGES STREQUAL "")
|
||||
list(JOIN LLVM_CONFIG_ERROR_MESSAGES "\n" LLVM_CONFIG_ERROR_MESSAGE)
|
||||
message(FATAL_ERROR ${LLVM_CONFIG_ERROR_MESSAGE})
|
||||
else()
|
||||
message(FATAL_ERROR "unable to find llvm-config")
|
||||
@@ -37,7 +39,7 @@ if(ZIG_USE_LLVM_CONFIG)
|
||||
get_filename_component(LLVM_CONFIG_DIR "${LLVM_CONFIG_EXE}" DIRECTORY)
|
||||
if("${LLVM_CONFIG_VERSION}" VERSION_LESS 15 OR "${LLVM_CONFIG_VERSION}" VERSION_EQUAL 16 OR "${LLVM_CONFIG_VERSION}" VERSION_GREATER 16)
|
||||
# Save the error message, in case this is the last llvm-config we find
|
||||
set(LLVM_CONFIG_ERROR_MESSAGE "expected LLVM 15.x but found ${LLVM_CONFIG_VERSION} using ${LLVM_CONFIG_EXE}")
|
||||
list(APPEND LLVM_CONFIG_ERROR_MESSAGES "expected LLVM 15.x but found ${LLVM_CONFIG_VERSION} using ${LLVM_CONFIG_EXE}")
|
||||
|
||||
# Ignore this directory and try the search again
|
||||
list(APPEND CMAKE_IGNORE_PATH "${LLVM_CONFIG_DIR}")
|
||||
@@ -61,9 +63,9 @@ if(ZIG_USE_LLVM_CONFIG)
|
||||
if (LLVM_CONFIG_ERROR)
|
||||
# Save the error message, in case this is the last llvm-config we find
|
||||
if (ZIG_SHARED_LLVM)
|
||||
set(LLVM_CONFIG_ERROR_MESSAGE "LLVM 15.x found at ${LLVM_CONFIG_EXE} does not support linking as a shared library")
|
||||
list(APPEND LLVM_CONFIG_ERROR_MESSAGES "LLVM 15.x found at ${LLVM_CONFIG_EXE} does not support linking as a shared library")
|
||||
else()
|
||||
set(LLVM_CONFIG_ERROR_MESSAGE "LLVM 15.x found at ${LLVM_CONFIG_EXE} does not support linking as a static library")
|
||||
list(APPEND LLVM_CONFIG_ERROR_MESSAGES "LLVM 15.x found at ${LLVM_CONFIG_EXE} does not support linking as a static library")
|
||||
endif()
|
||||
|
||||
# Ignore this directory and try the search again
|
||||
@@ -81,7 +83,7 @@ if(ZIG_USE_LLVM_CONFIG)
|
||||
list (FIND LLVM_TARGETS_BUILT "${TARGET_NAME}" _index)
|
||||
if (${_index} EQUAL -1)
|
||||
# Save the error message, in case this is the last llvm-config we find
|
||||
set(LLVM_CONFIG_ERROR_MESSAGE "LLVM (according to ${LLVM_CONFIG_EXE}) is missing target ${TARGET_NAME}. Zig requires LLVM to be built with all default targets enabled.")
|
||||
list(APPEND LLVM_CONFIG_ERROR_MESSAGES "LLVM (according to ${LLVM_CONFIG_EXE}) is missing target ${TARGET_NAME}. Zig requires LLVM to be built with all default targets enabled.")
|
||||
|
||||
# Ignore this directory and try the search again
|
||||
list(APPEND CMAKE_IGNORE_PATH "${LLVM_CONFIG_DIR}")
|
||||
|
||||
@@ -1784,6 +1784,7 @@ pub fn updateSegfaultHandler(act: ?*const os.Sigaction) error{OperationNotSuppor
|
||||
try os.sigaction(os.SIG.SEGV, act, null);
|
||||
try os.sigaction(os.SIG.ILL, act, null);
|
||||
try os.sigaction(os.SIG.BUS, act, null);
|
||||
try os.sigaction(os.SIG.FPE, act, null);
|
||||
}
|
||||
|
||||
/// Attaches a global SIGSEGV handler which calls @panic("segmentation fault");
|
||||
@@ -1845,6 +1846,7 @@ fn handleSegfaultPosix(sig: i32, info: *const os.siginfo_t, ctx_ptr: ?*const any
|
||||
os.SIG.SEGV => stderr.print("Segmentation fault at address 0x{x}\n", .{addr}),
|
||||
os.SIG.ILL => stderr.print("Illegal instruction at address 0x{x}\n", .{addr}),
|
||||
os.SIG.BUS => stderr.print("Bus error at address 0x{x}\n", .{addr}),
|
||||
os.SIG.FPE => stderr.print("Arithmetic exception at address 0x{x}\n", .{addr}),
|
||||
else => unreachable,
|
||||
} catch os.abort();
|
||||
}
|
||||
|
||||
@@ -4244,6 +4244,7 @@ pub const INotifyAddWatchError = error{
|
||||
SystemResources,
|
||||
UserResourceLimitReached,
|
||||
NotDir,
|
||||
WatchAlreadyExists,
|
||||
} || UnexpectedError;
|
||||
|
||||
/// add a watch to an initialized inotify instance
|
||||
@@ -4266,6 +4267,7 @@ pub fn inotify_add_watchZ(inotify_fd: i32, pathname: [*:0]const u8, mask: u32) I
|
||||
.NOMEM => return error.SystemResources,
|
||||
.NOSPC => return error.UserResourceLimitReached,
|
||||
.NOTDIR => return error.NotDir,
|
||||
.EXIST => return error.WatchAlreadyExists,
|
||||
else => |err| return unexpectedErrno(err),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2976,6 +2976,7 @@ pub const IN = struct {
|
||||
pub const ONLYDIR = 0x01000000;
|
||||
pub const DONT_FOLLOW = 0x02000000;
|
||||
pub const EXCL_UNLINK = 0x04000000;
|
||||
pub const MASK_CREATE = 0x10000000;
|
||||
pub const MASK_ADD = 0x20000000;
|
||||
|
||||
pub const ISDIR = 0x40000000;
|
||||
|
||||
+5
-5
@@ -453,7 +453,7 @@ pub const Target = struct {
|
||||
pub const riscv = @import("target/riscv.zig");
|
||||
pub const sparc = @import("target/sparc.zig");
|
||||
pub const spirv = @import("target/spirv.zig");
|
||||
pub const systemz = @import("target/systemz.zig");
|
||||
pub const s390x = @import("target/s390x.zig");
|
||||
pub const ve = @import("target/ve.zig");
|
||||
pub const wasm = @import("target/wasm.zig");
|
||||
pub const x86 = @import("target/x86.zig");
|
||||
@@ -1178,7 +1178,7 @@ pub const Target = struct {
|
||||
.amdgcn => "amdgpu",
|
||||
.riscv32, .riscv64 => "riscv",
|
||||
.sparc, .sparc64, .sparcel => "sparc",
|
||||
.s390x => "systemz",
|
||||
.s390x => "s390x",
|
||||
.i386, .x86_64 => "x86",
|
||||
.nvptx, .nvptx64 => "nvptx",
|
||||
.wasm32, .wasm64 => "wasm",
|
||||
@@ -1202,7 +1202,7 @@ pub const Target = struct {
|
||||
.riscv32, .riscv64 => &riscv.all_features,
|
||||
.sparc, .sparc64, .sparcel => &sparc.all_features,
|
||||
.spirv32, .spirv64 => &spirv.all_features,
|
||||
.s390x => &systemz.all_features,
|
||||
.s390x => &s390x.all_features,
|
||||
.i386, .x86_64 => &x86.all_features,
|
||||
.nvptx, .nvptx64 => &nvptx.all_features,
|
||||
.ve => &ve.all_features,
|
||||
@@ -1226,7 +1226,7 @@ pub const Target = struct {
|
||||
.amdgcn => comptime allCpusFromDecls(amdgpu.cpu),
|
||||
.riscv32, .riscv64 => comptime allCpusFromDecls(riscv.cpu),
|
||||
.sparc, .sparc64, .sparcel => comptime allCpusFromDecls(sparc.cpu),
|
||||
.s390x => comptime allCpusFromDecls(systemz.cpu),
|
||||
.s390x => comptime allCpusFromDecls(s390x.cpu),
|
||||
.i386, .x86_64 => comptime allCpusFromDecls(x86.cpu),
|
||||
.nvptx, .nvptx64 => comptime allCpusFromDecls(nvptx.cpu),
|
||||
.ve => comptime allCpusFromDecls(ve.cpu),
|
||||
@@ -1287,7 +1287,7 @@ pub const Target = struct {
|
||||
.riscv64 => &riscv.cpu.generic_rv64,
|
||||
.sparc, .sparcel => &sparc.cpu.generic,
|
||||
.sparc64 => &sparc.cpu.v9, // 64-bit SPARC needs v9 as the baseline
|
||||
.s390x => &systemz.cpu.generic,
|
||||
.s390x => &s390x.cpu.generic,
|
||||
.i386 => &x86.cpu.i386,
|
||||
.x86_64 => &x86.cpu.x86_64,
|
||||
.nvptx, .nvptx64 => &nvptx.cpu.sm_20,
|
||||
|
||||
@@ -3257,7 +3257,7 @@ const Parser = struct {
|
||||
if (p.eatToken(.ellipsis2)) |_| {
|
||||
const end_expr = try p.parseExpr();
|
||||
if (p.eatToken(.colon)) |_| {
|
||||
const sentinel = try p.parseExpr();
|
||||
const sentinel = try p.expectExpr();
|
||||
_ = try p.expectToken(.r_bracket);
|
||||
return p.addNode(.{
|
||||
.tag = .slice_sentinel,
|
||||
|
||||
@@ -5118,6 +5118,14 @@ test "zig fmt: while continue expr" {
|
||||
});
|
||||
}
|
||||
|
||||
test "zig fmt: error for missing sentinel value in sentinel slice" {
|
||||
try testError(
|
||||
\\const foo = foo[0..:];
|
||||
, &[_]Error{
|
||||
.expected_expr,
|
||||
});
|
||||
}
|
||||
|
||||
test "zig fmt: error for invalid bit range" {
|
||||
try testError(
|
||||
\\var x: []align(0:0:0)u8 = bar;
|
||||
|
||||
+3
-2
@@ -111,8 +111,9 @@ pub const Inst = struct {
|
||||
div_floor,
|
||||
/// Same as `div_floor` with optimized float mode.
|
||||
div_floor_optimized,
|
||||
/// Integer or float division. Guaranteed no remainder.
|
||||
/// For integers, wrapping is undefined behavior.
|
||||
/// Integer or float division.
|
||||
/// If a remainder would be produced, undefined behavior occurs.
|
||||
/// For integers, overflow is undefined behavior.
|
||||
/// Both operands are guaranteed to be the same type, and the result type
|
||||
/// is the same as both operands.
|
||||
/// Uses the `bin_op` field.
|
||||
|
||||
+8
-1
@@ -1349,7 +1349,10 @@ fn arrayInitExpr(
|
||||
}
|
||||
}
|
||||
const array_type_inst = try typeExpr(gz, scope, array_init.ast.type_expr);
|
||||
_ = try gz.addUnNode(.validate_array_init_ty, array_type_inst, array_init.ast.type_expr);
|
||||
_ = try gz.addPlNode(.validate_array_init_ty, node, Zir.Inst.ArrayInit{
|
||||
.ty = array_type_inst,
|
||||
.init_count = @intCast(u32, array_init.ast.elements.len),
|
||||
});
|
||||
break :inst .{
|
||||
.array = array_type_inst,
|
||||
.elem = .none,
|
||||
@@ -1940,6 +1943,9 @@ fn continueExpr(parent_gz: *GenZir, parent_scope: *Scope, node: Ast.Node.Index)
|
||||
.break_inline
|
||||
else
|
||||
.@"break";
|
||||
if (break_tag == .break_inline) {
|
||||
_ = try parent_gz.addNode(.check_comptime_control_flow, node);
|
||||
}
|
||||
_ = try parent_gz.addBreak(break_tag, continue_block, .void_value);
|
||||
return Zir.Inst.Ref.unreachable_value;
|
||||
},
|
||||
@@ -2473,6 +2479,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: Ast.Node.Index) Inner
|
||||
.repeat_inline,
|
||||
.panic,
|
||||
.panic_comptime,
|
||||
.check_comptime_control_flow,
|
||||
=> {
|
||||
noreturn_src_node = statement;
|
||||
break :b true;
|
||||
|
||||
@@ -1494,31 +1494,14 @@ pub fn create(gpa: Allocator, options: InitOptions) !*Compilation {
|
||||
);
|
||||
errdefer test_pkg.destroy(gpa);
|
||||
|
||||
try test_pkg.add(gpa, "builtin", builtin_pkg);
|
||||
try test_pkg.add(gpa, "root", test_pkg);
|
||||
try test_pkg.add(gpa, "std", std_pkg);
|
||||
|
||||
break :root_pkg test_pkg;
|
||||
} else main_pkg;
|
||||
errdefer if (options.is_test) root_pkg.destroy(gpa);
|
||||
|
||||
var other_pkg_iter = main_pkg.table.valueIterator();
|
||||
while (other_pkg_iter.next()) |pkg| {
|
||||
try pkg.*.add(gpa, "builtin", builtin_pkg);
|
||||
try pkg.*.add(gpa, "std", std_pkg);
|
||||
}
|
||||
|
||||
try main_pkg.addAndAdopt(gpa, "builtin", builtin_pkg);
|
||||
try main_pkg.add(gpa, "root", root_pkg);
|
||||
try main_pkg.addAndAdopt(gpa, "std", std_pkg);
|
||||
|
||||
try std_pkg.add(gpa, "builtin", builtin_pkg);
|
||||
try std_pkg.add(gpa, "root", root_pkg);
|
||||
try std_pkg.add(gpa, "std", std_pkg);
|
||||
|
||||
try builtin_pkg.add(gpa, "std", std_pkg);
|
||||
try builtin_pkg.add(gpa, "builtin", builtin_pkg);
|
||||
|
||||
const main_pkg_in_std = m: {
|
||||
const std_path = try std.fs.path.resolve(arena, &[_][]const u8{
|
||||
std_pkg.root_src_directory.path orelse ".",
|
||||
|
||||
@@ -2283,6 +2283,8 @@ pub const SrcLoc = struct {
|
||||
.@"while" => tree.whileFull(node).ast.cond_expr,
|
||||
.for_simple => tree.forSimple(node).ast.cond_expr,
|
||||
.@"for" => tree.forFull(node).ast.cond_expr,
|
||||
.@"orelse" => node,
|
||||
.@"catch" => node,
|
||||
else => unreachable,
|
||||
};
|
||||
return nodeToSpan(tree, src_node);
|
||||
@@ -2726,6 +2728,21 @@ pub const SrcLoc = struct {
|
||||
};
|
||||
return nodeToSpan(tree, full.ast.value_expr);
|
||||
},
|
||||
.node_offset_init_ty => |node_off| {
|
||||
const tree = try src_loc.file_scope.getTree(gpa);
|
||||
const node_tags = tree.nodes.items(.tag);
|
||||
const parent_node = src_loc.declRelativeToNodeIndex(node_off);
|
||||
|
||||
var buf: [2]Ast.Node.Index = undefined;
|
||||
const full: Ast.full.ArrayInit = switch (node_tags[parent_node]) {
|
||||
.array_init_one, .array_init_one_comma => tree.arrayInitOne(buf[0..1], parent_node),
|
||||
.array_init_dot_two, .array_init_dot_two_comma => tree.arrayInitDotTwo(&buf, parent_node),
|
||||
.array_init_dot, .array_init_dot_comma => tree.arrayInitDot(parent_node),
|
||||
.array_init, .array_init_comma => tree.arrayInit(parent_node),
|
||||
else => unreachable,
|
||||
};
|
||||
return nodeToSpan(tree, full.ast.type_expr);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3046,6 +3063,9 @@ pub const LazySrcLoc = union(enum) {
|
||||
/// The source location points to the default value of a field.
|
||||
/// The Decl is determined contextually.
|
||||
node_offset_field_default: i32,
|
||||
/// The source location points to the type of an array or struct initializer.
|
||||
/// The Decl is determined contextually.
|
||||
node_offset_init_ty: i32,
|
||||
|
||||
pub const nodeOffset = if (TracedOffset.want_tracing) nodeOffsetDebug else nodeOffsetRelease;
|
||||
|
||||
@@ -3124,6 +3144,7 @@ pub const LazySrcLoc = union(enum) {
|
||||
.node_offset_ptr_hostsize,
|
||||
.node_offset_container_tag,
|
||||
.node_offset_field_default,
|
||||
.node_offset_init_ty,
|
||||
=> .{
|
||||
.file_scope = decl.getFileScope(),
|
||||
.parent_decl_node = decl.src_node,
|
||||
@@ -4673,6 +4694,15 @@ pub fn importFile(
|
||||
cur_file: *File,
|
||||
import_string: []const u8,
|
||||
) !ImportFileResult {
|
||||
if (std.mem.eql(u8, import_string, "std")) {
|
||||
return mod.importPkg(mod.main_pkg.table.get("std").?);
|
||||
}
|
||||
if (std.mem.eql(u8, import_string, "builtin")) {
|
||||
return mod.importPkg(mod.main_pkg.table.get("builtin").?);
|
||||
}
|
||||
if (std.mem.eql(u8, import_string, "root")) {
|
||||
return mod.importPkg(mod.root_pkg);
|
||||
}
|
||||
if (cur_file.pkg.table.get(import_string)) |pkg| {
|
||||
return mod.importPkg(pkg);
|
||||
}
|
||||
|
||||
+1170
-395
@@ -875,10 +875,6 @@ fn analyzeBodyInner(
|
||||
.add => try sema.zirArithmetic(block, inst, .add),
|
||||
.addwrap => try sema.zirArithmetic(block, inst, .addwrap),
|
||||
.add_sat => try sema.zirArithmetic(block, inst, .add_sat),
|
||||
.div => try sema.zirArithmetic(block, inst, .div),
|
||||
.div_exact => try sema.zirArithmetic(block, inst, .div_exact),
|
||||
.div_floor => try sema.zirArithmetic(block, inst, .div_floor),
|
||||
.div_trunc => try sema.zirArithmetic(block, inst, .div_trunc),
|
||||
.mod_rem => try sema.zirArithmetic(block, inst, .mod_rem),
|
||||
.mod => try sema.zirArithmetic(block, inst, .mod),
|
||||
.rem => try sema.zirArithmetic(block, inst, .rem),
|
||||
@@ -889,6 +885,11 @@ fn analyzeBodyInner(
|
||||
.subwrap => try sema.zirArithmetic(block, inst, .subwrap),
|
||||
.sub_sat => try sema.zirArithmetic(block, inst, .sub_sat),
|
||||
|
||||
.div => try sema.zirDiv(block, inst),
|
||||
.div_exact => try sema.zirDivExact(block, inst),
|
||||
.div_floor => try sema.zirDivFloor(block, inst),
|
||||
.div_trunc => try sema.zirDivTrunc(block, inst),
|
||||
|
||||
.maximum => try sema.zirMinMax(block, inst, .max),
|
||||
.minimum => try sema.zirMinMax(block, inst, .min),
|
||||
|
||||
@@ -1146,6 +1147,24 @@ fn analyzeBodyInner(
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
.check_comptime_control_flow => {
|
||||
if (!block.is_comptime) {
|
||||
if (block.runtime_cond orelse block.runtime_loop) |runtime_src| {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].node;
|
||||
const src = LazySrcLoc.nodeOffset(inst_data);
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(block, src, "comptime control flow inside runtime block", .{});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
|
||||
try sema.errNote(block, runtime_src, msg, "runtime control flow here", .{});
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
continue;
|
||||
},
|
||||
|
||||
// Special case instructions to handle comptime control flow.
|
||||
.@"break" => {
|
||||
@@ -3475,19 +3494,43 @@ fn validateArrayInitTy(
|
||||
block: *Block,
|
||||
inst: Zir.Inst.Index,
|
||||
) CompileError!void {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].un_node;
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src = inst_data.src();
|
||||
const ty = try sema.resolveType(block, src, inst_data.operand);
|
||||
const ty_src: LazySrcLoc = .{ .node_offset_init_ty = inst_data.src_node };
|
||||
const extra = sema.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data;
|
||||
const ty = try sema.resolveType(block, ty_src, extra.ty);
|
||||
|
||||
switch (ty.zigTypeTag()) {
|
||||
.Array, .Vector => return,
|
||||
.Array => {
|
||||
const array_len = ty.arrayLen();
|
||||
if (extra.init_count != array_len) {
|
||||
return sema.fail(block, src, "expected {d} array elements; found {d}", .{
|
||||
array_len, extra.init_count,
|
||||
});
|
||||
}
|
||||
return;
|
||||
},
|
||||
.Vector => {
|
||||
const array_len = ty.arrayLen();
|
||||
if (extra.init_count != array_len) {
|
||||
return sema.fail(block, src, "expected {d} vector elements; found {d}", .{
|
||||
array_len, extra.init_count,
|
||||
});
|
||||
}
|
||||
return;
|
||||
},
|
||||
.Struct => if (ty.isTuple()) {
|
||||
// TODO validate element count
|
||||
const array_len = ty.arrayLen();
|
||||
if (extra.init_count > array_len) {
|
||||
return sema.fail(block, src, "expected at most {d} tuple fields; found {d}", .{
|
||||
array_len, extra.init_count,
|
||||
});
|
||||
}
|
||||
return;
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
return sema.failWithArrayInitNotSupported(block, src, ty);
|
||||
return sema.failWithArrayInitNotSupported(block, ty_src, ty);
|
||||
}
|
||||
|
||||
fn validateStructInitTy(
|
||||
@@ -3723,6 +3766,15 @@ fn validateStructInit(
|
||||
|
||||
const default_val = struct_ty.structFieldDefaultValue(i);
|
||||
if (default_val.tag() == .unreachable_value) {
|
||||
if (struct_ty.isTuple()) {
|
||||
const template = "missing tuple field with index {d}";
|
||||
if (root_msg) |msg| {
|
||||
try sema.errNote(block, init_src, msg, template, .{i});
|
||||
} else {
|
||||
root_msg = try sema.errMsg(block, init_src, template, .{i});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const field_name = struct_ty.structFieldName(i);
|
||||
const template = "missing struct field: {s}";
|
||||
const args = .{field_name};
|
||||
@@ -3735,7 +3787,10 @@ fn validateStructInit(
|
||||
}
|
||||
|
||||
const field_src = init_src; // TODO better source location
|
||||
const default_field_ptr = try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(u32, i), field_src, struct_ty, true);
|
||||
const default_field_ptr = if (struct_ty.isTuple())
|
||||
try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(u32, i), true)
|
||||
else
|
||||
try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(u32, i), field_src, struct_ty, true);
|
||||
const field_ty = sema.typeOf(default_field_ptr).childType();
|
||||
const init = try sema.addConstant(field_ty, default_val);
|
||||
try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store);
|
||||
@@ -3850,6 +3905,15 @@ fn validateStructInit(
|
||||
|
||||
const default_val = struct_ty.structFieldDefaultValue(i);
|
||||
if (default_val.tag() == .unreachable_value) {
|
||||
if (struct_ty.isTuple()) {
|
||||
const template = "missing tuple field with index {d}";
|
||||
if (root_msg) |msg| {
|
||||
try sema.errNote(block, init_src, msg, template, .{i});
|
||||
} else {
|
||||
root_msg = try sema.errMsg(block, init_src, template, .{i});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const field_name = struct_ty.structFieldName(i);
|
||||
const template = "missing struct field: {s}";
|
||||
const args = .{field_name};
|
||||
@@ -3893,7 +3957,10 @@ fn validateStructInit(
|
||||
if (field_ptr != 0) continue;
|
||||
|
||||
const field_src = init_src; // TODO better source location
|
||||
const default_field_ptr = try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(u32, i), field_src, struct_ty, true);
|
||||
const default_field_ptr = if (struct_ty.isTuple())
|
||||
try sema.tupleFieldPtr(block, init_src, struct_ptr, field_src, @intCast(u32, i), true)
|
||||
else
|
||||
try sema.structFieldPtrByIndex(block, init_src, struct_ptr, @intCast(u32, i), field_src, struct_ty, true);
|
||||
const field_ty = sema.typeOf(default_field_ptr).childType();
|
||||
const init = try sema.addConstant(field_ty, field_values[i]);
|
||||
try sema.storePtr2(block, init_src, default_field_ptr, init_src, init, field_src, .store);
|
||||
@@ -3916,15 +3983,24 @@ fn zirValidateArrayInit(
|
||||
const array_ty = sema.typeOf(array_ptr).childType();
|
||||
const array_len = array_ty.arrayLen();
|
||||
|
||||
if (instrs.len != array_len) {
|
||||
if (array_ty.zigTypeTag() == .Array) {
|
||||
return sema.fail(block, init_src, "expected {d} array elements; found {d}", .{
|
||||
array_len, instrs.len,
|
||||
});
|
||||
} else {
|
||||
return sema.fail(block, init_src, "expected {d} vector elements; found {d}", .{
|
||||
array_len, instrs.len,
|
||||
});
|
||||
if (instrs.len != array_len and array_ty.isTuple()) {
|
||||
const struct_obj = array_ty.castTag(.tuple).?.data;
|
||||
var root_msg: ?*Module.ErrorMsg = null;
|
||||
for (struct_obj.values) |default_val, i| {
|
||||
if (i < instrs.len) continue;
|
||||
|
||||
if (default_val.tag() == .unreachable_value) {
|
||||
const template = "missing tuple field with index {d}";
|
||||
if (root_msg) |msg| {
|
||||
try sema.errNote(block, init_src, msg, template, .{i});
|
||||
} else {
|
||||
root_msg = try sema.errMsg(block, init_src, template, .{i});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (root_msg) |msg| {
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3977,10 +4053,17 @@ fn zirValidateArrayInit(
|
||||
}
|
||||
first_block_index = @minimum(first_block_index, block_index);
|
||||
|
||||
// Array has one possible value, so value is always comptime-known
|
||||
if (opt_opv) |opv| {
|
||||
element_vals[i] = opv;
|
||||
continue;
|
||||
if (array_ty.isTuple()) {
|
||||
if (array_ty.structFieldValueComptime(i)) |opv| {
|
||||
element_vals[i] = opv;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
// Array has one possible value, so value is always comptime-known
|
||||
if (opt_opv) |opv| {
|
||||
element_vals[i] = opv;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// If the next instructon is a store with a comptime operand, this element
|
||||
@@ -4674,10 +4757,6 @@ fn zirCImport(sema: *Sema, parent_block: *Block, inst: Zir.Inst.Index) CompileEr
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
else => unreachable, // we pass null for root_src_dir_path
|
||||
};
|
||||
const std_pkg = mod.main_pkg.table.get("std").?;
|
||||
const builtin_pkg = mod.main_pkg.table.get("builtin").?;
|
||||
try c_import_pkg.add(sema.gpa, "builtin", builtin_pkg);
|
||||
try c_import_pkg.add(sema.gpa, "std", std_pkg);
|
||||
|
||||
const result = mod.importPkg(c_import_pkg) catch |err|
|
||||
return sema.fail(&child_block, src, "C import failed: {s}", .{@errorName(err)});
|
||||
@@ -10842,6 +10921,636 @@ fn zirArithmetic(
|
||||
return sema.analyzeArithmetic(block, zir_tag, lhs, rhs, sema.src, lhs_src, rhs_src);
|
||||
}
|
||||
|
||||
fn zirDiv(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
|
||||
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
|
||||
const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
|
||||
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
|
||||
const lhs = try sema.resolveInst(extra.lhs);
|
||||
const rhs = try sema.resolveInst(extra.rhs);
|
||||
const lhs_ty = sema.typeOf(lhs);
|
||||
const rhs_ty = sema.typeOf(rhs);
|
||||
const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
|
||||
const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
|
||||
try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
|
||||
try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .div);
|
||||
|
||||
const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
|
||||
const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
|
||||
.override = &[_]LazySrcLoc{ lhs_src, rhs_src },
|
||||
});
|
||||
|
||||
const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
|
||||
const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
|
||||
|
||||
const lhs_scalar_ty = lhs_ty.scalarType();
|
||||
const rhs_scalar_ty = rhs_ty.scalarType();
|
||||
const scalar_tag = resolved_type.scalarType().zigTypeTag();
|
||||
|
||||
const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
|
||||
|
||||
try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div);
|
||||
|
||||
const mod = sema.mod;
|
||||
const target = mod.getTarget();
|
||||
const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs);
|
||||
const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs);
|
||||
|
||||
// TODO: emit compile error when .div is used on integers and there would be an
|
||||
// ambiguous result between div_floor and div_trunc.
|
||||
|
||||
// For integers:
|
||||
// If the lhs is zero, then zero is returned regardless of rhs.
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined:
|
||||
// * if lhs type is signed:
|
||||
// * if rhs is comptime-known and not -1, result is undefined
|
||||
// * if rhs is -1 or runtime-known, compile error because there is a
|
||||
// possible value (-min_int / -1) for which division would be
|
||||
// illegal behavior.
|
||||
// * if lhs type is unsigned, undef is returned regardless of rhs.
|
||||
//
|
||||
// For floats:
|
||||
// If the rhs is zero:
|
||||
// * comptime_float: compile error for division by zero.
|
||||
// * other float type:
|
||||
// * if the lhs is zero: QNaN
|
||||
// * otherwise: +Inf or -Inf depending on lhs sign
|
||||
// If the rhs is undefined:
|
||||
// * comptime_float: compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// * other float type: result is undefined
|
||||
// If the lhs is undefined, result is undefined.
|
||||
switch (scalar_tag) {
|
||||
.Int, .ComptimeInt, .ComptimeFloat => {
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (!lhs_val.isUndef()) {
|
||||
if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
|
||||
return sema.addConstant(resolved_type, Value.zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (rhs_val.isUndef()) {
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
|
||||
return sema.failWithDivideByZero(block, rhs_src);
|
||||
}
|
||||
// TODO: if the RHS is one, return the LHS directly
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
const runtime_src = rs: {
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (lhs_val.isUndef()) {
|
||||
if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) {
|
||||
return sema.addConstUndef(resolved_type);
|
||||
}
|
||||
}
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
return sema.addConstUndef(resolved_type);
|
||||
}
|
||||
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (is_int) {
|
||||
return sema.addConstant(
|
||||
resolved_type,
|
||||
try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target),
|
||||
);
|
||||
} else {
|
||||
return sema.addConstant(
|
||||
resolved_type,
|
||||
try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, target),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
break :rs rhs_src;
|
||||
}
|
||||
} else {
|
||||
break :rs lhs_src;
|
||||
}
|
||||
};
|
||||
|
||||
try sema.requireRuntimeBlock(block, src, runtime_src);
|
||||
|
||||
if (block.wantSafety()) {
|
||||
try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
|
||||
try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
|
||||
}
|
||||
|
||||
const air_tag = if (is_int) Air.Inst.Tag.div_trunc else switch (block.float_mode) {
|
||||
.Optimized => Air.Inst.Tag.div_float_optimized,
|
||||
.Strict => Air.Inst.Tag.div_float,
|
||||
};
|
||||
return block.addBinOp(air_tag, casted_lhs, casted_rhs);
|
||||
}
|
||||
|
||||
fn zirDivExact(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
|
||||
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
|
||||
const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
|
||||
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
|
||||
const lhs = try sema.resolveInst(extra.lhs);
|
||||
const rhs = try sema.resolveInst(extra.rhs);
|
||||
const lhs_ty = sema.typeOf(lhs);
|
||||
const rhs_ty = sema.typeOf(rhs);
|
||||
const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
|
||||
const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
|
||||
try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
|
||||
try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .div_exact);
|
||||
|
||||
const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
|
||||
const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
|
||||
.override = &[_]LazySrcLoc{ lhs_src, rhs_src },
|
||||
});
|
||||
|
||||
const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
|
||||
const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
|
||||
|
||||
const lhs_scalar_ty = lhs_ty.scalarType();
|
||||
const scalar_tag = resolved_type.scalarType().zigTypeTag();
|
||||
|
||||
const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
|
||||
|
||||
try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_exact);
|
||||
|
||||
const mod = sema.mod;
|
||||
const target = mod.getTarget();
|
||||
const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs);
|
||||
const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs);
|
||||
|
||||
const runtime_src = rs: {
|
||||
// For integers:
|
||||
// If the lhs is zero, then zero is returned regardless of rhs.
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined, compile error because there is a possible
|
||||
// value for which the division would result in a remainder.
|
||||
// TODO: emit runtime safety for if there is a remainder
|
||||
// TODO: emit runtime safety for division by zero
|
||||
//
|
||||
// For floats:
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined, compile error because there is a possible
|
||||
// value for which the division would result in a remainder.
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (lhs_val.isUndef()) {
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
} else {
|
||||
if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
|
||||
return sema.addConstant(resolved_type, Value.zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (rhs_val.isUndef()) {
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
|
||||
return sema.failWithDivideByZero(block, rhs_src);
|
||||
}
|
||||
// TODO: if the RHS is one, return the LHS directly
|
||||
}
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (is_int) {
|
||||
// TODO: emit compile error if there is a remainder
|
||||
return sema.addConstant(
|
||||
resolved_type,
|
||||
try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target),
|
||||
);
|
||||
} else {
|
||||
// TODO: emit compile error if there is a remainder
|
||||
return sema.addConstant(
|
||||
resolved_type,
|
||||
try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, target),
|
||||
);
|
||||
}
|
||||
} else break :rs rhs_src;
|
||||
} else break :rs lhs_src;
|
||||
};
|
||||
|
||||
try sema.requireRuntimeBlock(block, src, runtime_src);
|
||||
|
||||
// Depending on whether safety is enabled, we will have a slightly different strategy
|
||||
// here. The `div_exact` AIR instruction causes undefined behavior if a remainder
|
||||
// is produced, so in the safety check case, it cannot be used. Instead we do a
|
||||
// div_trunc and check for remainder.
|
||||
|
||||
if (block.wantSafety()) {
|
||||
try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
|
||||
try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
|
||||
|
||||
const result = try block.addBinOp(.div_trunc, casted_lhs, casted_rhs);
|
||||
const ok = if (!is_int) ok: {
|
||||
const floored = try block.addUnOp(.floor, result);
|
||||
|
||||
if (resolved_type.zigTypeTag() == .Vector) {
|
||||
const eql = try block.addCmpVector(result, floored, .eq, try sema.addType(resolved_type));
|
||||
break :ok try block.addInst(.{
|
||||
.tag = switch (block.float_mode) {
|
||||
.Strict => .reduce,
|
||||
.Optimized => .reduce_optimized,
|
||||
},
|
||||
.data = .{ .reduce = .{
|
||||
.operand = eql,
|
||||
.operation = .And,
|
||||
} },
|
||||
});
|
||||
} else {
|
||||
const is_in_range = try block.addBinOp(switch (block.float_mode) {
|
||||
.Strict => .cmp_eq,
|
||||
.Optimized => .cmp_eq_optimized,
|
||||
}, result, floored);
|
||||
break :ok is_in_range;
|
||||
}
|
||||
} else ok: {
|
||||
const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs);
|
||||
|
||||
if (resolved_type.zigTypeTag() == .Vector) {
|
||||
const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
|
||||
const zero = try sema.addConstant(resolved_type, zero_val);
|
||||
const eql = try block.addCmpVector(remainder, zero, .eq, try sema.addType(resolved_type));
|
||||
break :ok try block.addInst(.{
|
||||
.tag = .reduce,
|
||||
.data = .{ .reduce = .{
|
||||
.operand = eql,
|
||||
.operation = .And,
|
||||
} },
|
||||
});
|
||||
} else {
|
||||
const zero = try sema.addConstant(resolved_type, Value.zero);
|
||||
const is_in_range = try block.addBinOp(.cmp_eq, remainder, zero);
|
||||
break :ok is_in_range;
|
||||
}
|
||||
};
|
||||
try sema.addSafetyCheck(block, ok, .exact_division_remainder);
|
||||
return result;
|
||||
}
|
||||
|
||||
return block.addBinOp(airTag(block, is_int, .div_exact, .div_exact_optimized), casted_lhs, casted_rhs);
|
||||
}
|
||||
|
||||
fn zirDivFloor(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
|
||||
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
|
||||
const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
|
||||
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
|
||||
const lhs = try sema.resolveInst(extra.lhs);
|
||||
const rhs = try sema.resolveInst(extra.rhs);
|
||||
const lhs_ty = sema.typeOf(lhs);
|
||||
const rhs_ty = sema.typeOf(rhs);
|
||||
const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
|
||||
const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
|
||||
try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
|
||||
try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .div_floor);
|
||||
|
||||
const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
|
||||
const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
|
||||
.override = &[_]LazySrcLoc{ lhs_src, rhs_src },
|
||||
});
|
||||
|
||||
const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
|
||||
const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
|
||||
|
||||
const lhs_scalar_ty = lhs_ty.scalarType();
|
||||
const rhs_scalar_ty = rhs_ty.scalarType();
|
||||
const scalar_tag = resolved_type.scalarType().zigTypeTag();
|
||||
|
||||
const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
|
||||
|
||||
try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_floor);
|
||||
|
||||
const mod = sema.mod;
|
||||
const target = mod.getTarget();
|
||||
const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs);
|
||||
const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs);
|
||||
|
||||
const runtime_src = rs: {
|
||||
// For integers:
|
||||
// If the lhs is zero, then zero is returned regardless of rhs.
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined:
|
||||
// * if lhs type is signed:
|
||||
// * if rhs is comptime-known and not -1, result is undefined
|
||||
// * if rhs is -1 or runtime-known, compile error because there is a
|
||||
// possible value (-min_int / -1) for which division would be
|
||||
// illegal behavior.
|
||||
// * if lhs type is unsigned, undef is returned regardless of rhs.
|
||||
// TODO: emit runtime safety for division by zero
|
||||
//
|
||||
// For floats:
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined, result is undefined.
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (!lhs_val.isUndef()) {
|
||||
if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
|
||||
return sema.addConstant(resolved_type, Value.zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (rhs_val.isUndef()) {
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
|
||||
return sema.failWithDivideByZero(block, rhs_src);
|
||||
}
|
||||
// TODO: if the RHS is one, return the LHS directly
|
||||
}
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (lhs_val.isUndef()) {
|
||||
if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) {
|
||||
return sema.addConstUndef(resolved_type);
|
||||
}
|
||||
}
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
return sema.addConstUndef(resolved_type);
|
||||
}
|
||||
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (is_int) {
|
||||
return sema.addConstant(
|
||||
resolved_type,
|
||||
try lhs_val.intDivFloor(rhs_val, resolved_type, sema.arena, target),
|
||||
);
|
||||
} else {
|
||||
return sema.addConstant(
|
||||
resolved_type,
|
||||
try lhs_val.floatDivFloor(rhs_val, resolved_type, sema.arena, target),
|
||||
);
|
||||
}
|
||||
} else break :rs rhs_src;
|
||||
} else break :rs lhs_src;
|
||||
};
|
||||
|
||||
try sema.requireRuntimeBlock(block, src, runtime_src);
|
||||
|
||||
if (block.wantSafety()) {
|
||||
try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
|
||||
try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
|
||||
}
|
||||
|
||||
return block.addBinOp(airTag(block, is_int, .div_floor, .div_floor_optimized), casted_lhs, casted_rhs);
|
||||
}
|
||||
|
||||
fn zirDivTrunc(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
|
||||
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
|
||||
const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
|
||||
const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
|
||||
const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
|
||||
const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
|
||||
const lhs = try sema.resolveInst(extra.lhs);
|
||||
const rhs = try sema.resolveInst(extra.rhs);
|
||||
const lhs_ty = sema.typeOf(lhs);
|
||||
const rhs_ty = sema.typeOf(rhs);
|
||||
const lhs_zig_ty_tag = try lhs_ty.zigTypeTagOrPoison();
|
||||
const rhs_zig_ty_tag = try rhs_ty.zigTypeTagOrPoison();
|
||||
try sema.checkVectorizableBinaryOperands(block, src, lhs_ty, rhs_ty, lhs_src, rhs_src);
|
||||
try sema.checkInvalidPtrArithmetic(block, src, lhs_ty, .div_trunc);
|
||||
|
||||
const instructions = &[_]Air.Inst.Ref{ lhs, rhs };
|
||||
const resolved_type = try sema.resolvePeerTypes(block, src, instructions, .{
|
||||
.override = &[_]LazySrcLoc{ lhs_src, rhs_src },
|
||||
});
|
||||
|
||||
const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
|
||||
const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
|
||||
|
||||
const lhs_scalar_ty = lhs_ty.scalarType();
|
||||
const rhs_scalar_ty = rhs_ty.scalarType();
|
||||
const scalar_tag = resolved_type.scalarType().zigTypeTag();
|
||||
|
||||
const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
|
||||
|
||||
try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, .div_trunc);
|
||||
|
||||
const mod = sema.mod;
|
||||
const target = mod.getTarget();
|
||||
const maybe_lhs_val = try sema.resolveMaybeUndefValIntable(block, lhs_src, casted_lhs);
|
||||
const maybe_rhs_val = try sema.resolveMaybeUndefValIntable(block, rhs_src, casted_rhs);
|
||||
|
||||
const runtime_src = rs: {
|
||||
// For integers:
|
||||
// If the lhs is zero, then zero is returned regardless of rhs.
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined:
|
||||
// * if lhs type is signed:
|
||||
// * if rhs is comptime-known and not -1, result is undefined
|
||||
// * if rhs is -1 or runtime-known, compile error because there is a
|
||||
// possible value (-min_int / -1) for which division would be
|
||||
// illegal behavior.
|
||||
// * if lhs type is unsigned, undef is returned regardless of rhs.
|
||||
// TODO: emit runtime safety for division by zero
|
||||
//
|
||||
// For floats:
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined, result is undefined.
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (!lhs_val.isUndef()) {
|
||||
if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
|
||||
return sema.addConstant(resolved_type, Value.zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (rhs_val.isUndef()) {
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
|
||||
return sema.failWithDivideByZero(block, rhs_src);
|
||||
}
|
||||
}
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (lhs_val.isUndef()) {
|
||||
if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) {
|
||||
return sema.addConstUndef(resolved_type);
|
||||
}
|
||||
}
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
return sema.addConstUndef(resolved_type);
|
||||
}
|
||||
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (is_int) {
|
||||
return sema.addConstant(
|
||||
resolved_type,
|
||||
try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target),
|
||||
);
|
||||
} else {
|
||||
return sema.addConstant(
|
||||
resolved_type,
|
||||
try lhs_val.floatDivTrunc(rhs_val, resolved_type, sema.arena, target),
|
||||
);
|
||||
}
|
||||
} else break :rs rhs_src;
|
||||
} else break :rs lhs_src;
|
||||
};
|
||||
|
||||
try sema.requireRuntimeBlock(block, src, runtime_src);
|
||||
|
||||
if (block.wantSafety()) {
|
||||
try sema.addDivIntOverflowSafety(block, resolved_type, lhs_scalar_ty, maybe_lhs_val, maybe_rhs_val, casted_lhs, casted_rhs, is_int);
|
||||
try sema.addDivByZeroSafety(block, resolved_type, maybe_rhs_val, casted_rhs, is_int);
|
||||
}
|
||||
|
||||
return block.addBinOp(airTag(block, is_int, .div_trunc, .div_trunc_optimized), casted_lhs, casted_rhs);
|
||||
}
|
||||
|
||||
fn addDivIntOverflowSafety(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
resolved_type: Type,
|
||||
lhs_scalar_ty: Type,
|
||||
maybe_lhs_val: ?Value,
|
||||
maybe_rhs_val: ?Value,
|
||||
casted_lhs: Air.Inst.Ref,
|
||||
casted_rhs: Air.Inst.Ref,
|
||||
is_int: bool,
|
||||
) CompileError!void {
|
||||
if (!is_int) return;
|
||||
|
||||
// If the LHS is unsigned, it cannot cause overflow.
|
||||
if (!lhs_scalar_ty.isSignedInt()) return;
|
||||
|
||||
const mod = sema.mod;
|
||||
const target = mod.getTarget();
|
||||
|
||||
// If the LHS is widened to a larger integer type, no overflow is possible.
|
||||
if (lhs_scalar_ty.intInfo(target).bits < resolved_type.intInfo(target).bits) {
|
||||
return;
|
||||
}
|
||||
|
||||
const min_int = try resolved_type.minInt(sema.arena, target);
|
||||
const neg_one_scalar = try Value.Tag.int_i64.create(sema.arena, -1);
|
||||
const neg_one = if (resolved_type.zigTypeTag() == .Vector)
|
||||
try Value.Tag.repeated.create(sema.arena, neg_one_scalar)
|
||||
else
|
||||
neg_one_scalar;
|
||||
|
||||
// If the LHS is comptime-known to be not equal to the min int,
|
||||
// no overflow is possible.
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (!lhs_val.compare(.eq, min_int, resolved_type, mod)) return;
|
||||
}
|
||||
|
||||
// If the RHS is comptime-known to not be equal to -1, no overflow is possible.
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (!rhs_val.compare(.eq, neg_one, resolved_type, mod)) return;
|
||||
}
|
||||
|
||||
var ok: Air.Inst.Ref = .none;
|
||||
if (resolved_type.zigTypeTag() == .Vector) {
|
||||
const vector_ty_ref = try sema.addType(resolved_type);
|
||||
if (maybe_lhs_val == null) {
|
||||
const min_int_ref = try sema.addConstant(resolved_type, min_int);
|
||||
ok = try block.addCmpVector(casted_lhs, min_int_ref, .neq, vector_ty_ref);
|
||||
}
|
||||
if (maybe_rhs_val == null) {
|
||||
const neg_one_ref = try sema.addConstant(resolved_type, neg_one);
|
||||
const rhs_ok = try block.addCmpVector(casted_rhs, neg_one_ref, .neq, vector_ty_ref);
|
||||
if (ok == .none) {
|
||||
ok = rhs_ok;
|
||||
} else {
|
||||
ok = try block.addBinOp(.bool_or, ok, rhs_ok);
|
||||
}
|
||||
}
|
||||
assert(ok != .none);
|
||||
ok = try block.addInst(.{
|
||||
.tag = .reduce,
|
||||
.data = .{ .reduce = .{
|
||||
.operand = ok,
|
||||
.operation = .And,
|
||||
} },
|
||||
});
|
||||
} else {
|
||||
if (maybe_lhs_val == null) {
|
||||
const min_int_ref = try sema.addConstant(resolved_type, min_int);
|
||||
ok = try block.addBinOp(.cmp_neq, casted_lhs, min_int_ref);
|
||||
}
|
||||
if (maybe_rhs_val == null) {
|
||||
const neg_one_ref = try sema.addConstant(resolved_type, neg_one);
|
||||
const rhs_ok = try block.addBinOp(.cmp_neq, casted_rhs, neg_one_ref);
|
||||
if (ok == .none) {
|
||||
ok = rhs_ok;
|
||||
} else {
|
||||
ok = try block.addBinOp(.bool_or, ok, rhs_ok);
|
||||
}
|
||||
}
|
||||
assert(ok != .none);
|
||||
}
|
||||
try sema.addSafetyCheck(block, ok, .integer_overflow);
|
||||
}
|
||||
|
||||
fn addDivByZeroSafety(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
resolved_type: Type,
|
||||
maybe_rhs_val: ?Value,
|
||||
casted_rhs: Air.Inst.Ref,
|
||||
is_int: bool,
|
||||
) CompileError!void {
|
||||
// Strict IEEE floats have well-defined division by zero.
|
||||
if (!is_int and block.float_mode == .Strict) return;
|
||||
|
||||
// If rhs was comptime-known to be zero a compile error would have been
|
||||
// emitted above.
|
||||
if (maybe_rhs_val != null) return;
|
||||
|
||||
const ok = if (resolved_type.zigTypeTag() == .Vector) ok: {
|
||||
const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
|
||||
const zero = try sema.addConstant(resolved_type, zero_val);
|
||||
const ok = try block.addCmpVector(casted_rhs, zero, .neq, try sema.addType(resolved_type));
|
||||
break :ok try block.addInst(.{
|
||||
.tag = if (is_int) .reduce else .reduce_optimized,
|
||||
.data = .{ .reduce = .{
|
||||
.operand = ok,
|
||||
.operation = .And,
|
||||
} },
|
||||
});
|
||||
} else ok: {
|
||||
const zero = try sema.addConstant(resolved_type, Value.zero);
|
||||
break :ok try block.addBinOp(if (is_int) .cmp_neq else .cmp_neq_optimized, casted_rhs, zero);
|
||||
};
|
||||
try sema.addSafetyCheck(block, ok, .divide_by_zero);
|
||||
}
|
||||
|
||||
fn airTag(block: *Block, is_int: bool, normal: Air.Inst.Tag, optimized: Air.Inst.Tag) Air.Inst.Tag {
|
||||
if (is_int) return normal;
|
||||
return switch (block.float_mode) {
|
||||
.Strict => normal,
|
||||
.Optimized => optimized,
|
||||
};
|
||||
}
|
||||
|
||||
fn zirOverflowArithmetic(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
@@ -11108,13 +11817,8 @@ fn analyzeArithmetic(
|
||||
const scalar_tag = resolved_type.scalarType().zigTypeTag();
|
||||
|
||||
const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
|
||||
const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
|
||||
|
||||
if (!is_int and !(is_float and floatOpAllowed(zir_tag))) {
|
||||
return sema.fail(block, src, "invalid operands to binary expression: '{s}' and '{s}'", .{
|
||||
@tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag),
|
||||
});
|
||||
}
|
||||
try sema.checkArithmeticOp(block, src, scalar_tag, lhs_zig_ty_tag, rhs_zig_ty_tag, zir_tag);
|
||||
|
||||
const mod = sema.mod;
|
||||
const target = mod.getTarget();
|
||||
@@ -11321,277 +12025,6 @@ fn analyzeArithmetic(
|
||||
} else break :rs .{ .src = rhs_src, .air_tag = .sub_sat };
|
||||
} else break :rs .{ .src = lhs_src, .air_tag = .sub_sat };
|
||||
},
|
||||
.div => {
|
||||
// TODO: emit compile error when .div is used on integers and there would be an
|
||||
// ambiguous result between div_floor and div_trunc.
|
||||
|
||||
// For integers:
|
||||
// If the lhs is zero, then zero is returned regardless of rhs.
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined:
|
||||
// * if lhs type is signed:
|
||||
// * if rhs is comptime-known and not -1, result is undefined
|
||||
// * if rhs is -1 or runtime-known, compile error because there is a
|
||||
// possible value (-min_int / -1) for which division would be
|
||||
// illegal behavior.
|
||||
// * if lhs type is unsigned, undef is returned regardless of rhs.
|
||||
// TODO: emit runtime safety for division by zero
|
||||
//
|
||||
// For floats:
|
||||
// If the rhs is zero:
|
||||
// * comptime_float: compile error for division by zero.
|
||||
// * other float type:
|
||||
// * if the lhs is zero: QNaN
|
||||
// * otherwise: +Inf or -Inf depending on lhs sign
|
||||
// If the rhs is undefined:
|
||||
// * comptime_float: compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// * other float type: result is undefined
|
||||
// If the lhs is undefined, result is undefined.
|
||||
switch (scalar_tag) {
|
||||
.Int, .ComptimeInt, .ComptimeFloat => {
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (!lhs_val.isUndef()) {
|
||||
if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
|
||||
return sema.addConstant(resolved_type, Value.zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (rhs_val.isUndef()) {
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
|
||||
return sema.failWithDivideByZero(block, rhs_src);
|
||||
}
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (lhs_val.isUndef()) {
|
||||
if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) {
|
||||
return sema.addConstUndef(resolved_type);
|
||||
}
|
||||
}
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
return sema.addConstUndef(resolved_type);
|
||||
}
|
||||
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (is_int) {
|
||||
return sema.addConstant(
|
||||
resolved_type,
|
||||
try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target),
|
||||
);
|
||||
} else {
|
||||
return sema.addConstant(
|
||||
resolved_type,
|
||||
try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, target),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (is_int) {
|
||||
break :rs .{ .src = rhs_src, .air_tag = .div_trunc };
|
||||
} else {
|
||||
break :rs .{ .src = rhs_src, .air_tag = if (block.float_mode == .Optimized) .div_float_optimized else .div_float };
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (is_int) {
|
||||
break :rs .{ .src = lhs_src, .air_tag = .div_trunc };
|
||||
} else {
|
||||
break :rs .{ .src = lhs_src, .air_tag = if (block.float_mode == .Optimized) .div_float_optimized else .div_float };
|
||||
}
|
||||
}
|
||||
},
|
||||
.div_trunc => {
|
||||
// For integers:
|
||||
// If the lhs is zero, then zero is returned regardless of rhs.
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined:
|
||||
// * if lhs type is signed:
|
||||
// * if rhs is comptime-known and not -1, result is undefined
|
||||
// * if rhs is -1 or runtime-known, compile error because there is a
|
||||
// possible value (-min_int / -1) for which division would be
|
||||
// illegal behavior.
|
||||
// * if lhs type is unsigned, undef is returned regardless of rhs.
|
||||
// TODO: emit runtime safety for division by zero
|
||||
//
|
||||
// For floats:
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined, result is undefined.
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (!lhs_val.isUndef()) {
|
||||
if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
|
||||
return sema.addConstant(resolved_type, Value.zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (rhs_val.isUndef()) {
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
|
||||
return sema.failWithDivideByZero(block, rhs_src);
|
||||
}
|
||||
}
|
||||
const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .div_trunc_optimized else .div_trunc;
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (lhs_val.isUndef()) {
|
||||
if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) {
|
||||
return sema.addConstUndef(resolved_type);
|
||||
}
|
||||
}
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
return sema.addConstUndef(resolved_type);
|
||||
}
|
||||
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (is_int) {
|
||||
return sema.addConstant(
|
||||
resolved_type,
|
||||
try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target),
|
||||
);
|
||||
} else {
|
||||
return sema.addConstant(
|
||||
resolved_type,
|
||||
try lhs_val.floatDivTrunc(rhs_val, resolved_type, sema.arena, target),
|
||||
);
|
||||
}
|
||||
} else break :rs .{ .src = rhs_src, .air_tag = air_tag };
|
||||
} else break :rs .{ .src = lhs_src, .air_tag = air_tag };
|
||||
},
|
||||
.div_floor => {
|
||||
// For integers:
|
||||
// If the lhs is zero, then zero is returned regardless of rhs.
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined:
|
||||
// * if lhs type is signed:
|
||||
// * if rhs is comptime-known and not -1, result is undefined
|
||||
// * if rhs is -1 or runtime-known, compile error because there is a
|
||||
// possible value (-min_int / -1) for which division would be
|
||||
// illegal behavior.
|
||||
// * if lhs type is unsigned, undef is returned regardless of rhs.
|
||||
// TODO: emit runtime safety for division by zero
|
||||
//
|
||||
// For floats:
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined, result is undefined.
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (!lhs_val.isUndef()) {
|
||||
if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
|
||||
return sema.addConstant(resolved_type, Value.zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (rhs_val.isUndef()) {
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
|
||||
return sema.failWithDivideByZero(block, rhs_src);
|
||||
}
|
||||
}
|
||||
const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .div_floor_optimized else .div_floor;
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (lhs_val.isUndef()) {
|
||||
if (lhs_scalar_ty.isSignedInt() and rhs_scalar_ty.isSignedInt()) {
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (try sema.compare(block, src, rhs_val, .neq, Value.negative_one, resolved_type)) {
|
||||
return sema.addConstUndef(resolved_type);
|
||||
}
|
||||
}
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
return sema.addConstUndef(resolved_type);
|
||||
}
|
||||
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (is_int) {
|
||||
return sema.addConstant(
|
||||
resolved_type,
|
||||
try lhs_val.intDivFloor(rhs_val, resolved_type, sema.arena, target),
|
||||
);
|
||||
} else {
|
||||
return sema.addConstant(
|
||||
resolved_type,
|
||||
try lhs_val.floatDivFloor(rhs_val, resolved_type, sema.arena, target),
|
||||
);
|
||||
}
|
||||
} else break :rs .{ .src = rhs_src, .air_tag = air_tag };
|
||||
} else break :rs .{ .src = lhs_src, .air_tag = air_tag };
|
||||
},
|
||||
.div_exact => {
|
||||
// For integers:
|
||||
// If the lhs is zero, then zero is returned regardless of rhs.
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined, compile error because there is a possible
|
||||
// value for which the division would result in a remainder.
|
||||
// TODO: emit runtime safety for if there is a remainder
|
||||
// TODO: emit runtime safety for division by zero
|
||||
//
|
||||
// For floats:
|
||||
// If the rhs is zero, compile error for division by zero.
|
||||
// If the rhs is undefined, compile error because there is a possible
|
||||
// value (zero) for which the division would be illegal behavior.
|
||||
// If the lhs is undefined, compile error because there is a possible
|
||||
// value for which the division would result in a remainder.
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (lhs_val.isUndef()) {
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
} else {
|
||||
if (try lhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
|
||||
return sema.addConstant(resolved_type, Value.zero);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (rhs_val.isUndef()) {
|
||||
return sema.failWithUseOfUndef(block, rhs_src);
|
||||
}
|
||||
if (try rhs_val.compareWithZeroAdvanced(.eq, sema.kit(block, src))) {
|
||||
return sema.failWithDivideByZero(block, rhs_src);
|
||||
}
|
||||
}
|
||||
const air_tag: Air.Inst.Tag = if (block.float_mode == .Optimized) .div_exact_optimized else .div_exact;
|
||||
if (maybe_lhs_val) |lhs_val| {
|
||||
if (maybe_rhs_val) |rhs_val| {
|
||||
if (is_int) {
|
||||
// TODO: emit compile error if there is a remainder
|
||||
return sema.addConstant(
|
||||
resolved_type,
|
||||
try lhs_val.intDiv(rhs_val, resolved_type, sema.arena, target),
|
||||
);
|
||||
} else {
|
||||
// TODO: emit compile error if there is a remainder
|
||||
return sema.addConstant(
|
||||
resolved_type,
|
||||
try lhs_val.floatDiv(rhs_val, resolved_type, sema.arena, target),
|
||||
);
|
||||
}
|
||||
} else break :rs .{ .src = rhs_src, .air_tag = air_tag };
|
||||
} else break :rs .{ .src = lhs_src, .air_tag = air_tag };
|
||||
},
|
||||
.mul => {
|
||||
// For integers:
|
||||
// If either of the operands are zero, the result is zero.
|
||||
@@ -11970,28 +12403,6 @@ fn analyzeArithmetic(
|
||||
}
|
||||
}
|
||||
switch (rs.air_tag) {
|
||||
// zig fmt: off
|
||||
.div_float, .div_exact, .div_trunc, .div_floor, .div_float_optimized,
|
||||
.div_exact_optimized, .div_trunc_optimized, .div_floor_optimized
|
||||
// zig fmt: on
|
||||
=> if (scalar_tag == .Int or block.float_mode == .Optimized) {
|
||||
const ok = if (resolved_type.zigTypeTag() == .Vector) ok: {
|
||||
const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
|
||||
const zero = try sema.addConstant(sema.typeOf(casted_rhs), zero_val);
|
||||
const ok = try block.addCmpVector(casted_rhs, zero, .neq, try sema.addType(resolved_type));
|
||||
break :ok try block.addInst(.{
|
||||
.tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
|
||||
.data = .{ .reduce = .{
|
||||
.operand = ok,
|
||||
.operation = .And,
|
||||
} },
|
||||
});
|
||||
} else ok: {
|
||||
const zero = try sema.addConstant(sema.typeOf(casted_rhs), Value.zero);
|
||||
break :ok try block.addBinOp(if (block.float_mode == .Optimized) .cmp_neq_optimized else .cmp_neq, casted_rhs, zero);
|
||||
};
|
||||
try sema.addSafetyCheck(block, ok, .divide_by_zero);
|
||||
},
|
||||
.rem, .mod, .rem_optimized, .mod_optimized => {
|
||||
const ok = if (resolved_type.zigTypeTag() == .Vector) ok: {
|
||||
const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
|
||||
@@ -12018,47 +12429,6 @@ fn analyzeArithmetic(
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
if (rs.air_tag == .div_exact or rs.air_tag == .div_exact_optimized) {
|
||||
const result = try block.addBinOp(.div_exact, casted_lhs, casted_rhs);
|
||||
const ok = if (scalar_tag == .Float) ok: {
|
||||
const floored = try block.addUnOp(.floor, result);
|
||||
|
||||
if (resolved_type.zigTypeTag() == .Vector) {
|
||||
const eql = try block.addCmpVector(result, floored, .eq, try sema.addType(resolved_type));
|
||||
break :ok try block.addInst(.{
|
||||
.tag = if (block.float_mode == .Optimized) .reduce_optimized else .reduce,
|
||||
.data = .{ .reduce = .{
|
||||
.operand = eql,
|
||||
.operation = .And,
|
||||
} },
|
||||
});
|
||||
} else {
|
||||
const is_in_range = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, result, floored);
|
||||
break :ok is_in_range;
|
||||
}
|
||||
} else ok: {
|
||||
const remainder = try block.addBinOp(.rem, casted_lhs, casted_rhs);
|
||||
|
||||
if (resolved_type.zigTypeTag() == .Vector) {
|
||||
const zero_val = try Value.Tag.repeated.create(sema.arena, Value.zero);
|
||||
const zero = try sema.addConstant(sema.typeOf(casted_rhs), zero_val);
|
||||
const eql = try block.addCmpVector(remainder, zero, .eq, try sema.addType(resolved_type));
|
||||
break :ok try block.addInst(.{
|
||||
.tag = .reduce,
|
||||
.data = .{ .reduce = .{
|
||||
.operand = eql,
|
||||
.operation = .And,
|
||||
} },
|
||||
});
|
||||
} else {
|
||||
const zero = try sema.addConstant(sema.typeOf(casted_rhs), Value.zero);
|
||||
const is_in_range = try block.addBinOp(if (block.float_mode == .Optimized) .cmp_eq_optimized else .cmp_eq, remainder, zero);
|
||||
break :ok is_in_range;
|
||||
}
|
||||
};
|
||||
try sema.addSafetyCheck(block, ok, .exact_division_remainder);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return block.addBinOp(rs.air_tag, casted_lhs, casted_rhs);
|
||||
}
|
||||
@@ -14696,6 +15066,22 @@ fn finishStructInit(
|
||||
field_inits[i] = try sema.addConstant(struct_obj.types[i], default_val);
|
||||
}
|
||||
}
|
||||
} else if (struct_ty.isTuple()) {
|
||||
const struct_obj = struct_ty.castTag(.tuple).?.data;
|
||||
for (struct_obj.values) |default_val, i| {
|
||||
if (field_inits[i] != .none) continue;
|
||||
|
||||
if (default_val.tag() == .unreachable_value) {
|
||||
const template = "missing tuple field with index {d}";
|
||||
if (root_msg) |msg| {
|
||||
try sema.errNote(block, init_src, msg, template, .{i});
|
||||
} else {
|
||||
root_msg = try sema.errMsg(block, init_src, template, .{i});
|
||||
}
|
||||
} else {
|
||||
field_inits[i] = try sema.addConstant(struct_obj.types[i], default_val);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const struct_obj = struct_ty.castTag(.@"struct").?.data;
|
||||
for (struct_obj.fields.values()) |field, i| {
|
||||
@@ -15373,6 +15759,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
|
||||
const tag_ty = type_info_ty.unionTagType().?;
|
||||
const target = mod.getTarget();
|
||||
const tag_index = tag_ty.enumTagFieldIndex(union_val.tag, mod).?;
|
||||
if (union_val.val.anyUndef()) return sema.failWithUseOfUndef(block, src);
|
||||
switch (@intToEnum(std.builtin.TypeId, tag_index)) {
|
||||
.Type => return Air.Inst.Ref.type_type,
|
||||
.Void => return Air.Inst.Ref.void_type,
|
||||
@@ -15442,7 +15829,10 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
|
||||
const is_allowzero_val = struct_val[6];
|
||||
const sentinel_val = struct_val[7];
|
||||
|
||||
const abi_align = @intCast(u29, alignment_val.toUnsignedInt(target)); // TODO: Validate this value.
|
||||
if (!try sema.intFitsInType(block, src, alignment_val, Type.u32, null)) {
|
||||
return sema.fail(block, src, "alignment must fit in 'u32'", .{});
|
||||
}
|
||||
const abi_align = @intCast(u29, alignment_val.toUnsignedInt(target));
|
||||
|
||||
var buffer: Value.ToTypeBuffer = undefined;
|
||||
const unresolved_elem_ty = child_val.toType(&buffer);
|
||||
@@ -15469,6 +15859,39 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
|
||||
actual_sentinel = (try sema.pointerDeref(block, src, sentinel_ptr_val, ptr_ty)).?;
|
||||
}
|
||||
|
||||
if (elem_ty.zigTypeTag() == .NoReturn) {
|
||||
return sema.fail(block, src, "pointer to noreturn not allowed", .{});
|
||||
} else if (elem_ty.zigTypeTag() == .Fn) {
|
||||
if (ptr_size != .One) {
|
||||
return sema.fail(block, src, "function pointers must be single pointers", .{});
|
||||
}
|
||||
const fn_align = elem_ty.fnInfo().alignment;
|
||||
if (abi_align != 0 and fn_align != 0 and
|
||||
abi_align != fn_align)
|
||||
{
|
||||
return sema.fail(block, src, "function pointer alignment disagrees with function alignment", .{});
|
||||
}
|
||||
} else if (ptr_size == .Many and elem_ty.zigTypeTag() == .Opaque) {
|
||||
return sema.fail(block, src, "unknown-length pointer to opaque not allowed", .{});
|
||||
} else if (ptr_size == .C) {
|
||||
if (!(try sema.validateExternType(elem_ty, .other))) {
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(block, src, "C pointers cannot point to non-C-ABI-compatible type '{}'", .{elem_ty.fmt(sema.mod)});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
|
||||
const src_decl = sema.mod.declPtr(block.src_decl);
|
||||
try sema.explainWhyTypeIsNotExtern(block, src, msg, src.toSrcLoc(src_decl), elem_ty, .other);
|
||||
|
||||
try sema.addDeclaredHereNote(msg, elem_ty);
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
if (elem_ty.zigTypeTag() == .Opaque) {
|
||||
return sema.fail(block, src, "C pointers cannot point to opaque types", .{});
|
||||
}
|
||||
}
|
||||
|
||||
const ty = try Type.ptr(sema.arena, mod, .{
|
||||
.size = ptr_size,
|
||||
.mutable = !is_const_val.toBool(),
|
||||
@@ -15529,6 +15952,10 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
|
||||
const error_set_ty = try error_set_val.toType(&buffer).copy(sema.arena);
|
||||
const payload_ty = try payload_val.toType(&buffer).copy(sema.arena);
|
||||
|
||||
if (error_set_ty.zigTypeTag() != .ErrorSet) {
|
||||
return sema.fail(block, src, "Type.ErrorUnion.error_set must be an error set type", .{});
|
||||
}
|
||||
|
||||
const ty = try Type.Tag.error_union.create(sema.arena, .{
|
||||
.error_set = error_set_ty,
|
||||
.payload = payload_ty,
|
||||
@@ -15542,7 +15969,7 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
|
||||
const decl_index = slice_val.ptr.pointerDecl().?;
|
||||
try sema.ensureDeclAnalyzed(decl_index);
|
||||
const decl = mod.declPtr(decl_index);
|
||||
const array_val = decl.val.castTag(.aggregate).?.data;
|
||||
const array_val: []Value = if (decl.val.castTag(.aggregate)) |some| some.data else &.{};
|
||||
|
||||
var names: Module.ErrorSet.NameMap = .{};
|
||||
try names.ensureUnusedCapacity(sema.arena, array_val.len);
|
||||
@@ -15554,7 +15981,10 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
|
||||
const name_str = try name_val.toAllocatedBytes(Type.initTag(.const_slice_u8), sema.arena, sema.mod);
|
||||
|
||||
const kv = try mod.getErrorValue(name_str);
|
||||
names.putAssumeCapacityNoClobber(kv.key, {});
|
||||
const gop = names.getOrPutAssumeCapacity(kv.key);
|
||||
if (gop.found_existing) {
|
||||
return sema.fail(block, src, "duplicate error '{s}'", .{name_str});
|
||||
}
|
||||
}
|
||||
|
||||
// names must be sorted
|
||||
@@ -15636,13 +16066,9 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
|
||||
new_decl.owns_tv = true;
|
||||
errdefer mod.abortAnonDecl(new_decl_index);
|
||||
|
||||
// Enum tag type
|
||||
var buffer: Value.ToTypeBuffer = undefined;
|
||||
const int_tag_ty = try tag_type_val.toType(&buffer).copy(new_decl_arena_allocator);
|
||||
|
||||
enum_obj.* = .{
|
||||
.owner_decl = new_decl_index,
|
||||
.tag_ty = int_tag_ty,
|
||||
.tag_ty = Type.@"null",
|
||||
.tag_ty_inferred = false,
|
||||
.fields = .{},
|
||||
.values = .{},
|
||||
@@ -15654,6 +16080,15 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
|
||||
},
|
||||
};
|
||||
|
||||
// Enum tag type
|
||||
var buffer: Value.ToTypeBuffer = undefined;
|
||||
const int_tag_ty = try tag_type_val.toType(&buffer).copy(new_decl_arena_allocator);
|
||||
|
||||
if (int_tag_ty.zigTypeTag() != .Int) {
|
||||
return sema.fail(block, src, "Type.Enum.tag_type must be an integer type", .{});
|
||||
}
|
||||
enum_obj.tag_ty = int_tag_ty;
|
||||
|
||||
// Fields
|
||||
const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod));
|
||||
if (fields_len > 0) {
|
||||
@@ -15691,6 +16126,8 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
|
||||
.mod = mod,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return sema.fail(block, src, "enums must have at least one field", .{});
|
||||
}
|
||||
|
||||
try new_decl.finalizeNewArena(&new_decl_arena);
|
||||
@@ -15800,11 +16237,17 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
|
||||
};
|
||||
|
||||
// Tag type
|
||||
var tag_ty_field_names: ?Module.EnumFull.NameMap = null;
|
||||
var enum_field_names: ?*Module.EnumNumbered.NameMap = null;
|
||||
const fields_len = try sema.usizeCast(block, src, fields_val.sliceLen(mod));
|
||||
if (tag_type_val.optionalValue()) |payload_val| {
|
||||
var buffer: Value.ToTypeBuffer = undefined;
|
||||
union_obj.tag_ty = try payload_val.toType(&buffer).copy(new_decl_arena_allocator);
|
||||
|
||||
if (union_obj.tag_ty.zigTypeTag() != .Enum) {
|
||||
return sema.fail(block, src, "Type.Union.tag_type must be an enum type", .{});
|
||||
}
|
||||
tag_ty_field_names = try union_obj.tag_ty.enumFields().clone(sema.arena);
|
||||
} else {
|
||||
union_obj.tag_ty = try sema.generateUnionTagTypeSimple(block, fields_len, null);
|
||||
enum_field_names = &union_obj.tag_ty.castTag(.enum_simple).?.data.fields;
|
||||
@@ -15836,6 +16279,19 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
|
||||
set.putAssumeCapacity(field_name, {});
|
||||
}
|
||||
|
||||
if (tag_ty_field_names) |*names| {
|
||||
const enum_has_field = names.orderedRemove(field_name);
|
||||
if (!enum_has_field) {
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(block, src, "no field named '{s}' in enum '{}'", .{ field_name, union_obj.tag_ty.fmt(sema.mod) });
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
}
|
||||
|
||||
const gop = union_obj.fields.getOrPutAssumeCapacity(field_name);
|
||||
if (gop.found_existing) {
|
||||
// TODO: better source location
|
||||
@@ -15848,12 +16304,108 @@ fn zirReify(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.I
|
||||
.abi_align = @intCast(u32, alignment_val.toUnsignedInt(target)),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return sema.fail(block, src, "unions must have at least one field", .{});
|
||||
}
|
||||
|
||||
if (tag_ty_field_names) |names| {
|
||||
if (names.count() > 0) {
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(block, src, "enum field(s) missing in union", .{});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
|
||||
const enum_ty = union_obj.tag_ty;
|
||||
for (names.keys()) |field_name| {
|
||||
const field_index = enum_ty.enumFieldIndex(field_name).?;
|
||||
try sema.addFieldErrNote(block, enum_ty, field_index, msg, "field '{s}' missing, declared here", .{field_name});
|
||||
}
|
||||
try sema.addDeclaredHereNote(msg, union_obj.tag_ty);
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
}
|
||||
|
||||
try new_decl.finalizeNewArena(&new_decl_arena);
|
||||
return sema.analyzeDeclVal(block, src, new_decl_index);
|
||||
},
|
||||
.Fn => return sema.fail(block, src, "TODO: Sema.zirReify for Fn", .{}),
|
||||
.Fn => {
|
||||
const struct_val = union_val.val.castTag(.aggregate).?.data;
|
||||
// TODO use reflection instead of magic numbers here
|
||||
// calling_convention: CallingConvention,
|
||||
const cc = struct_val[0].toEnum(std.builtin.CallingConvention);
|
||||
// alignment: comptime_int,
|
||||
const alignment_val = struct_val[1];
|
||||
// is_generic: bool,
|
||||
const is_generic = struct_val[2].toBool();
|
||||
// is_var_args: bool,
|
||||
const is_var_args = struct_val[3].toBool();
|
||||
// return_type: ?type,
|
||||
const return_type_val = struct_val[4];
|
||||
// args: []const Param,
|
||||
const args_val = struct_val[5];
|
||||
|
||||
if (is_generic) {
|
||||
return sema.fail(block, src, "Type.Fn.is_generic must be false for @Type", .{});
|
||||
}
|
||||
|
||||
if (is_var_args and cc != .C) {
|
||||
return sema.fail(block, src, "varargs functions must have C calling convention", .{});
|
||||
}
|
||||
|
||||
const alignment = @intCast(u29, alignment_val.toUnsignedInt(target)); // TODO: Validate this value.
|
||||
var buf: Value.ToTypeBuffer = undefined;
|
||||
|
||||
const args: []Value = if (args_val.castTag(.aggregate)) |some| some.data else &.{};
|
||||
var param_types = try sema.arena.alloc(Type, args.len);
|
||||
var comptime_params = try sema.arena.alloc(bool, args.len);
|
||||
var noalias_bits: u32 = 0;
|
||||
for (args) |arg, i| {
|
||||
const arg_val = arg.castTag(.aggregate).?.data;
|
||||
// TODO use reflection instead of magic numbers here
|
||||
// is_generic: bool,
|
||||
const arg_is_generic = arg_val[0].toBool();
|
||||
// is_noalias: bool,
|
||||
const arg_is_noalias = arg_val[1].toBool();
|
||||
// arg_type: ?type,
|
||||
const param_type_val = arg_val[2];
|
||||
|
||||
if (arg_is_generic) {
|
||||
return sema.fail(block, src, "Type.Fn.Param.is_generic must be false for @Type", .{});
|
||||
}
|
||||
|
||||
if (arg_is_noalias) {
|
||||
noalias_bits = @as(u32, 1) << (std.math.cast(u5, i) orelse
|
||||
return sema.fail(block, src, "this compiler implementation only supports 'noalias' on the first 32 parameters", .{}));
|
||||
}
|
||||
|
||||
const param_type = param_type_val.optionalValue() orelse
|
||||
return sema.fail(block, src, "Type.Fn.Param.arg_type must be non-null for @Type", .{});
|
||||
|
||||
param_types[i] = try param_type.toType(&buf).copy(sema.arena);
|
||||
}
|
||||
|
||||
const return_type = return_type_val.optionalValue() orelse
|
||||
return sema.fail(block, src, "Type.Fn.return_type must be non-null for @Type", .{});
|
||||
|
||||
var fn_info = Type.Payload.Function.Data{
|
||||
.param_types = param_types,
|
||||
.comptime_params = comptime_params.ptr,
|
||||
.noalias_bits = noalias_bits,
|
||||
.return_type = try return_type.toType(&buf).copy(sema.arena),
|
||||
.alignment = alignment,
|
||||
.cc = cc,
|
||||
.is_var_args = is_var_args,
|
||||
.is_generic = false,
|
||||
.align_is_generic = false,
|
||||
.cc_is_generic = false,
|
||||
.section_is_generic = false,
|
||||
.addrspace_is_generic = false,
|
||||
};
|
||||
|
||||
const ty = try Type.Tag.function.create(sema.arena, fn_info);
|
||||
return sema.addType(ty);
|
||||
},
|
||||
.BoundFn => @panic("TODO delete BoundFn from the language"),
|
||||
.Frame => @panic("TODO implement https://github.com/ziglang/zig/issues/10710"),
|
||||
}
|
||||
@@ -15996,6 +16548,11 @@ fn reifyStruct(
|
||||
// alignment: comptime_int,
|
||||
const alignment_val = field_struct_val[4];
|
||||
|
||||
if (!try sema.intFitsInType(block, src, alignment_val, Type.u32, null)) {
|
||||
return sema.fail(block, src, "alignment must fit in 'u32'", .{});
|
||||
}
|
||||
const abi_align = @intCast(u29, alignment_val.toUnsignedInt(target));
|
||||
|
||||
const field_name = try name_val.toAllocatedBytes(
|
||||
Type.initTag(.const_slice_u8),
|
||||
new_decl_arena_allocator,
|
||||
@@ -16019,7 +16576,7 @@ fn reifyStruct(
|
||||
var buffer: Value.ToTypeBuffer = undefined;
|
||||
gop.value_ptr.* = .{
|
||||
.ty = try field_type_val.toType(&buffer).copy(new_decl_arena_allocator),
|
||||
.abi_align = @intCast(u32, alignment_val.toUnsignedInt(target)),
|
||||
.abi_align = abi_align,
|
||||
.default_val = default_val,
|
||||
.is_comptime = is_comptime_val.toBool(),
|
||||
.offset = undefined,
|
||||
@@ -16710,6 +17267,46 @@ fn checkIntType(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) CompileEr
|
||||
}
|
||||
}
|
||||
|
||||
fn checkInvalidPtrArithmetic(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
src: LazySrcLoc,
|
||||
ty: Type,
|
||||
zir_tag: Zir.Inst.Tag,
|
||||
) CompileError!void {
|
||||
switch (try ty.zigTypeTagOrPoison()) {
|
||||
.Pointer => switch (ty.ptrSize()) {
|
||||
.One, .Slice => return,
|
||||
.Many, .C => return sema.fail(
|
||||
block,
|
||||
src,
|
||||
"invalid pointer arithmetic operand: '{s}''",
|
||||
.{@tagName(zir_tag)},
|
||||
),
|
||||
},
|
||||
else => return,
|
||||
}
|
||||
}
|
||||
|
||||
fn checkArithmeticOp(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
src: LazySrcLoc,
|
||||
scalar_tag: std.builtin.TypeId,
|
||||
lhs_zig_ty_tag: std.builtin.TypeId,
|
||||
rhs_zig_ty_tag: std.builtin.TypeId,
|
||||
zir_tag: Zir.Inst.Tag,
|
||||
) CompileError!void {
|
||||
const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
|
||||
const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
|
||||
|
||||
if (!is_int and !(is_float and floatOpAllowed(zir_tag))) {
|
||||
return sema.fail(block, src, "invalid operands to binary expression: '{s}' and '{s}'", .{
|
||||
@tagName(lhs_zig_ty_tag), @tagName(rhs_zig_ty_tag),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn checkPtrOperand(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
@@ -20241,7 +20838,7 @@ fn tupleFieldVal(
|
||||
return tupleFieldValByIndex(sema, block, src, tuple_byval, field_index, tuple_ty);
|
||||
}
|
||||
|
||||
/// Don't forget to check for "len" before calling this.
|
||||
/// Asserts that `field_name` is not "len".
|
||||
fn tupleFieldIndex(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
@@ -20249,8 +20846,12 @@ fn tupleFieldIndex(
|
||||
field_name: []const u8,
|
||||
field_name_src: LazySrcLoc,
|
||||
) CompileError!u32 {
|
||||
assert(!std.mem.eql(u8, field_name, "len"));
|
||||
if (std.fmt.parseUnsigned(u32, field_name, 10)) |field_index| {
|
||||
if (field_index < tuple_ty.structFieldCount()) return field_index;
|
||||
return sema.fail(block, field_name_src, "index '{s}' out of bounds of tuple '{}'", .{
|
||||
field_name, tuple_ty.fmt(sema.mod),
|
||||
});
|
||||
} else |_| {}
|
||||
|
||||
return sema.fail(block, field_name_src, "no field named '{s}' in tuple '{}'", .{
|
||||
@@ -23927,8 +24528,7 @@ fn coerceTupleToStruct(
|
||||
const struct_ty = try sema.resolveTypeFields(block, dest_ty_src, dest_ty);
|
||||
|
||||
if (struct_ty.isTupleOrAnonStruct()) {
|
||||
// NOTE remember to handle comptime fields
|
||||
return sema.fail(block, dest_ty_src, "TODO: implement coercion from tuples to tuples", .{});
|
||||
return sema.coerceTupleToTuple(block, struct_ty, inst, inst_src);
|
||||
}
|
||||
|
||||
const fields = struct_ty.structFields();
|
||||
@@ -24011,6 +24611,110 @@ fn coerceTupleToStruct(
|
||||
);
|
||||
}
|
||||
|
||||
fn coerceTupleToTuple(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
tuple_ty: Type,
|
||||
inst: Air.Inst.Ref,
|
||||
inst_src: LazySrcLoc,
|
||||
) !Air.Inst.Ref {
|
||||
const field_count = tuple_ty.structFieldCount();
|
||||
const field_vals = try sema.arena.alloc(Value, field_count);
|
||||
const field_refs = try sema.arena.alloc(Air.Inst.Ref, field_vals.len);
|
||||
mem.set(Air.Inst.Ref, field_refs, .none);
|
||||
|
||||
const inst_ty = sema.typeOf(inst);
|
||||
const tuple = inst_ty.tupleFields();
|
||||
var runtime_src: ?LazySrcLoc = null;
|
||||
for (tuple.types) |_, i_usize| {
|
||||
const i = @intCast(u32, i_usize);
|
||||
const field_src = inst_src; // TODO better source location
|
||||
const field_name = if (inst_ty.castTag(.anon_struct)) |payload|
|
||||
payload.data.names[i]
|
||||
else
|
||||
try std.fmt.allocPrint(sema.arena, "{d}", .{i});
|
||||
|
||||
if (mem.eql(u8, field_name, "len")) {
|
||||
return sema.fail(block, field_src, "cannot assign to 'len' field of tuple", .{});
|
||||
}
|
||||
|
||||
const field_index = try sema.tupleFieldIndex(block, tuple_ty, field_name, field_src);
|
||||
|
||||
const field_ty = tuple_ty.structFieldType(i);
|
||||
const default_val = tuple_ty.structFieldDefaultValue(i);
|
||||
const elem_ref = try tupleField(sema, block, inst_src, inst, field_src, i);
|
||||
const coerced = try sema.coerce(block, field_ty, elem_ref, field_src);
|
||||
field_refs[field_index] = coerced;
|
||||
if (default_val.tag() != .unreachable_value) {
|
||||
const init_val = (try sema.resolveMaybeUndefVal(block, field_src, coerced)) orelse {
|
||||
return sema.failWithNeededComptime(block, field_src, "value stored in comptime field must be comptime known");
|
||||
};
|
||||
|
||||
if (!init_val.eql(default_val, field_ty, sema.mod)) {
|
||||
return sema.failWithInvalidComptimeFieldStore(block, field_src, inst_ty, i);
|
||||
}
|
||||
}
|
||||
if (runtime_src == null) {
|
||||
if (try sema.resolveMaybeUndefVal(block, field_src, coerced)) |field_val| {
|
||||
field_vals[field_index] = field_val;
|
||||
} else {
|
||||
runtime_src = field_src;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Populate default field values and report errors for missing fields.
|
||||
var root_msg: ?*Module.ErrorMsg = null;
|
||||
|
||||
for (field_refs) |*field_ref, i| {
|
||||
if (field_ref.* != .none) continue;
|
||||
|
||||
const default_val = tuple_ty.structFieldDefaultValue(i);
|
||||
const field_ty = tuple_ty.structFieldType(i);
|
||||
|
||||
const field_src = inst_src; // TODO better source location
|
||||
if (default_val.tag() == .unreachable_value) {
|
||||
if (tuple_ty.isTuple()) {
|
||||
const template = "missing tuple field: {d}";
|
||||
if (root_msg) |msg| {
|
||||
try sema.errNote(block, field_src, msg, template, .{i});
|
||||
} else {
|
||||
root_msg = try sema.errMsg(block, field_src, template, .{i});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
const template = "missing struct field: {s}";
|
||||
const args = .{tuple_ty.structFieldName(i)};
|
||||
if (root_msg) |msg| {
|
||||
try sema.errNote(block, field_src, msg, template, args);
|
||||
} else {
|
||||
root_msg = try sema.errMsg(block, field_src, template, args);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (runtime_src == null) {
|
||||
field_vals[i] = default_val;
|
||||
} else {
|
||||
field_ref.* = try sema.addConstant(field_ty, default_val);
|
||||
}
|
||||
}
|
||||
|
||||
if (root_msg) |msg| {
|
||||
try sema.addDeclaredHereNote(msg, tuple_ty);
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
|
||||
if (runtime_src) |rs| {
|
||||
try sema.requireRuntimeBlock(block, inst_src, rs);
|
||||
return block.addAggregateInit(tuple_ty, field_refs);
|
||||
}
|
||||
|
||||
return sema.addConstant(
|
||||
tuple_ty,
|
||||
try Value.Tag.aggregate.create(sema.arena, field_vals),
|
||||
);
|
||||
}
|
||||
|
||||
fn analyzeDeclVal(
|
||||
sema: *Sema,
|
||||
block: *Block,
|
||||
@@ -24446,7 +25150,10 @@ fn analyzeSlice(
|
||||
if (!end_is_len) {
|
||||
const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
|
||||
if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
|
||||
if (try sema.resolveDefinedValue(block, src, ptr_or_slice)) |slice_val| {
|
||||
if (try sema.resolveMaybeUndefVal(block, src, ptr_or_slice)) |slice_val| {
|
||||
if (slice_val.isUndef()) {
|
||||
return sema.fail(block, src, "slice of undefined", .{});
|
||||
}
|
||||
const has_sentinel = slice_ty.sentinel() != null;
|
||||
var int_payload: Value.Payload.U64 = .{
|
||||
.base = .{ .tag = .int_u64 },
|
||||
@@ -24509,8 +25216,8 @@ fn analyzeSlice(
|
||||
};
|
||||
|
||||
// requirement: start <= end
|
||||
if (try sema.resolveDefinedValue(block, src, end)) |end_val| {
|
||||
if (try sema.resolveDefinedValue(block, src, start)) |start_val| {
|
||||
if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
|
||||
if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {
|
||||
if (try sema.compare(block, src, start_val, .gt, end_val, Type.usize)) {
|
||||
return sema.fail(
|
||||
block,
|
||||
@@ -24522,6 +25229,45 @@ fn analyzeSlice(
|
||||
},
|
||||
);
|
||||
}
|
||||
if (try sema.resolveMaybeUndefVal(block, ptr_src, new_ptr)) |ptr_val| sentinel_check: {
|
||||
const expected_sentinel = sentinel orelse break :sentinel_check;
|
||||
const start_int = start_val.getUnsignedInt(sema.mod.getTarget()).?;
|
||||
const end_int = end_val.getUnsignedInt(sema.mod.getTarget()).?;
|
||||
const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int);
|
||||
|
||||
const elem_ptr = try ptr_val.elemPtr(sema.typeOf(new_ptr), sema.arena, sentinel_index, sema.mod);
|
||||
const res = try sema.pointerDerefExtra(block, src, elem_ptr, elem_ty, false);
|
||||
const actual_sentinel = switch (res) {
|
||||
.runtime_load => break :sentinel_check,
|
||||
.val => |v| v,
|
||||
.needed_well_defined => |ty| return sema.fail(
|
||||
block,
|
||||
src,
|
||||
"comptime dereference requires '{}' to have a well-defined layout, but it does not.",
|
||||
.{ty.fmt(sema.mod)},
|
||||
),
|
||||
.out_of_bounds => |ty| return sema.fail(
|
||||
block,
|
||||
end_src,
|
||||
"slice end index {d} exceeds bounds of containing decl of type '{}'",
|
||||
.{ end_int, ty.fmt(sema.mod) },
|
||||
),
|
||||
};
|
||||
|
||||
if (!actual_sentinel.eql(expected_sentinel, elem_ty, sema.mod)) {
|
||||
const msg = msg: {
|
||||
const msg = try sema.errMsg(block, src, "value in memory does not match slice sentinel", .{});
|
||||
errdefer msg.destroy(sema.gpa);
|
||||
try sema.errNote(block, src, msg, "expected '{}', found '{}'", .{
|
||||
expected_sentinel.fmtValue(elem_ty, sema.mod),
|
||||
actual_sentinel.fmtValue(elem_ty, sema.mod),
|
||||
});
|
||||
|
||||
break :msg msg;
|
||||
};
|
||||
return sema.failWithOwnedErrorMsg(block, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26884,7 +27630,8 @@ fn enumFieldSrcLoc(
|
||||
.container_decl_arg_trailing,
|
||||
=> tree.containerDeclArg(enum_node),
|
||||
|
||||
else => unreachable,
|
||||
// Container was constructed with `@Type`.
|
||||
else => return LazySrcLoc.nodeOffset(node_offset),
|
||||
};
|
||||
var it_index: usize = 0;
|
||||
for (container_decl.ast.members) |member_node| {
|
||||
@@ -27161,9 +27908,36 @@ pub fn analyzeAddrspace(
|
||||
/// Returns `null` if the pointer contents cannot be loaded at comptime.
|
||||
fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr_ty: Type) CompileError!?Value {
|
||||
const load_ty = ptr_ty.childType();
|
||||
const res = try sema.pointerDerefExtra(block, src, ptr_val, load_ty, true);
|
||||
switch (res) {
|
||||
.runtime_load => return null,
|
||||
.val => |v| return v,
|
||||
.needed_well_defined => |ty| return sema.fail(
|
||||
block,
|
||||
src,
|
||||
"comptime dereference requires '{}' to have a well-defined layout, but it does not.",
|
||||
.{ty.fmt(sema.mod)},
|
||||
),
|
||||
.out_of_bounds => |ty| return sema.fail(
|
||||
block,
|
||||
src,
|
||||
"dereference of '{}' exceeds bounds of containing decl of type '{}'",
|
||||
.{ ptr_ty.fmt(sema.mod), ty.fmt(sema.mod) },
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
const DerefResult = union(enum) {
|
||||
runtime_load,
|
||||
val: Value,
|
||||
needed_well_defined: Type,
|
||||
out_of_bounds: Type,
|
||||
};
|
||||
|
||||
fn pointerDerefExtra(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, load_ty: Type, want_mutable: bool) CompileError!DerefResult {
|
||||
const target = sema.mod.getTarget();
|
||||
const deref = sema.beginComptimePtrLoad(block, src, ptr_val, load_ty) catch |err| switch (err) {
|
||||
error.RuntimeLoad => return null,
|
||||
error.RuntimeLoad => return DerefResult{ .runtime_load = {} },
|
||||
else => |e| return e,
|
||||
};
|
||||
|
||||
@@ -27174,39 +27948,40 @@ fn pointerDeref(sema: *Sema, block: *Block, src: LazySrcLoc, ptr_val: Value, ptr
|
||||
if (coerce_in_mem_ok) {
|
||||
// We have a Value that lines up in virtual memory exactly with what we want to load,
|
||||
// and it is in-memory coercible to load_ty. It may be returned without modifications.
|
||||
if (deref.is_mutable) {
|
||||
if (deref.is_mutable and want_mutable) {
|
||||
// The decl whose value we are obtaining here may be overwritten with
|
||||
// a different value upon further semantic analysis, which would
|
||||
// invalidate this memory. So we must copy here.
|
||||
return try tv.val.copy(sema.arena);
|
||||
return DerefResult{ .val = try tv.val.copy(sema.arena) };
|
||||
}
|
||||
return tv.val;
|
||||
return DerefResult{ .val = tv.val };
|
||||
}
|
||||
}
|
||||
|
||||
// The type is not in-memory coercible or the direct dereference failed, so it must
|
||||
// be bitcast according to the pointer type we are performing the load through.
|
||||
if (!load_ty.hasWellDefinedLayout())
|
||||
return sema.fail(block, src, "comptime dereference requires '{}' to have a well-defined layout, but it does not.", .{load_ty.fmt(sema.mod)});
|
||||
if (!load_ty.hasWellDefinedLayout()) {
|
||||
return DerefResult{ .needed_well_defined = load_ty };
|
||||
}
|
||||
|
||||
const load_sz = try sema.typeAbiSize(block, src, load_ty);
|
||||
|
||||
// Try the smaller bit-cast first, since that's more efficient than using the larger `parent`
|
||||
if (deref.pointee) |tv| if (load_sz <= try sema.typeAbiSize(block, src, tv.ty))
|
||||
return try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0);
|
||||
return DerefResult{ .val = try sema.bitCastVal(block, src, tv.val, tv.ty, load_ty, 0) };
|
||||
|
||||
// If that fails, try to bit-cast from the largest parent value with a well-defined layout
|
||||
if (deref.parent) |parent| if (load_sz + parent.byte_offset <= try sema.typeAbiSize(block, src, parent.tv.ty))
|
||||
return try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset);
|
||||
return DerefResult{ .val = try sema.bitCastVal(block, src, parent.tv.val, parent.tv.ty, load_ty, parent.byte_offset) };
|
||||
|
||||
if (deref.ty_without_well_defined_layout) |bad_ty| {
|
||||
// We got no parent for bit-casting, or the parent we got was too small. Either way, the problem
|
||||
// is that some type we encountered when de-referencing does not have a well-defined layout.
|
||||
return sema.fail(block, src, "comptime dereference requires '{}' to have a well-defined layout, but it does not.", .{bad_ty.fmt(sema.mod)});
|
||||
return DerefResult{ .needed_well_defined = bad_ty };
|
||||
} else {
|
||||
// If all encountered types had well-defined layouts, the parent is the root decl and it just
|
||||
// wasn't big enough for the load.
|
||||
return sema.fail(block, src, "dereference of '{}' exceeds bounds of containing decl of type '{}'", .{ ptr_ty.fmt(sema.mod), deref.parent.?.tv.ty.fmt(sema.mod) });
|
||||
return DerefResult{ .out_of_bounds = deref.parent.?.tv.ty };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+10
-4
@@ -73,6 +73,9 @@ pub fn print(
|
||||
const target = mod.getTarget();
|
||||
var val = tv.val;
|
||||
var ty = tv.ty;
|
||||
if (val.isVariable(mod))
|
||||
return writer.writeAll("(variable)");
|
||||
|
||||
while (true) switch (val.tag()) {
|
||||
.u1_type => return writer.writeAll("u1"),
|
||||
.u8_type => return writer.writeAll("u8"),
|
||||
@@ -155,9 +158,12 @@ pub fn print(
|
||||
}
|
||||
try print(.{
|
||||
.ty = ty.structFieldType(i),
|
||||
.val = ty.structFieldValueComptime(i) orelse b: {
|
||||
const vals = val.castTag(.aggregate).?.data;
|
||||
break :b vals[i];
|
||||
.val = switch (ty.containerLayout()) {
|
||||
.Packed => val.castTag(.aggregate).?.data[i],
|
||||
else => ty.structFieldValueComptime(i) orelse b: {
|
||||
const vals = val.castTag(.aggregate).?.data;
|
||||
break :b vals[i];
|
||||
},
|
||||
},
|
||||
}, writer, level - 1, mod);
|
||||
}
|
||||
@@ -241,7 +247,7 @@ pub fn print(
|
||||
mod.declPtr(val.castTag(.function).?.data.owner_decl).name,
|
||||
}),
|
||||
.extern_fn => return writer.writeAll("(extern function)"),
|
||||
.variable => return writer.writeAll("(variable)"),
|
||||
.variable => unreachable,
|
||||
.decl_ref_mut => {
|
||||
const decl_index = val.castTag(.decl_ref_mut).?.data.decl_index;
|
||||
const decl = mod.declPtr(decl_index);
|
||||
|
||||
+12
-1
@@ -280,6 +280,9 @@ pub const Inst = struct {
|
||||
/// break instruction in a block, and the target block is the parent.
|
||||
/// Uses the `break` union field.
|
||||
break_inline,
|
||||
/// Checks that comptime control flow does not happen inside a runtime block.
|
||||
/// Uses the `node` union field.
|
||||
check_comptime_control_flow,
|
||||
/// Function call.
|
||||
/// Uses the `pl_node` union field with payload `Call`.
|
||||
/// AST node is the function call.
|
||||
@@ -1266,6 +1269,7 @@ pub const Inst = struct {
|
||||
.repeat_inline,
|
||||
.panic,
|
||||
.panic_comptime,
|
||||
.check_comptime_control_flow,
|
||||
=> true,
|
||||
};
|
||||
}
|
||||
@@ -1315,6 +1319,7 @@ pub const Inst = struct {
|
||||
.set_runtime_safety,
|
||||
.memcpy,
|
||||
.memset,
|
||||
.check_comptime_control_flow,
|
||||
=> true,
|
||||
|
||||
.param,
|
||||
@@ -1595,6 +1600,7 @@ pub const Inst = struct {
|
||||
.bool_br_or = .bool_br,
|
||||
.@"break" = .@"break",
|
||||
.break_inline = .@"break",
|
||||
.check_comptime_control_flow = .node,
|
||||
.call = .pl_node,
|
||||
.cmp_lt = .pl_node,
|
||||
.cmp_lte = .pl_node,
|
||||
@@ -1703,7 +1709,7 @@ pub const Inst = struct {
|
||||
.switch_capture_multi_ref = .switch_capture,
|
||||
.array_base_ptr = .un_node,
|
||||
.field_base_ptr = .un_node,
|
||||
.validate_array_init_ty = .un_node,
|
||||
.validate_array_init_ty = .pl_node,
|
||||
.validate_struct_init_ty = .un_node,
|
||||
.validate_struct_init = .pl_node,
|
||||
.validate_struct_init_comptime = .pl_node,
|
||||
@@ -3537,6 +3543,11 @@ pub const Inst = struct {
|
||||
line: u32,
|
||||
column: u32,
|
||||
};
|
||||
|
||||
pub const ArrayInit = struct {
|
||||
ty: Ref,
|
||||
init_count: u32,
|
||||
};
|
||||
};
|
||||
|
||||
pub const SpecialProng = enum { none, @"else", under };
|
||||
|
||||
+33
-26
@@ -4300,17 +4300,6 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
|
||||
);
|
||||
defer self.gpa.free(liveness.deaths);
|
||||
|
||||
// If the condition dies here in this switch instruction, process
|
||||
// that death now instead of later as this has an effect on
|
||||
// whether it needs to be spilled in the branches
|
||||
if (self.liveness.operandDies(inst, 0)) {
|
||||
const op_int = @enumToInt(pl_op.operand);
|
||||
if (op_int >= Air.Inst.Ref.typed_value_map.len) {
|
||||
const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
|
||||
self.processDeath(op_index);
|
||||
}
|
||||
}
|
||||
|
||||
var extra_index: usize = switch_br.end;
|
||||
var case_i: u32 = 0;
|
||||
while (case_i < switch_br.data.cases_len) : (case_i += 1) {
|
||||
@@ -4320,21 +4309,43 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
|
||||
const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len];
|
||||
extra_index = case.end + items.len + case_body.len;
|
||||
|
||||
var relocs = try self.gpa.alloc(u32, items.len);
|
||||
defer self.gpa.free(relocs);
|
||||
// For every item, we compare it to condition and branch into
|
||||
// the prong if they are equal. After we compared to all
|
||||
// items, we branch into the next prong (or if no other prongs
|
||||
// exist out of the switch statement).
|
||||
//
|
||||
// cmp condition, item1
|
||||
// beq prong
|
||||
// cmp condition, item2
|
||||
// beq prong
|
||||
// cmp condition, item3
|
||||
// beq prong
|
||||
// b out
|
||||
// prong: ...
|
||||
// ...
|
||||
// out: ...
|
||||
const branch_into_prong_relocs = try self.gpa.alloc(u32, items.len);
|
||||
defer self.gpa.free(branch_into_prong_relocs);
|
||||
|
||||
if (items.len == 1) {
|
||||
for (items) |item, idx| {
|
||||
const condition = try self.resolveInst(pl_op.operand);
|
||||
const item = try self.resolveInst(items[0]);
|
||||
const item_mcv = try self.resolveInst(item);
|
||||
|
||||
const operands: BinOpOperands = .{ .mcv = .{
|
||||
.lhs = condition,
|
||||
.rhs = item,
|
||||
.rhs = item_mcv,
|
||||
} };
|
||||
const cmp_result = try self.cmp(operands, condition_ty, .eq);
|
||||
relocs[0] = try self.condBr(cmp_result);
|
||||
} else {
|
||||
return self.fail("TODO switch with multiple items", .{});
|
||||
const cmp_result = try self.cmp(operands, condition_ty, .neq);
|
||||
branch_into_prong_relocs[idx] = try self.condBr(cmp_result);
|
||||
}
|
||||
|
||||
const branch_away_from_prong_reloc = try self.addInst(.{
|
||||
.tag = .b,
|
||||
.data = .{ .inst = undefined }, // populated later through performReloc
|
||||
});
|
||||
|
||||
for (branch_into_prong_relocs) |reloc| {
|
||||
try self.performReloc(reloc);
|
||||
}
|
||||
|
||||
// Capture the state of register and stack allocation state so that we can revert to it.
|
||||
@@ -4369,9 +4380,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
|
||||
self.next_stack_offset = parent_next_stack_offset;
|
||||
self.register_manager.free_registers = parent_free_registers;
|
||||
|
||||
for (relocs) |reloc| {
|
||||
try self.performReloc(reloc);
|
||||
}
|
||||
try self.performReloc(branch_away_from_prong_reloc);
|
||||
}
|
||||
|
||||
if (switch_br.data.else_body_len > 0) {
|
||||
@@ -4414,9 +4423,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
|
||||
// in airCondBr.
|
||||
}
|
||||
|
||||
// We already took care of pl_op.operand earlier, so we're going
|
||||
// to pass .none here
|
||||
return self.finishAir(inst, .unreach, .{ .none, .none, .none });
|
||||
return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none });
|
||||
}
|
||||
|
||||
fn performReloc(self: *Self, inst: Mir.Inst.Index) !void {
|
||||
|
||||
@@ -603,7 +603,7 @@ stack_alignment: u32 = 16,
|
||||
|
||||
const InnerError = error{
|
||||
OutOfMemory,
|
||||
/// An error occured when trying to lower AIR to MIR.
|
||||
/// An error occurred when trying to lower AIR to MIR.
|
||||
CodegenFail,
|
||||
/// Can occur when dereferencing a pointer that points to a `Decl` of which the analysis has failed
|
||||
AnalysisFail,
|
||||
@@ -4410,7 +4410,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) InnerError!WValue {
|
||||
}
|
||||
|
||||
// We store the bit if it's overflowed or not in this. As it's zero-initialized
|
||||
// we only need to update it if an overflow (or underflow) occured.
|
||||
// we only need to update it if an overflow (or underflow) occurred.
|
||||
const overflow_bit = try self.allocLocal(Type.initTag(.u1));
|
||||
const int_info = lhs_ty.intInfo(self.target);
|
||||
const wasm_bits = toWasmBits(int_info.bits) orelse {
|
||||
|
||||
@@ -1913,3 +1913,6 @@ extern fn ZigClangLoadFromCommandLine(
|
||||
errors_len: *usize,
|
||||
resources_path: [*:0]const u8,
|
||||
) ?*ASTUnit;
|
||||
|
||||
pub const isLLVMUsingSeparateLibcxx = ZigClangIsLLVMUsingSeparateLibcxx;
|
||||
extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool;
|
||||
|
||||
+109
-32
@@ -5491,22 +5491,26 @@ pub const FuncGen = struct {
|
||||
defer arena_allocator.deinit();
|
||||
const arena = arena_allocator.allocator();
|
||||
|
||||
const return_count: u8 = for (outputs) |output| {
|
||||
if (output == .none) break 1;
|
||||
} else 0;
|
||||
const llvm_params_len = inputs.len + outputs.len - return_count;
|
||||
const llvm_param_types = try arena.alloc(*const llvm.Type, llvm_params_len);
|
||||
const llvm_param_values = try arena.alloc(*const llvm.Value, llvm_params_len);
|
||||
const llvm_param_attrs = try arena.alloc(bool, llvm_params_len);
|
||||
// The exact number of return / parameter values depends on which output values
|
||||
// are passed by reference as indirect outputs (determined below).
|
||||
const max_return_count = outputs.len;
|
||||
const llvm_ret_types = try arena.alloc(*const llvm.Type, max_return_count);
|
||||
const llvm_ret_indirect = try arena.alloc(bool, max_return_count);
|
||||
|
||||
const max_param_count = inputs.len + outputs.len;
|
||||
const llvm_param_types = try arena.alloc(*const llvm.Type, max_param_count);
|
||||
const llvm_param_values = try arena.alloc(*const llvm.Value, max_param_count);
|
||||
const llvm_param_attrs = try arena.alloc(bool, max_param_count);
|
||||
const target = self.dg.module.getTarget();
|
||||
|
||||
var llvm_ret_i: usize = 0;
|
||||
var llvm_param_i: usize = 0;
|
||||
var total_i: usize = 0;
|
||||
var total_i: u16 = 0;
|
||||
|
||||
var name_map: std.StringArrayHashMapUnmanaged(void) = .{};
|
||||
try name_map.ensureUnusedCapacity(arena, outputs.len + inputs.len);
|
||||
var name_map: std.StringArrayHashMapUnmanaged(u16) = .{};
|
||||
try name_map.ensureUnusedCapacity(arena, max_param_count);
|
||||
|
||||
for (outputs) |output| {
|
||||
for (outputs) |output, i| {
|
||||
const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]);
|
||||
const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0);
|
||||
const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0);
|
||||
@@ -5519,15 +5523,30 @@ pub const FuncGen = struct {
|
||||
llvm_constraints.appendAssumeCapacity(',');
|
||||
}
|
||||
llvm_constraints.appendAssumeCapacity('=');
|
||||
|
||||
// Pass any non-return outputs indirectly, if the constraint accepts a memory location
|
||||
llvm_ret_indirect[i] = (output != .none) and constraintAllowsMemory(constraint);
|
||||
if (output != .none) {
|
||||
try llvm_constraints.ensureUnusedCapacity(self.gpa, llvm_constraints.capacity + 1);
|
||||
llvm_constraints.appendAssumeCapacity('*');
|
||||
|
||||
const output_inst = try self.resolveInst(output);
|
||||
llvm_param_values[llvm_param_i] = output_inst;
|
||||
llvm_param_types[llvm_param_i] = output_inst.typeOf();
|
||||
llvm_param_attrs[llvm_param_i] = true;
|
||||
llvm_param_i += 1;
|
||||
|
||||
if (llvm_ret_indirect[i]) {
|
||||
// Pass the result by reference as an indirect output (e.g. "=*m")
|
||||
llvm_constraints.appendAssumeCapacity('*');
|
||||
|
||||
llvm_param_values[llvm_param_i] = output_inst;
|
||||
llvm_param_types[llvm_param_i] = output_inst.typeOf();
|
||||
llvm_param_attrs[llvm_param_i] = true;
|
||||
llvm_param_i += 1;
|
||||
} else {
|
||||
// Pass the result directly (e.g. "=r")
|
||||
llvm_ret_types[llvm_ret_i] = output_inst.typeOf().getElementType();
|
||||
llvm_ret_i += 1;
|
||||
}
|
||||
} else {
|
||||
const ret_ty = self.air.typeOfIndex(inst);
|
||||
llvm_ret_types[llvm_ret_i] = try self.dg.lowerType(ret_ty);
|
||||
llvm_ret_i += 1;
|
||||
}
|
||||
|
||||
// LLVM uses commas internally to separate different constraints,
|
||||
@@ -5536,13 +5555,16 @@ pub const FuncGen = struct {
|
||||
// to GCC's inline assembly.
|
||||
// http://llvm.org/docs/LangRef.html#constraint-codes
|
||||
for (constraint[1..]) |byte| {
|
||||
llvm_constraints.appendAssumeCapacity(switch (byte) {
|
||||
',' => '|',
|
||||
else => byte,
|
||||
});
|
||||
switch (byte) {
|
||||
',' => llvm_constraints.appendAssumeCapacity('|'),
|
||||
'*' => {}, // Indirect outputs are handled above
|
||||
else => llvm_constraints.appendAssumeCapacity(byte),
|
||||
}
|
||||
}
|
||||
|
||||
name_map.putAssumeCapacityNoClobber(name, {});
|
||||
if (!std.mem.eql(u8, name, "_")) {
|
||||
name_map.putAssumeCapacityNoClobber(name, total_i);
|
||||
}
|
||||
total_i += 1;
|
||||
}
|
||||
|
||||
@@ -5594,7 +5616,7 @@ pub const FuncGen = struct {
|
||||
}
|
||||
|
||||
if (!std.mem.eql(u8, name, "_")) {
|
||||
name_map.putAssumeCapacityNoClobber(name, {});
|
||||
name_map.putAssumeCapacityNoClobber(name, total_i);
|
||||
}
|
||||
|
||||
// In the case of indirect inputs, LLVM requires the callsite to have
|
||||
@@ -5625,6 +5647,11 @@ pub const FuncGen = struct {
|
||||
}
|
||||
}
|
||||
|
||||
// We have finished scanning through all inputs/outputs, so the number of
|
||||
// parameters and return values is known.
|
||||
const param_count = llvm_param_i;
|
||||
const return_count = llvm_ret_i;
|
||||
|
||||
// For some targets, Clang unconditionally adds some clobbers to all inline assembly.
|
||||
// While this is probably not strictly necessary, if we don't follow Clang's lead
|
||||
// here then we may risk tripping LLVM bugs since anything not used by Clang tends
|
||||
@@ -5682,7 +5709,7 @@ pub const FuncGen = struct {
|
||||
const name = asm_source[name_start..i];
|
||||
state = .start;
|
||||
|
||||
const index = name_map.getIndex(name) orelse {
|
||||
const index = name_map.get(name) orelse {
|
||||
// we should validate the assembly in Sema; by now it is too late
|
||||
return self.todo("unknown input or output name: '{s}'", .{name});
|
||||
};
|
||||
@@ -5693,12 +5720,20 @@ pub const FuncGen = struct {
|
||||
}
|
||||
}
|
||||
|
||||
const ret_ty = self.air.typeOfIndex(inst);
|
||||
const ret_llvm_ty = try self.dg.lowerType(ret_ty);
|
||||
const ret_llvm_ty = switch (return_count) {
|
||||
0 => self.context.voidType(),
|
||||
1 => llvm_ret_types[0],
|
||||
else => self.context.structType(
|
||||
llvm_ret_types.ptr,
|
||||
@intCast(c_uint, return_count),
|
||||
.False,
|
||||
),
|
||||
};
|
||||
|
||||
const llvm_fn_ty = llvm.functionType(
|
||||
ret_llvm_ty,
|
||||
llvm_param_types.ptr,
|
||||
@intCast(c_uint, llvm_param_types.len),
|
||||
@intCast(c_uint, param_count),
|
||||
.False,
|
||||
);
|
||||
const asm_fn = llvm.getInlineAsm(
|
||||
@@ -5715,18 +5750,40 @@ pub const FuncGen = struct {
|
||||
const call = self.builder.buildCall(
|
||||
asm_fn,
|
||||
llvm_param_values.ptr,
|
||||
@intCast(c_uint, llvm_param_values.len),
|
||||
@intCast(c_uint, param_count),
|
||||
.C,
|
||||
.Auto,
|
||||
"",
|
||||
);
|
||||
for (llvm_param_attrs) |need_elem_ty, i| {
|
||||
for (llvm_param_attrs[0..param_count]) |need_elem_ty, i| {
|
||||
if (need_elem_ty) {
|
||||
const elem_ty = llvm_param_types[i].getElementType();
|
||||
llvm.setCallElemTypeAttr(call, i, elem_ty);
|
||||
}
|
||||
}
|
||||
return call;
|
||||
|
||||
var ret_val = call;
|
||||
llvm_ret_i = 0;
|
||||
for (outputs) |output, i| {
|
||||
if (llvm_ret_indirect[i]) continue;
|
||||
|
||||
const output_value = if (return_count > 1) b: {
|
||||
break :b self.builder.buildExtractValue(call, @intCast(c_uint, llvm_ret_i), "");
|
||||
} else call;
|
||||
|
||||
if (output != .none) {
|
||||
const output_ptr = try self.resolveInst(output);
|
||||
const output_ptr_ty = self.air.typeOf(output);
|
||||
|
||||
const store_inst = self.builder.buildStore(output_value, output_ptr);
|
||||
store_inst.setAlignment(output_ptr_ty.ptrAlignment(target));
|
||||
} else {
|
||||
ret_val = output_value;
|
||||
}
|
||||
llvm_ret_i += 1;
|
||||
}
|
||||
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
fn airIsNonNull(
|
||||
@@ -9709,10 +9766,30 @@ fn errUnionErrorOffset(payload_ty: Type, target: std.Target) u1 {
|
||||
return @boolToInt(Type.anyerror.abiAlignment(target) <= payload_ty.abiAlignment(target));
|
||||
}
|
||||
|
||||
/// Returns true for asm constraint (e.g. "=*m", "=r") if it accepts a memory location
|
||||
///
|
||||
/// See also TargetInfo::validateOutputConstraint, AArch64TargetInfo::validateAsmConstraint, etc. in Clang
|
||||
fn constraintAllowsMemory(constraint: []const u8) bool {
|
||||
return constraint[0] == 'm';
|
||||
// TODO: This implementation is woefully incomplete.
|
||||
for (constraint) |byte| {
|
||||
switch (byte) {
|
||||
'=', '*', ',', '&' => {},
|
||||
'm', 'o', 'X', 'g' => return true,
|
||||
else => {},
|
||||
}
|
||||
} else return false;
|
||||
}
|
||||
|
||||
/// Returns true for asm constraint (e.g. "=*m", "=r") if it accepts a register
|
||||
///
|
||||
/// See also TargetInfo::validateOutputConstraint, AArch64TargetInfo::validateAsmConstraint, etc. in Clang
|
||||
fn constraintAllowsRegister(constraint: []const u8) bool {
|
||||
return constraint[0] != 'm';
|
||||
// TODO: This implementation is woefully incomplete.
|
||||
for (constraint) |byte| {
|
||||
switch (byte) {
|
||||
'=', '*', ',', '&' => {},
|
||||
'm', 'o' => {},
|
||||
else => return true,
|
||||
}
|
||||
} else return false;
|
||||
}
|
||||
|
||||
@@ -1592,6 +1592,15 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
|
||||
}
|
||||
}
|
||||
}
|
||||
for (self.base.options.objects) |obj| {
|
||||
if (Compilation.classifyFileExt(obj.path) == .shared_library) {
|
||||
const lib_dir_path = std.fs.path.dirname(obj.path).?;
|
||||
if ((try rpath_table.fetchPut(lib_dir_path, {})) == null) {
|
||||
try argv.append("-rpath");
|
||||
try argv.append(lib_dir_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (self.base.options.lib_dirs) |lib_dir| {
|
||||
|
||||
+24
-1
@@ -174,6 +174,17 @@ pub fn main() anyerror!void {
|
||||
return mainArgs(gpa, arena, args);
|
||||
}
|
||||
|
||||
/// Check that LLVM and Clang have been linked properly so that they are using the same
|
||||
/// libc++ and can safely share objects with pointers to static variables in libc++
|
||||
fn verifyLibcxxCorrectlyLinked() void {
|
||||
if (build_options.have_llvm and ZigClangIsLLVMUsingSeparateLibcxx()) {
|
||||
fatal(
|
||||
\\Zig was built/linked incorrectly: LLVM and Clang have separate copies of libc++
|
||||
\\ If you are dynamically linking LLVM, make sure you dynamically link libc++ too
|
||||
, .{});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
|
||||
if (args.len <= 1) {
|
||||
std.log.info("{s}", .{usage});
|
||||
@@ -261,8 +272,12 @@ pub fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi
|
||||
const stdout = io.getStdOut().writer();
|
||||
return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target);
|
||||
} else if (mem.eql(u8, cmd, "version")) {
|
||||
return std.io.getStdOut().writeAll(build_options.version ++ "\n");
|
||||
try std.io.getStdOut().writeAll(build_options.version ++ "\n");
|
||||
// Check libc++ linkage to make sure Zig was built correctly, but only for "env" and "version"
|
||||
// to avoid affecting the startup time for build-critical commands (check takes about ~10 μs)
|
||||
return verifyLibcxxCorrectlyLinked();
|
||||
} else if (mem.eql(u8, cmd, "env")) {
|
||||
verifyLibcxxCorrectlyLinked();
|
||||
return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer());
|
||||
} else if (mem.eql(u8, cmd, "zen")) {
|
||||
return io.getStdOut().writeAll(info_zen);
|
||||
@@ -858,6 +873,12 @@ fn buildOutputType(
|
||||
) catch |err| {
|
||||
fatal("Failed to add package at path {s}: {s}", .{ pkg_path.?, @errorName(err) });
|
||||
};
|
||||
|
||||
if (mem.eql(u8, pkg_name.?, "std") or mem.eql(u8, pkg_name.?, "root") or mem.eql(u8, pkg_name.?, "builtin")) {
|
||||
fatal("unable to add package '{s}' -> '{s}': conflicts with builtin package", .{ pkg_name.?, pkg_path.? });
|
||||
} else if (cur_pkg.table.get(pkg_name.?)) |prev| {
|
||||
fatal("unable to add package '{s}' -> '{s}': already exists as '{s}", .{ pkg_name.?, pkg_path.?, prev.root_src_path });
|
||||
}
|
||||
try cur_pkg.addAndAdopt(gpa, pkg_name.?, new_cur_pkg);
|
||||
cur_pkg = new_cur_pkg;
|
||||
} else if (mem.eql(u8, arg, "--pkg-end")) {
|
||||
@@ -4481,6 +4502,8 @@ pub const info_zen =
|
||||
\\
|
||||
;
|
||||
|
||||
extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool;
|
||||
|
||||
extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
|
||||
extern "c" fn ZigLlvmAr_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int;
|
||||
|
||||
|
||||
+14
-1
@@ -229,7 +229,6 @@ const Writer = struct {
|
||||
.switch_cond_ref,
|
||||
.array_base_ptr,
|
||||
.field_base_ptr,
|
||||
.validate_array_init_ty,
|
||||
.validate_struct_init_ty,
|
||||
.make_ptr_const,
|
||||
.validate_deref,
|
||||
@@ -246,6 +245,7 @@ const Writer = struct {
|
||||
.bool_br_or,
|
||||
=> try self.writeBoolBr(stream, inst),
|
||||
|
||||
.validate_array_init_ty => try self.writeValidateArrayInitTy(stream, inst),
|
||||
.array_type_sentinel => try self.writeArrayTypeSentinel(stream, inst),
|
||||
.param_type => try self.writeParamType(stream, inst),
|
||||
.ptr_type => try self.writePtrType(stream, inst),
|
||||
@@ -409,6 +409,7 @@ const Writer = struct {
|
||||
.alloc_inferred_comptime_mut,
|
||||
.ret_ptr,
|
||||
.ret_type,
|
||||
.check_comptime_control_flow,
|
||||
=> try self.writeNode(stream, inst),
|
||||
|
||||
.error_value,
|
||||
@@ -576,6 +577,18 @@ const Writer = struct {
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
}
|
||||
|
||||
fn writeValidateArrayInitTy(
|
||||
self: *Writer,
|
||||
stream: anytype,
|
||||
inst: Zir.Inst.Index,
|
||||
) (@TypeOf(stream).Error || error{OutOfMemory})!void {
|
||||
const inst_data = self.code.instructions.items(.data)[inst].pl_node;
|
||||
const extra = self.code.extraData(Zir.Inst.ArrayInit, inst_data.payload_index).data;
|
||||
try self.writeInstRef(stream, extra.ty);
|
||||
try stream.print(", {d}) ", .{extra.init_count});
|
||||
try self.writeSrc(stream, inst_data.src());
|
||||
}
|
||||
|
||||
fn writeArrayTypeSentinel(
|
||||
self: *Writer,
|
||||
stream: anytype,
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
AstNode * ast_parse(Buf *buf, ZigType *owner, ErrColor err_color);
|
||||
|
||||
void ast_print(AstNode *node, int indent);
|
||||
|
||||
void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *context), void *context);
|
||||
|
||||
Buf *node_identifier_buf(AstNode *node);
|
||||
|
||||
+22
-8
@@ -2688,16 +2688,26 @@ fn transInitListExprVector(
|
||||
) TransError!Node {
|
||||
_ = ty;
|
||||
const qt = getExprQualType(c, @ptrCast(*const clang.Expr, expr));
|
||||
const vector_type = try transQualType(c, scope, qt, loc);
|
||||
const vector_ty = @ptrCast(*const clang.VectorType, qualTypeCanon(qt));
|
||||
|
||||
const init_count = expr.getNumInits();
|
||||
const num_elements = vector_ty.getNumElements();
|
||||
const element_qt = vector_ty.getElementType();
|
||||
|
||||
if (init_count == 0) {
|
||||
return Tag.container_init.create(c.arena, .{
|
||||
.lhs = vector_type,
|
||||
.inits = try c.arena.alloc(ast.Payload.ContainerInit.Initializer, 0),
|
||||
const zero_node = try Tag.as.create(c.arena, .{
|
||||
.lhs = try transQualType(c, scope, element_qt, loc),
|
||||
.rhs = Tag.zero_literal.init(),
|
||||
});
|
||||
|
||||
return Tag.vector_zero_init.create(c.arena, .{
|
||||
.lhs = try transCreateNodeNumber(c, num_elements, .int),
|
||||
.rhs = zero_node,
|
||||
});
|
||||
}
|
||||
|
||||
const vector_type = try transQualType(c, scope, qt, loc);
|
||||
|
||||
var block_scope = try Scope.Block.init(c, scope, true);
|
||||
defer block_scope.deinit();
|
||||
|
||||
@@ -2716,11 +2726,15 @@ fn transInitListExprVector(
|
||||
try block_scope.statements.append(tmp_decl_node);
|
||||
}
|
||||
|
||||
const init_list = try c.arena.alloc(Node, init_count);
|
||||
const init_list = try c.arena.alloc(Node, num_elements);
|
||||
for (init_list) |*init, init_index| {
|
||||
const tmp_decl = block_scope.statements.items[init_index];
|
||||
const name = tmp_decl.castTag(.var_simple).?.data.name;
|
||||
init.* = try Tag.identifier.create(c.arena, name);
|
||||
if (init_index < init_count) {
|
||||
const tmp_decl = block_scope.statements.items[init_index];
|
||||
const name = tmp_decl.castTag(.var_simple).?.data.name;
|
||||
init.* = try Tag.identifier.create(c.arena, name);
|
||||
} else {
|
||||
init.* = Tag.undefined_literal.init();
|
||||
}
|
||||
}
|
||||
|
||||
const array_init = try Tag.array_init.create(c.arena, .{
|
||||
|
||||
@@ -154,6 +154,8 @@ pub const Node = extern union {
|
||||
div_exact,
|
||||
/// @offsetOf(lhs, rhs)
|
||||
offset_of,
|
||||
/// @splat(lhs, rhs)
|
||||
vector_zero_init,
|
||||
/// @shuffle(type, a, b, mask)
|
||||
shuffle,
|
||||
|
||||
@@ -328,6 +330,7 @@ pub const Node = extern union {
|
||||
.div_exact,
|
||||
.offset_of,
|
||||
.helpers_cast,
|
||||
.vector_zero_init,
|
||||
=> Payload.BinOp,
|
||||
|
||||
.integer_literal,
|
||||
@@ -1829,6 +1832,10 @@ fn renderNode(c: *Context, node: Node) Allocator.Error!NodeIndex {
|
||||
const type_expr = try renderNode(c, payload.cond);
|
||||
return renderArrayInit(c, type_expr, payload.cases);
|
||||
},
|
||||
.vector_zero_init => {
|
||||
const payload = node.castTag(.vector_zero_init).?.data;
|
||||
return renderBuiltinCall(c, "@splat", &.{ payload.lhs, payload.rhs });
|
||||
},
|
||||
.field_access => {
|
||||
const payload = node.castTag(.field_access).?.data;
|
||||
const lhs = try renderNodeGrouped(c, payload.lhs);
|
||||
@@ -2305,6 +2312,7 @@ fn renderNodeGrouped(c: *Context, node: Node) !NodeIndex {
|
||||
.@"struct",
|
||||
.@"union",
|
||||
.array_init,
|
||||
.vector_zero_init,
|
||||
.tuple,
|
||||
.container_init,
|
||||
.container_init_dot,
|
||||
|
||||
+13
-3
@@ -5201,10 +5201,20 @@ pub const Type = extern union {
|
||||
};
|
||||
}
|
||||
|
||||
// Works for vectors and vectors of integers.
|
||||
pub fn minInt(ty: Type, arena: Allocator, target: Target) !Value {
|
||||
const scalar = try minIntScalar(ty.scalarType(), arena, target);
|
||||
if (ty.zigTypeTag() == .Vector) {
|
||||
return Value.Tag.repeated.create(arena, scalar);
|
||||
} else {
|
||||
return scalar;
|
||||
}
|
||||
}
|
||||
|
||||
/// Asserts that self.zigTypeTag() == .Int.
|
||||
pub fn minInt(self: Type, arena: Allocator, target: Target) !Value {
|
||||
assert(self.zigTypeTag() == .Int);
|
||||
const info = self.intInfo(target);
|
||||
pub fn minIntScalar(ty: Type, arena: Allocator, target: Target) !Value {
|
||||
assert(ty.zigTypeTag() == .Int);
|
||||
const info = ty.intInfo(target);
|
||||
|
||||
if (info.signedness == .unsigned) {
|
||||
return Value.zero;
|
||||
|
||||
+36
-15
@@ -2292,25 +2292,13 @@ pub const Value = extern union {
|
||||
}
|
||||
},
|
||||
.Struct => {
|
||||
if (ty.isTupleOrAnonStruct()) {
|
||||
const fields = ty.tupleFields();
|
||||
for (fields.values) |field_val, i| {
|
||||
field_val.hash(fields.types[i], hasher, mod);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const fields = ty.structFields().values();
|
||||
if (fields.len == 0) return;
|
||||
switch (val.tag()) {
|
||||
.empty_struct_value => {
|
||||
for (fields) |field| {
|
||||
field.default_val.hash(field.ty, hasher, mod);
|
||||
}
|
||||
},
|
||||
.empty_struct_value => {},
|
||||
.aggregate => {
|
||||
const field_values = val.castTag(.aggregate).?.data;
|
||||
for (field_values) |field_val, i| {
|
||||
field_val.hash(fields[i].ty, hasher, mod);
|
||||
const field_ty = ty.structFieldType(i);
|
||||
field_val.hash(field_ty, hasher, mod);
|
||||
}
|
||||
},
|
||||
else => unreachable,
|
||||
@@ -2664,6 +2652,26 @@ pub const Value = extern union {
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns true if a Value is backed by a variable
|
||||
pub fn isVariable(
|
||||
val: Value,
|
||||
mod: *Module,
|
||||
) bool {
|
||||
return switch (val.tag()) {
|
||||
.slice => val.castTag(.slice).?.data.ptr.isVariable(mod),
|
||||
.comptime_field_ptr => val.castTag(.comptime_field_ptr).?.data.field_val.isVariable(mod),
|
||||
.elem_ptr => val.castTag(.elem_ptr).?.data.array_ptr.isVariable(mod),
|
||||
.field_ptr => val.castTag(.field_ptr).?.data.container_ptr.isVariable(mod),
|
||||
.eu_payload_ptr => val.castTag(.eu_payload_ptr).?.data.container_ptr.isVariable(mod),
|
||||
.opt_payload_ptr => val.castTag(.opt_payload_ptr).?.data.container_ptr.isVariable(mod),
|
||||
.decl_ref => mod.declPtr(val.castTag(.decl_ref).?.data).val.isVariable(mod),
|
||||
.decl_ref_mut => mod.declPtr(val.castTag(.decl_ref_mut).?.data.decl_index).val.isVariable(mod),
|
||||
|
||||
.variable => true,
|
||||
else => false,
|
||||
};
|
||||
}
|
||||
|
||||
// Asserts that the provided start/end are in-bounds.
|
||||
pub fn sliceArray(
|
||||
val: Value,
|
||||
@@ -2778,6 +2786,19 @@ pub const Value = extern union {
|
||||
return self.isUndef();
|
||||
}
|
||||
|
||||
/// Returns true if any value contained in `self` is undefined.
|
||||
/// TODO: check for cases such as array that is not marked undef but all the element
|
||||
/// values are marked undef, or struct that is not marked undef but all fields are marked
|
||||
/// undef, etc.
|
||||
pub fn anyUndef(self: Value) bool {
|
||||
if (self.castTag(.aggregate)) |aggregate| {
|
||||
for (aggregate.data) |val| {
|
||||
if (val.anyUndef()) return true;
|
||||
}
|
||||
}
|
||||
return self.isUndef();
|
||||
}
|
||||
|
||||
/// Asserts the value is not undefined and not unreachable.
|
||||
/// Integer value 0 is considered null because of C pointers.
|
||||
pub fn isNull(self: Value) bool {
|
||||
|
||||
@@ -3432,3 +3432,31 @@ const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct Zi
|
||||
const llvm::APSInt *result = &casted->getInitVal();
|
||||
return reinterpret_cast<const ZigClangAPSInt *>(result);
|
||||
}
|
||||
|
||||
// Get a pointer to a static variable in libc++ from LLVM and make sure that
|
||||
// it matches our own.
|
||||
//
|
||||
// This check is needed because if static/dynamic linking is mixed incorrectly,
|
||||
// it's possible for Clang and LLVM to end up with duplicate "copies" of libc++.
|
||||
//
|
||||
// This is not benign: Static variables are not shared, so equality comparisons
|
||||
// that depend on pointers to static variables will fail. One such failure is
|
||||
// std::generic_category(), which causes POSIX error codes to compare as unequal
|
||||
// when passed between LLVM and Clang.
|
||||
//
|
||||
// See also: https://github.com/ziglang/zig/issues/11168
|
||||
bool ZigClangIsLLVMUsingSeparateLibcxx() {
|
||||
|
||||
// Temporarily create an InMemoryFileSystem, so that we can perform a file
|
||||
// lookup that is guaranteed to fail.
|
||||
auto FS = new llvm::vfs::InMemoryFileSystem(true);
|
||||
auto StatusOrErr = FS->status("foo.txt");
|
||||
delete FS;
|
||||
|
||||
// This should return a POSIX (generic_category) error code, but if LLVM has
|
||||
// its own copy of libc++ this will actually be a separate category instance.
|
||||
assert(!StatusOrErr);
|
||||
auto EC = StatusOrErr.getError();
|
||||
return EC.category() != std::generic_category();
|
||||
}
|
||||
|
||||
|
||||
@@ -1418,4 +1418,5 @@ ZIG_EXTERN_C const struct ZigClangRecordDecl *ZigClangFieldDecl_getParent(const
|
||||
ZIG_EXTERN_C unsigned ZigClangFieldDecl_getFieldIndex(const struct ZigClangFieldDecl *);
|
||||
|
||||
ZIG_EXTERN_C const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct ZigClangEnumConstantDecl *);
|
||||
ZIG_EXTERN_C bool ZigClangIsLLVMUsingSeparateLibcxx();
|
||||
#endif
|
||||
|
||||
@@ -53,7 +53,6 @@ test "implicit comptime switch" {
|
||||
}
|
||||
|
||||
test "switch on enum" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
|
||||
const fruit = Fruit.Orange;
|
||||
@@ -73,7 +72,6 @@ fn nonConstSwitchOnEnum(fruit: Fruit) void {
|
||||
}
|
||||
|
||||
test "switch statement" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
|
||||
try nonConstSwitch(SwitchStatementFoo.C);
|
||||
@@ -91,7 +89,6 @@ const SwitchStatementFoo = enum { A, B, C, D };
|
||||
|
||||
test "switch with multiple expressions" {
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
|
||||
const x = switch (returnsFive()) {
|
||||
@@ -120,7 +117,6 @@ fn trueIfBoolFalseOtherwise(comptime T: type) bool {
|
||||
}
|
||||
|
||||
test "switching on booleans" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
|
||||
try testSwitchOnBools();
|
||||
@@ -218,7 +214,6 @@ fn poll() void {
|
||||
}
|
||||
|
||||
test "switch on global mutable var isn't constant-folded" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
|
||||
while (state < 2) {
|
||||
@@ -278,7 +273,6 @@ fn testSwitchEnumPtrCapture() !void {
|
||||
|
||||
test "switch handles all cases of number" {
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
|
||||
try testSwitchHandleAllCases();
|
||||
@@ -370,7 +364,6 @@ test "anon enum literal used in switch on union enum" {
|
||||
}
|
||||
|
||||
test "switch all prongs unreachable" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
|
||||
try testAllProngsUnreachable();
|
||||
@@ -582,7 +575,6 @@ test "switch on pointer type" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
|
||||
const S = struct {
|
||||
const X = struct {
|
||||
@@ -674,7 +666,6 @@ test "capture of integer forwards the switch condition directly" {
|
||||
}
|
||||
|
||||
test "enum value without tag name used as switch item" {
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
|
||||
const E = enum(u32) {
|
||||
|
||||
@@ -255,3 +255,38 @@ test "initializing anon struct with mixed comptime-runtime fields" {
|
||||
var a: T = .{ .foo = -1234, .bar = x + 1 };
|
||||
_ = a;
|
||||
}
|
||||
|
||||
test "tuple in tuple passed to generic function" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
|
||||
const S = struct {
|
||||
fn pair(x: f32, y: f32) std.meta.Tuple(&.{ f32, f32 }) {
|
||||
return .{ x, y };
|
||||
}
|
||||
|
||||
fn foo(x: anytype) !void {
|
||||
try expect(x[0][0] == 1.5);
|
||||
try expect(x[0][1] == 2.5);
|
||||
}
|
||||
};
|
||||
const x = comptime S.pair(1.5, 2.5);
|
||||
try S.foo(.{x});
|
||||
}
|
||||
|
||||
test "coerce tuple to tuple" {
|
||||
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
|
||||
|
||||
const T = std.meta.Tuple(&.{u8});
|
||||
const S = struct {
|
||||
fn foo(x: T) !void {
|
||||
try expect(x[0] == 123);
|
||||
}
|
||||
};
|
||||
try S.foo(.{123});
|
||||
}
|
||||
|
||||
+3
-3
@@ -9,8 +9,8 @@ fn bad() !void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:4:21: error: comptime control flow inside runtime block
|
||||
// tmp.zig:4:15: note: runtime block created here
|
||||
// :4:21: error: comptime control flow inside runtime block
|
||||
// :4:15: note: runtime control flow here
|
||||
+3
-3
@@ -8,8 +8,8 @@ export fn entry() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:5:22: error: comptime control flow inside runtime block
|
||||
// tmp.zig:5:9: note: runtime block created here
|
||||
// :5:22: error: comptime control flow inside runtime block
|
||||
// :5:15: note: runtime control flow here
|
||||
+3
-3
@@ -8,8 +8,8 @@ export fn entry() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:5:20: error: comptime control flow inside runtime block
|
||||
// tmp.zig:5:9: note: runtime block created here
|
||||
// :5:20: error: comptime control flow inside runtime block
|
||||
// :5:13: note: runtime control flow here
|
||||
+3
-3
@@ -8,8 +8,8 @@ export fn entry() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:5:20: error: comptime control flow inside runtime block
|
||||
// tmp.zig:5:9: note: runtime block created here
|
||||
// :5:20: error: comptime control flow inside runtime block
|
||||
// :5:13: note: runtime control flow here
|
||||
@@ -0,0 +1,16 @@
|
||||
export fn entry() void {
|
||||
const ints = [_]u8{ 1, 2 };
|
||||
inline for (ints) |_| {
|
||||
bad() orelse continue;
|
||||
}
|
||||
}
|
||||
fn bad() ?void {
|
||||
return null;
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :4:22: error: comptime control flow inside runtime block
|
||||
// :4:15: note: runtime control flow here
|
||||
+3
-3
@@ -11,8 +11,8 @@ export fn entry() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:6:19: error: comptime control flow inside runtime block
|
||||
// tmp.zig:5:9: note: runtime block created here
|
||||
// :6:19: error: comptime control flow inside runtime block
|
||||
// :5:17: note: runtime control flow here
|
||||
+3
-3
@@ -8,8 +8,8 @@ export fn entry() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:5:25: error: comptime control flow inside runtime block
|
||||
// tmp.zig:5:9: note: runtime block created here
|
||||
// :5:25: error: comptime control flow inside runtime block
|
||||
// :5:18: note: runtime control flow here
|
||||
+3
-3
@@ -10,8 +10,8 @@ export fn entry() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:6:13: error: comptime control flow inside runtime block
|
||||
// tmp.zig:5:9: note: runtime block created here
|
||||
// :6:13: error: comptime control flow inside runtime block
|
||||
// :5:16: note: runtime control flow here
|
||||
+3
-3
@@ -8,8 +8,8 @@ export fn entry() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:5:23: error: comptime control flow inside runtime block
|
||||
// tmp.zig:5:9: note: runtime block created here
|
||||
// :5:23: error: comptime control flow inside runtime block
|
||||
// :5:16: note: runtime control flow here
|
||||
+15
-8
@@ -55,13 +55,20 @@ export fn foo_slice() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :4:29: error: slice-sentinel does not match memory at target index
|
||||
// :12:29: error: slice-sentinel does not match memory at target index
|
||||
// :20:29: error: slice-sentinel does not match memory at target index
|
||||
// :28:29: error: slice-sentinel does not match memory at target index
|
||||
// :36:29: error: slice-sentinel does not match memory at target index
|
||||
// :44:29: error: slice-sentinel does not match memory at target index
|
||||
// :52:29: error: slice-sentinel does not match memory at target index
|
||||
// :4:29: error: value in memory does not match slice sentinel
|
||||
// :4:29: note: expected '0', found '100'
|
||||
// :12:29: error: value in memory does not match slice sentinel
|
||||
// :12:29: note: expected '0', found '100'
|
||||
// :20:29: error: value in memory does not match slice sentinel
|
||||
// :20:29: note: expected '0', found '100'
|
||||
// :28:29: error: value in memory does not match slice sentinel
|
||||
// :28:29: note: expected '0', found '100'
|
||||
// :36:29: error: value in memory does not match slice sentinel
|
||||
// :36:29: note: expected '0', found '100'
|
||||
// :44:29: error: value in memory does not match slice sentinel
|
||||
// :44:29: note: expected '0', found '100'
|
||||
// :52:29: error: value in memory does not match slice sentinel
|
||||
// :52:29: note: expected '0', found '100'
|
||||
+15
-8
@@ -55,13 +55,20 @@ export fn foo_slice() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :4:29: error: slice-sentinel does not match memory at target index
|
||||
// :12:29: error: slice-sentinel does not match memory at target index
|
||||
// :20:29: error: slice-sentinel does not match memory at target index
|
||||
// :28:29: error: slice-sentinel does not match memory at target index
|
||||
// :36:29: error: slice-sentinel does not match memory at target index
|
||||
// :44:29: error: slice-sentinel does not match memory at target index
|
||||
// :52:29: error: slice-sentinel does not match memory at target index
|
||||
// :4:29: error: value in memory does not match slice sentinel
|
||||
// :4:29: note: expected '0', found '100'
|
||||
// :12:29: error: value in memory does not match slice sentinel
|
||||
// :12:29: note: expected '0', found '100'
|
||||
// :20:29: error: value in memory does not match slice sentinel
|
||||
// :20:29: note: expected '0', found '100'
|
||||
// :28:29: error: value in memory does not match slice sentinel
|
||||
// :28:29: note: expected '0', found '100'
|
||||
// :36:29: error: value in memory does not match slice sentinel
|
||||
// :36:29: note: expected '0', found '100'
|
||||
// :44:29: error: value in memory does not match slice sentinel
|
||||
// :44:29: note: expected '0', found '100'
|
||||
// :52:29: error: value in memory does not match slice sentinel
|
||||
// :52:29: note: expected '0', found '100'
|
||||
+15
-8
@@ -55,13 +55,20 @@ export fn foo_slice() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :4:29: error: slice-sentinel does not match target-sentinel
|
||||
// :12:29: error: slice-sentinel does not match target-sentinel
|
||||
// :20:29: error: slice-sentinel does not match target-sentinel
|
||||
// :28:29: error: slice-sentinel does not match target-sentinel
|
||||
// :36:29: error: slice-sentinel does not match target-sentinel
|
||||
// :44:29: error: slice-sentinel does not match target-sentinel
|
||||
// :52:29: error: slice-sentinel does not match target-sentinel
|
||||
// :4:29: error: value in memory does not match slice sentinel
|
||||
// :4:29: note: expected '255', found '0'
|
||||
// :12:29: error: value in memory does not match slice sentinel
|
||||
// :12:29: note: expected '255', found '0'
|
||||
// :20:29: error: value in memory does not match slice sentinel
|
||||
// :20:29: note: expected '255', found '0'
|
||||
// :28:29: error: value in memory does not match slice sentinel
|
||||
// :28:29: note: expected '255', found '0'
|
||||
// :36:29: error: value in memory does not match slice sentinel
|
||||
// :36:29: note: expected '255', found '0'
|
||||
// :44:29: error: value in memory does not match slice sentinel
|
||||
// :44:29: note: expected '255', found '0'
|
||||
// :52:29: error: value in memory does not match slice sentinel
|
||||
// :52:29: note: expected '255', found '0'
|
||||
+8
-8
@@ -55,13 +55,13 @@ export fn foo_slice() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :4:29: error: out of bounds slice
|
||||
// :12:29: error: out of bounds slice
|
||||
// :20:29: error: out of bounds slice
|
||||
// :28:29: error: out of bounds slice
|
||||
// :36:29: error: out of bounds slice
|
||||
// :44:29: error: out of bounds slice
|
||||
// :52:29: error: out of bounds slice
|
||||
// :4:33: error: slice end index 15 exceeds bounds of containing decl of type '[14:0]u8'
|
||||
// :12:33: error: slice end index 15 exceeds bounds of containing decl of type '[14:0]u8'
|
||||
// :20:33: error: slice end index 15 exceeds bounds of containing decl of type '[14:0]u8'
|
||||
// :28:33: error: slice end index 15 exceeds bounds of containing decl of type '[14:0]u8'
|
||||
// :36:33: error: slice end index 15 exceeds bounds of containing decl of type '[14:0]u8'
|
||||
// :44:33: error: slice end index 15 exceeds bounds of containing decl of type '[14:0]u8'
|
||||
// :52:33: error: end index 15 out of bounds for slice of length 14
|
||||
+8
-8
@@ -55,13 +55,13 @@ export fn foo_slice() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :4:29: error: slice-sentinel is out of bounds
|
||||
// :12:29: error: slice-sentinel is out of bounds
|
||||
// :20:29: error: slice-sentinel is out of bounds
|
||||
// :28:29: error: slice-sentinel is out of bounds
|
||||
// :36:29: error: slice-sentinel is out of bounds
|
||||
// :44:29: error: slice-sentinel is out of bounds
|
||||
// :52:29: error: slice-sentinel is out of bounds
|
||||
// :4:33: error: slice end index 14 exceeds bounds of containing decl of type '[14]u8'
|
||||
// :12:33: error: slice end index 14 exceeds bounds of containing decl of type '[14]u8'
|
||||
// :20:33: error: slice end index 14 exceeds bounds of containing decl of type '[14]u8'
|
||||
// :28:33: error: slice end index 14 exceeds bounds of containing decl of type '[14]u8'
|
||||
// :36:33: error: slice end index 14 exceeds bounds of containing decl of type '[14]u8'
|
||||
// :44:33: error: slice end index 14 exceeds bounds of containing decl of type '[14]u8'
|
||||
// :52:33: error: slice end index 14 exceeds bounds of containing decl of type '[14]u8'
|
||||
+2
-2
@@ -5,7 +5,7 @@ comptime {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:3:14: error: slice of undefined
|
||||
// :3:14: error: slice of undefined
|
||||
@@ -3,14 +3,14 @@ pub const List = struct {
|
||||
allocator: *Allocator,
|
||||
|
||||
pub fn init(allocator: *Allocator) List {
|
||||
return List {
|
||||
return List{
|
||||
.len = 0,
|
||||
.allocator = allocator,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub var global_allocator = Allocator {
|
||||
pub var global_allocator = Allocator{
|
||||
.field = 1234,
|
||||
};
|
||||
|
||||
@@ -28,4 +28,4 @@ export fn foo() void {
|
||||
// target=native
|
||||
//
|
||||
// :23:6: error: no field or member function named 'init' in 'tmp.List'
|
||||
// :1:14: note: struct declared here
|
||||
// :1:18: note: struct declared here
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ const Foo = @Type(.{
|
||||
comptime { _ = Foo; }
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:1:20: error: Type.Fn.is_generic must be false for @Type
|
||||
// :1:13: error: Type.Fn.is_generic must be false for @Type
|
||||
+2
-2
@@ -11,7 +11,7 @@ const Foo = @Type(.{
|
||||
comptime { _ = Foo; }
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:1:20: error: varargs functions must have C calling convention
|
||||
// :1:13: error: varargs functions must have C calling convention
|
||||
+2
-2
@@ -11,7 +11,7 @@ const Foo = @Type(.{
|
||||
comptime { _ = Foo; }
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:1:20: error: Type.Fn.return_type must be non-null for @Type
|
||||
// :1:13: error: Type.Fn.return_type must be non-null for @Type
|
||||
+2
-2
@@ -12,7 +12,7 @@ export fn entry() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:1:20: error: Type.Enum.tag_type must be an integer type, not 'bool'
|
||||
// :1:13: error: Type.Enum.tag_type must be an integer type
|
||||
+2
-2
@@ -12,7 +12,7 @@ export fn entry() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:1:20: error: use of undefined value here causes undefined behavior
|
||||
// :1:13: error: use of undefined value here causes undefined behavior
|
||||
+2
-2
@@ -12,7 +12,7 @@ export fn entry() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:1:20: error: enums must have 1 or more fields
|
||||
// :1:13: error: enums must have at least one field
|
||||
+4
-2
@@ -28,7 +28,9 @@ export fn entry() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:14:23: error: enum field missing: 'arst'
|
||||
// :14:16: error: enum field(s) missing in union
|
||||
// :1:13: note: field 'arst' missing, declared here
|
||||
// :1:13: note: enum declared here
|
||||
+3
-3
@@ -28,8 +28,8 @@ export fn entry() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:13:23: error: enum field not found: 'arst'
|
||||
// tmp.zig:1:20: note: enum declared here
|
||||
// :13:16: error: no field named 'arst' in enum 'tmp.Tag__enum_264'
|
||||
// :1:13: note: enum declared here
|
||||
+2
-2
@@ -11,7 +11,7 @@ export fn entry() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:1:25: error: unions must have 1 or more fields
|
||||
// :1:18: error: unions must have at least one field
|
||||
+2
-2
@@ -4,7 +4,7 @@ const Foo = @Type(.{
|
||||
comptime { _ = Foo; }
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:1:20: error: use of undefined value here causes undefined behavior
|
||||
// :1:13: error: use of undefined value here causes undefined behavior
|
||||
+4
-2
@@ -7,7 +7,9 @@ export fn entry() void {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:3:31: error: expected type 'std.builtin.Type', found 'std.builtin.Type.Int'
|
||||
// :3:31: error: expected type 'builtin.Type', found 'builtin.Type.Int'
|
||||
// :?:?: note: struct declared here
|
||||
// :?:?: note: union declared here
|
||||
+3
-3
@@ -13,8 +13,8 @@ comptime {
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// tmp.zig:2:16: error: use of undefined value here causes undefined behavior
|
||||
// tmp.zig:5:16: error: use of undefined value here causes undefined behavior
|
||||
// :2:9: error: use of undefined value here causes undefined behavior
|
||||
// :5:9: error: use of undefined value here causes undefined behavior
|
||||
@@ -1,11 +0,0 @@
|
||||
export fn entry() void {
|
||||
var x = .{};
|
||||
x = x ++ .{ 1, 2, 3 };
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage1
|
||||
// target=native
|
||||
// is_test=1
|
||||
//
|
||||
// tmp.zig:3:11: error: expected type 'struct:2:14', found 'struct:3:11'
|
||||
@@ -0,0 +1,44 @@
|
||||
pub export fn entry1() void {
|
||||
const T = @TypeOf(.{ 123, 3 });
|
||||
var b = T{ .@"1" = 3 }; _ = b;
|
||||
var c = T{ 123, 3 }; _ = c;
|
||||
var d = T{}; _ = d;
|
||||
}
|
||||
pub export fn entry2() void {
|
||||
var a: u32 = 2;
|
||||
const T = @TypeOf(.{ 123, a });
|
||||
var b = T{ .@"1" = 3 }; _ = b;
|
||||
var c = T{ 123, 3 }; _ = c;
|
||||
var d = T{}; _ = d;
|
||||
}
|
||||
pub export fn entry3() void {
|
||||
var a: u32 = 2;
|
||||
const T = @TypeOf(.{ 123, a });
|
||||
var b = T{ .@"0" = 123 }; _ = b;
|
||||
}
|
||||
comptime {
|
||||
var a: u32 = 2;
|
||||
const T = @TypeOf(.{ 123, a });
|
||||
var b = T{ .@"0" = 123 }; _ = b;
|
||||
var c = T{ 123, 2 }; _ = c;
|
||||
var d = T{}; _ = d;
|
||||
}
|
||||
pub export fn entry4() void {
|
||||
var a: u32 = 2;
|
||||
const T = @TypeOf(.{ 123, a });
|
||||
var b = T{ 123, 4, 5 }; _ = b;
|
||||
}
|
||||
pub export fn entry5() void {
|
||||
var a: u32 = 2;
|
||||
const T = @TypeOf(.{ 123, a });
|
||||
var b = T{ .@"0" = 123, .@"2" = 123, .@"1" = 123 }; _ = b;
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :12:14: error: missing tuple field with index 1
|
||||
// :17:14: error: missing tuple field with index 1
|
||||
// :29:14: error: expected at most 2 tuple fields; found 3
|
||||
// :34:30: error: index '2' out of bounds of tuple 'tuple{comptime comptime_int = 123, u32}'
|
||||
@@ -0,0 +1,10 @@
|
||||
export fn entry() void {
|
||||
var x = .{};
|
||||
x = x ++ .{ 1, 2, 3 };
|
||||
}
|
||||
|
||||
// error
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :3:11: error: index '0' out of bounds of tuple '@TypeOf(.{})'
|
||||
@@ -7,4 +7,4 @@ comptime {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :2:31: error: index 2 outside array of length 2
|
||||
// :2:24: error: expected 2 array elements; found 3
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
pub fn main() void {
|
||||
var i: u32 = 16;
|
||||
assert(i >> 1, 8);
|
||||
}
|
||||
fn assert(a: u32, b: u32) void {
|
||||
if (a != b) unreachable;
|
||||
}
|
||||
|
||||
// run
|
||||
// backend=llvm
|
||||
// target=x86_64-linux,x86_64-macos
|
||||
//
|
||||
@@ -1,10 +0,0 @@
|
||||
pub fn main() void {
|
||||
var i: u32 = 16;
|
||||
assert(i << 1, 32);
|
||||
}
|
||||
fn assert(a: u32, b: u32) void {
|
||||
if (a != b) unreachable;
|
||||
}
|
||||
|
||||
// run
|
||||
//
|
||||
+22
-22
@@ -1322,28 +1322,28 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
|
||||
\\}
|
||||
, "");
|
||||
|
||||
if (@import("builtin").zig_backend == .stage1) {
|
||||
// https://github.com/ziglang/zig/issues/12264
|
||||
cases.add("basic vector expressions",
|
||||
\\#include <stdlib.h>
|
||||
\\#include <stdint.h>
|
||||
\\typedef int16_t __v8hi __attribute__((__vector_size__(16)));
|
||||
\\int main(int argc, char**argv) {
|
||||
\\ __v8hi uninitialized;
|
||||
\\ __v8hi empty_init = {};
|
||||
\\ __v8hi partial_init = {0, 1, 2, 3};
|
||||
\\
|
||||
\\ __v8hi a = {0, 1, 2, 3, 4, 5, 6, 7};
|
||||
\\ __v8hi b = (__v8hi) {100, 200, 300, 400, 500, 600, 700, 800};
|
||||
\\
|
||||
\\ __v8hi sum = a + b;
|
||||
\\ for (int i = 0; i < 8; i++) {
|
||||
\\ if (sum[i] != a[i] + b[i]) abort();
|
||||
\\ }
|
||||
\\ return 0;
|
||||
\\}
|
||||
, "");
|
||||
}
|
||||
cases.add("basic vector expressions",
|
||||
\\#include <stdlib.h>
|
||||
\\#include <stdint.h>
|
||||
\\typedef int16_t __v8hi __attribute__((__vector_size__(16)));
|
||||
\\int main(int argc, char**argv) {
|
||||
\\ __v8hi uninitialized;
|
||||
\\ __v8hi empty_init = {};
|
||||
\\ for (int i = 0; i < 8; i++) {
|
||||
\\ if (empty_init[i] != 0) abort();
|
||||
\\ }
|
||||
\\ __v8hi partial_init = {0, 1, 2, 3};
|
||||
\\
|
||||
\\ __v8hi a = {0, 1, 2, 3, 4, 5, 6, 7};
|
||||
\\ __v8hi b = (__v8hi) {100, 200, 300, 400, 500, 600, 700, 800};
|
||||
\\
|
||||
\\ __v8hi sum = a + b;
|
||||
\\ for (int i = 0; i < 8; i++) {
|
||||
\\ if (sum[i] != a[i] + b[i]) abort();
|
||||
\\ }
|
||||
\\ return 0;
|
||||
\\}
|
||||
, "");
|
||||
|
||||
cases.add("__builtin_shufflevector",
|
||||
\\#include <stdlib.h>
|
||||
|
||||
@@ -793,7 +793,7 @@ const llvm_targets = [_]LlvmTarget{
|
||||
.td_name = "Sparc.td",
|
||||
},
|
||||
.{
|
||||
.zig_name = "systemz",
|
||||
.zig_name = "s390x",
|
||||
.llvm_name = "SystemZ",
|
||||
.td_name = "SystemZ.td",
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user