diff --git a/CMakeLists.txt b/CMakeLists.txt index 86cbdd6dad..506115214e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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" diff --git a/build.zig b/build.zig index 3e7d1888cd..0ff5285201 100644 --- a/build.zig +++ b/build.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, diff --git a/ci/zinc/linux_test.sh b/ci/zinc/linux_test.sh index 4c3b3842bf..3a54e82c38 100755 --- a/ci/zinc/linux_test.sh +++ b/ci/zinc/linux_test.sh @@ -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 diff --git a/cmake/Findllvm.cmake b/cmake/Findllvm.cmake index d6535927d1..d5de1eb822 100644 --- a/cmake/Findllvm.cmake +++ b/cmake/Findllvm.cmake @@ -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}") diff --git a/lib/std/debug.zig b/lib/std/debug.zig index b406fed471..7d0dcd35d0 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -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(); } diff --git a/lib/std/os.zig b/lib/std/os.zig index 5683c5300a..1192c72629 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -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), } } diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index aa127db8ed..ae9b441b60 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -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; diff --git a/lib/std/target.zig b/lib/std/target.zig index f026b0da21..d15eae7dc5 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -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, diff --git a/lib/std/target/systemz.zig b/lib/std/target/s390x.zig similarity index 100% rename from lib/std/target/systemz.zig rename to lib/std/target/s390x.zig diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index f3c219cfc6..2a7d2623ef 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -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, diff --git a/lib/std/zig/parser_test.zig b/lib/std/zig/parser_test.zig index 38c2960f31..a74d53f21c 100644 --- a/lib/std/zig/parser_test.zig +++ b/lib/std/zig/parser_test.zig @@ -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; diff --git a/src/Air.zig b/src/Air.zig index 2c0c38a2ef..302822fc99 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -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. diff --git a/src/AstGen.zig b/src/AstGen.zig index 528ef930e6..b6a7450f3a 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -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; diff --git a/src/Compilation.zig b/src/Compilation.zig index 869cd43f0f..17ffe356a3 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -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 ".", diff --git a/src/Module.zig b/src/Module.zig index 4576538a35..4ac2775515 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -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); } diff --git a/src/Sema.zig b/src/Sema.zig index ad8beff0f3..a0829d6eb7 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -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 }; } } diff --git a/src/TypedValue.zig b/src/TypedValue.zig index 21d8629061..ba32e55f1e 100644 --- a/src/TypedValue.zig +++ b/src/TypedValue.zig @@ -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); diff --git a/src/Zir.zig b/src/Zir.zig index 6e9b133310..ccd677df0b 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -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 }; diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 8bd589e7ba..93d98c41d3 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -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 { diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig index fd9e13a220..91072d0b4c 100644 --- a/src/arch/wasm/CodeGen.zig +++ b/src/arch/wasm/CodeGen.zig @@ -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 { diff --git a/src/clang.zig b/src/clang.zig index ef90ddebce..40dacc5df8 100644 --- a/src/clang.zig +++ b/src/clang.zig @@ -1913,3 +1913,6 @@ extern fn ZigClangLoadFromCommandLine( errors_len: *usize, resources_path: [*:0]const u8, ) ?*ASTUnit; + +pub const isLLVMUsingSeparateLibcxx = ZigClangIsLLVMUsingSeparateLibcxx; +extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool; diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index 6a2e346113..664edb0304 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -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; } diff --git a/src/link/Elf.zig b/src/link/Elf.zig index 3e9b1ab3ed..917e4c18d1 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -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| { diff --git a/src/main.zig b/src/main.zig index bd86cd2bb9..f192137b3c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -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; diff --git a/src/print_zir.zig b/src/print_zir.zig index de51c271c4..6e33154bbd 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -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, diff --git a/src/stage1/parser.hpp b/src/stage1/parser.hpp index 8ac8ce6de1..065f951e91 100644 --- a/src/stage1/parser.hpp +++ b/src/stage1/parser.hpp @@ -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); diff --git a/src/translate_c.zig b/src/translate_c.zig index e53342990e..97e47d84f3 100644 --- a/src/translate_c.zig +++ b/src/translate_c.zig @@ -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, .{ diff --git a/src/translate_c/ast.zig b/src/translate_c/ast.zig index b88b481ae7..307a7e9ea7 100644 --- a/src/translate_c/ast.zig +++ b/src/translate_c/ast.zig @@ -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, diff --git a/src/type.zig b/src/type.zig index 6750ec724b..3b1b0dd59d 100644 --- a/src/type.zig +++ b/src/type.zig @@ -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; diff --git a/src/value.zig b/src/value.zig index 46624a822d..3994040ba6 100644 --- a/src/value.zig +++ b/src/value.zig @@ -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 { diff --git a/src/zig_clang.cpp b/src/zig_clang.cpp index 1c0ff7bf09..7b79f8e985 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -3432,3 +3432,31 @@ const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct Zi const llvm::APSInt *result = &casted->getInitVal(); return reinterpret_cast(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(); +} + diff --git a/src/zig_clang.h b/src/zig_clang.h index 6babb21bfe..3da57d4301 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -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 diff --git a/test/behavior/switch.zig b/test/behavior/switch.zig index 3a49c03b18..4e86bcadeb 100644 --- a/test/behavior/switch.zig +++ b/test/behavior/switch.zig @@ -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) { diff --git a/test/behavior/tuple.zig b/test/behavior/tuple.zig index 2442ae3629..14297bd61c 100644 --- a/test/behavior/tuple.zig +++ b/test/behavior/tuple.zig @@ -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}); +} diff --git a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig b/test/cases/compile_errors/comptime_continue_inside_runtime_catch.zig similarity index 58% rename from test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig rename to test/cases/compile_errors/comptime_continue_inside_runtime_catch.zig index 7eefeb80b4..9e62420f1f 100644 --- a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_catch.zig +++ b/test/cases/compile_errors/comptime_continue_inside_runtime_catch.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig b/test/cases/compile_errors/comptime_continue_inside_runtime_if_bool.zig similarity index 59% rename from test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig rename to test/cases/compile_errors/comptime_continue_inside_runtime_if_bool.zig index 9ace5ddceb..b2a7312c52 100644 --- a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_bool.zig +++ b/test/cases/compile_errors/comptime_continue_inside_runtime_if_bool.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig b/test/cases/compile_errors/comptime_continue_inside_runtime_if_error.zig similarity index 60% rename from test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig rename to test/cases/compile_errors/comptime_continue_inside_runtime_if_error.zig index 554ba3c43e..194274a1ed 100644 --- a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_error.zig +++ b/test/cases/compile_errors/comptime_continue_inside_runtime_if_error.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig b/test/cases/compile_errors/comptime_continue_inside_runtime_if_optional.zig similarity index 58% rename from test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig rename to test/cases/compile_errors/comptime_continue_inside_runtime_if_optional.zig index 32c71e5c77..965454ef03 100644 --- a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_if_optional.zig +++ b/test/cases/compile_errors/comptime_continue_inside_runtime_if_optional.zig @@ -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 diff --git a/test/cases/compile_errors/comptime_continue_inside_runtime_orelse.zig b/test/cases/compile_errors/comptime_continue_inside_runtime_orelse.zig new file mode 100644 index 0000000000..56b65c1ab7 --- /dev/null +++ b/test/cases/compile_errors/comptime_continue_inside_runtime_orelse.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig b/test/cases/compile_errors/comptime_continue_inside_runtime_switch.zig similarity index 64% rename from test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig rename to test/cases/compile_errors/comptime_continue_inside_runtime_switch.zig index d145897b41..391ecbdf1a 100644 --- a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_switch.zig +++ b/test/cases/compile_errors/comptime_continue_inside_runtime_switch.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig b/test/cases/compile_errors/comptime_continue_inside_runtime_while_bool.zig similarity index 61% rename from test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig rename to test/cases/compile_errors/comptime_continue_inside_runtime_while_bool.zig index 8e57854728..54d62e6d37 100644 --- a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_bool.zig +++ b/test/cases/compile_errors/comptime_continue_inside_runtime_while_bool.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig b/test/cases/compile_errors/comptime_continue_inside_runtime_while_error.zig similarity index 65% rename from test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig rename to test/cases/compile_errors/comptime_continue_inside_runtime_while_error.zig index 818455c354..0eef1c3374 100644 --- a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_error.zig +++ b/test/cases/compile_errors/comptime_continue_inside_runtime_while_error.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig b/test/cases/compile_errors/comptime_continue_inside_runtime_while_optional.zig similarity index 60% rename from test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig rename to test/cases/compile_errors/comptime_continue_inside_runtime_while_optional.zig index ed22cc2cac..e6753a5911 100644 --- a/test/cases/compile_errors/stage1/obj/comptime_continue_inside_runtime_while_optional.zig +++ b/test/cases/compile_errors/comptime_continue_inside_runtime_while_optional.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig b/test/cases/compile_errors/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig similarity index 68% rename from test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig rename to test/cases/compile_errors/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig index 598d23a305..ffa21af10a 100644 --- a/test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig +++ b/test/cases/compile_errors/comptime_slice-sentinel_does_not_match_memory_at_target_index_terminated.zig @@ -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' diff --git a/test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig b/test/cases/compile_errors/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig similarity index 68% rename from test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig rename to test/cases/compile_errors/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig index d6b469aaf1..c5bb2d9643 100644 --- a/test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig +++ b/test/cases/compile_errors/comptime_slice-sentinel_does_not_match_memory_at_target_index_unterminated.zig @@ -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' diff --git a/test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_target-sentinel.zig b/test/cases/compile_errors/comptime_slice-sentinel_does_not_match_target-sentinel.zig similarity index 68% rename from test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_target-sentinel.zig rename to test/cases/compile_errors/comptime_slice-sentinel_does_not_match_target-sentinel.zig index b204cfc684..b574df8833 100644 --- a/test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_does_not_match_target-sentinel.zig +++ b/test/cases/compile_errors/comptime_slice-sentinel_does_not_match_target-sentinel.zig @@ -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' diff --git a/test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_terminated.zig b/test/cases/compile_errors/comptime_slice-sentinel_is_out_of_bounds_terminated.zig similarity index 72% rename from test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_terminated.zig rename to test/cases/compile_errors/comptime_slice-sentinel_is_out_of_bounds_terminated.zig index 82c19126c0..86bd4ce8bb 100644 --- a/test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_terminated.zig +++ b/test/cases/compile_errors/comptime_slice-sentinel_is_out_of_bounds_terminated.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig b/test/cases/compile_errors/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig similarity index 72% rename from test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig rename to test/cases/compile_errors/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig index 952b17600a..e1b8a5bc2d 100644 --- a/test/cases/compile_errors/stage1/obj/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig +++ b/test/cases/compile_errors/comptime_slice-sentinel_is_out_of_bounds_unterminated.zig @@ -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' diff --git a/test/cases/compile_errors/stage1/obj/comptime_slice_of_an_undefined_slice.zig b/test/cases/compile_errors/comptime_slice_of_an_undefined_slice.zig similarity index 63% rename from test/cases/compile_errors/stage1/obj/comptime_slice_of_an_undefined_slice.zig rename to test/cases/compile_errors/comptime_slice_of_an_undefined_slice.zig index 4aa519f41e..d1b22d86b7 100644 --- a/test/cases/compile_errors/stage1/obj/comptime_slice_of_an_undefined_slice.zig +++ b/test/cases/compile_errors/comptime_slice_of_an_undefined_slice.zig @@ -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 diff --git a/test/cases/compile_errors/method_call_with_first_arg_type_wrong_container.zig b/test/cases/compile_errors/method_call_with_first_arg_type_wrong_container.zig index 9e05a370f9..ad481a6158 100644 --- a/test/cases/compile_errors/method_call_with_first_arg_type_wrong_container.zig +++ b/test/cases/compile_errors/method_call_with_first_arg_type_wrong_container.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/reify_type.Fn_with_is_generic_true.zig b/test/cases/compile_errors/reify_type.Fn_with_is_generic_true.zig similarity index 76% rename from test/cases/compile_errors/stage1/obj/reify_type.Fn_with_is_generic_true.zig rename to test/cases/compile_errors/reify_type.Fn_with_is_generic_true.zig index f2849b5eb4..cf80c9f4ba 100644 --- a/test/cases/compile_errors/stage1/obj/reify_type.Fn_with_is_generic_true.zig +++ b/test/cases/compile_errors/reify_type.Fn_with_is_generic_true.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig b/test/cases/compile_errors/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig similarity index 74% rename from test/cases/compile_errors/stage1/obj/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig rename to test/cases/compile_errors/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig index 4d449e9eb9..8328ee9b97 100644 --- a/test/cases/compile_errors/stage1/obj/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig +++ b/test/cases/compile_errors/reify_type.Fn_with_is_var_args_true_and_non-C_callconv.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/reify_type.Fn_with_return_type_null.zig b/test/cases/compile_errors/reify_type.Fn_with_return_type_null.zig similarity index 75% rename from test/cases/compile_errors/stage1/obj/reify_type.Fn_with_return_type_null.zig rename to test/cases/compile_errors/reify_type.Fn_with_return_type_null.zig index 98cbc37d41..f6587dcd7e 100644 --- a/test/cases/compile_errors/stage1/obj/reify_type.Fn_with_return_type_null.zig +++ b/test/cases/compile_errors/reify_type.Fn_with_return_type_null.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig b/test/cases/compile_errors/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig similarity index 72% rename from test/cases/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig rename to test/cases/compile_errors/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig index 56d05578be..e72b783d83 100644 --- a/test/cases/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig +++ b/test/cases/compile_errors/reify_type_for_exhaustive_enum_with_non-integer_tag_type.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig b/test/cases/compile_errors/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig similarity index 73% rename from test/cases/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig rename to test/cases/compile_errors/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig index e6454d2ee5..1c237a17bd 100644 --- a/test/cases/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig +++ b/test/cases/compile_errors/reify_type_for_exhaustive_enum_with_undefined_tag_type.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_zero_fields.zig b/test/cases/compile_errors/reify_type_for_exhaustive_enum_with_zero_fields.zig similarity index 77% rename from test/cases/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_zero_fields.zig rename to test/cases/compile_errors/reify_type_for_exhaustive_enum_with_zero_fields.zig index d3ce70c1b0..44876e938a 100644 --- a/test/cases/compile_errors/stage1/obj/reify_type_for_exhaustive_enum_with_zero_fields.zig +++ b/test/cases/compile_errors/reify_type_for_exhaustive_enum_with_zero_fields.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_enum_field.zig b/test/cases/compile_errors/reify_type_for_tagged_union_with_extra_enum_field.zig similarity index 83% rename from test/cases/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_enum_field.zig rename to test/cases/compile_errors/reify_type_for_tagged_union_with_extra_enum_field.zig index 0c56cb91ea..ccd0000494 100644 --- a/test/cases/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_enum_field.zig +++ b/test/cases/compile_errors/reify_type_for_tagged_union_with_extra_enum_field.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_union_field.zig b/test/cases/compile_errors/reify_type_for_tagged_union_with_extra_union_field.zig similarity index 87% rename from test/cases/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_union_field.zig rename to test/cases/compile_errors/reify_type_for_tagged_union_with_extra_union_field.zig index 63cf1f178e..414bf2ce5e 100644 --- a/test/cases/compile_errors/stage1/obj/reify_type_for_tagged_union_with_extra_union_field.zig +++ b/test/cases/compile_errors/reify_type_for_tagged_union_with_extra_union_field.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/reify_type_for_union_with_zero_fields.zig b/test/cases/compile_errors/reify_type_for_union_with_zero_fields.zig similarity index 75% rename from test/cases/compile_errors/stage1/obj/reify_type_for_union_with_zero_fields.zig rename to test/cases/compile_errors/reify_type_for_union_with_zero_fields.zig index 578f902697..0b4f395c81 100644 --- a/test/cases/compile_errors/stage1/obj/reify_type_for_union_with_zero_fields.zig +++ b/test/cases/compile_errors/reify_type_for_union_with_zero_fields.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/reify_type_union_payload_is_undefined.zig b/test/cases/compile_errors/reify_type_union_payload_is_undefined.zig similarity index 51% rename from test/cases/compile_errors/stage1/obj/reify_type_union_payload_is_undefined.zig rename to test/cases/compile_errors/reify_type_union_payload_is_undefined.zig index 47be31c711..410bb92658 100644 --- a/test/cases/compile_errors/stage1/obj/reify_type_union_payload_is_undefined.zig +++ b/test/cases/compile_errors/reify_type_union_payload_is_undefined.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/reify_type_with_Type.Int.zig b/test/cases/compile_errors/reify_type_with_Type.Int.zig similarity index 53% rename from test/cases/compile_errors/stage1/obj/reify_type_with_Type.Int.zig rename to test/cases/compile_errors/reify_type_with_Type.Int.zig index 116bd86e0f..bd98912a03 100644 --- a/test/cases/compile_errors/stage1/obj/reify_type_with_Type.Int.zig +++ b/test/cases/compile_errors/reify_type_with_Type.Int.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/reify_type_with_undefined.zig b/test/cases/compile_errors/reify_type_with_undefined.zig similarity index 64% rename from test/cases/compile_errors/stage1/obj/reify_type_with_undefined.zig rename to test/cases/compile_errors/reify_type_with_undefined.zig index 1de93ccdf6..e5753fa420 100644 --- a/test/cases/compile_errors/stage1/obj/reify_type_with_undefined.zig +++ b/test/cases/compile_errors/reify_type_with_undefined.zig @@ -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 diff --git a/test/cases/compile_errors/stage1/obj/reify_type.Pointer_with_invalid_address_space.zig b/test/cases/compile_errors/stage1/reify_type.Pointer_with_invalid_address_space.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/reify_type.Pointer_with_invalid_address_space.zig rename to test/cases/compile_errors/stage1/reify_type.Pointer_with_invalid_address_space.zig diff --git a/test/cases/compile_errors/stage1/obj/reify_type_with_non-constant_expression.zig b/test/cases/compile_errors/stage1/reify_type_with_non-constant_expression.zig similarity index 100% rename from test/cases/compile_errors/stage1/obj/reify_type_with_non-constant_expression.zig rename to test/cases/compile_errors/stage1/reify_type_with_non-constant_expression.zig diff --git a/test/cases/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig b/test/cases/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig deleted file mode 100644 index 5983224676..0000000000 --- a/test/cases/compile_errors/stage1/test/type_mismatch_with_tuple_concatenation.zig +++ /dev/null @@ -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' diff --git a/test/cases/compile_errors/tuple_init_edge_cases.zig b/test/cases/compile_errors/tuple_init_edge_cases.zig new file mode 100644 index 0000000000..32b52cdc1f --- /dev/null +++ b/test/cases/compile_errors/tuple_init_edge_cases.zig @@ -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}' diff --git a/test/cases/compile_errors/type_mismatch_with_tuple_concatenation.zig b/test/cases/compile_errors/type_mismatch_with_tuple_concatenation.zig new file mode 100644 index 0000000000..4e9bdfa2e5 --- /dev/null +++ b/test/cases/compile_errors/type_mismatch_with_tuple_concatenation.zig @@ -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(.{})' diff --git a/test/cases/compile_errors/wrong_size_to_an_array_literal.zig b/test/cases/compile_errors/wrong_size_to_an_array_literal.zig index bb8b3c215c..a38d8d4d85 100644 --- a/test/cases/compile_errors/wrong_size_to_an_array_literal.zig +++ b/test/cases/compile_errors/wrong_size_to_an_array_literal.zig @@ -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 diff --git a/test/cases/llvm/shift_right_plus_left.0.zig b/test/cases/llvm/shift_right_plus_left.0.zig deleted file mode 100644 index 23b733c493..0000000000 --- a/test/cases/llvm/shift_right_plus_left.0.zig +++ /dev/null @@ -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 -// diff --git a/test/cases/llvm/shift_right_plus_left.1.zig b/test/cases/llvm/shift_right_plus_left.1.zig deleted file mode 100644 index 994b67b9d0..0000000000 --- a/test/cases/llvm/shift_right_plus_left.1.zig +++ /dev/null @@ -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 -// diff --git a/test/run_translated_c.zig b/test/run_translated_c.zig index 871ec98fbc..4345625dc1 100644 --- a/test/run_translated_c.zig +++ b/test/run_translated_c.zig @@ -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 - \\#include - \\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 + \\#include + \\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 diff --git a/tools/update_cpu_features.zig b/tools/update_cpu_features.zig index 3757b56743..c43799d280 100644 --- a/tools/update_cpu_features.zig +++ b/tools/update_cpu_features.zig @@ -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", },