Merge remote-tracking branch 'origin/master' into llvm15

This commit is contained in:
Andrew Kelley 2022-07-31 15:55:44 -07:00
commit d46446e4df
72 changed files with 1876 additions and 715 deletions

View File

@ -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"

View File

@ -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.
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);
}
}
var check_dir = fs.path.dirname(b.zig_exe).?;
while (true) {
var dir = fs.cwd().openDir(check_dir, .{}) catch unreachable;
defer dir.close();
break :blk dir.readFileAlloc(b.allocator, "config.h", max_config_h_bytes) catch |err| switch (err) {
error.FileNotFound => {
// 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.?;
continue;
},
else => unreachable,
};
} 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,

View File

@ -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

View File

@ -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}")

View File

@ -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();
}

View File

@ -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),
}
}

View File

@ -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;

View File

@ -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,

View File

@ -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,

View File

@ -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;

View File

@ -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.

View File

@ -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;

View File

@ -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 ".",

View File

@ -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);
}

File diff suppressed because it is too large Load Diff

View File

@ -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,10 +158,13 @@ pub fn print(
}
try print(.{
.ty = ty.structFieldType(i),
.val = ty.structFieldValueComptime(i) orelse b: {
.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);
}
if (ty.structFieldCount() > max_aggregate_items) {
@ -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);

View File

@ -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 };

View File

@ -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 {

View File

@ -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 {

View File

@ -1913,3 +1913,6 @@ extern fn ZigClangLoadFromCommandLine(
errors_len: *usize,
resources_path: [*:0]const u8,
) ?*ASTUnit;
pub const isLLVMUsingSeparateLibcxx = ZigClangIsLLVMUsingSeparateLibcxx;
extern fn ZigClangIsLLVMUsingSeparateLibcxx() bool;

View File

@ -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);
const output_inst = try self.resolveInst(output);
if (llvm_ret_indirect[i]) {
// Pass the result by reference as an indirect output (e.g. "=*m")
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;
} 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;
}

View File

@ -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| {

View File

@ -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;

View File

@ -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,

View File

@ -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);

View File

@ -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| {
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, .{

View File

@ -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,

View File

@ -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;

View File

@ -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 {

View File

@ -3432,3 +3432,31 @@ const struct ZigClangAPSInt *ZigClangEnumConstantDecl_getInitVal(const struct Zi
const llvm::APSInt *result = &casted->getInitVal();
return reinterpret_cast<const ZigClangAPSInt *>(result);
}
// Get a pointer to a static variable in libc++ from LLVM and make sure that
// it matches our own.
//
// This check is needed because if static/dynamic linking is mixed incorrectly,
// it's possible for Clang and LLVM to end up with duplicate "copies" of libc++.
//
// This is not benign: Static variables are not shared, so equality comparisons
// that depend on pointers to static variables will fail. One such failure is
// std::generic_category(), which causes POSIX error codes to compare as unequal
// when passed between LLVM and Clang.
//
// See also: https://github.com/ziglang/zig/issues/11168
bool ZigClangIsLLVMUsingSeparateLibcxx() {
// Temporarily create an InMemoryFileSystem, so that we can perform a file
// lookup that is guaranteed to fail.
auto FS = new llvm::vfs::InMemoryFileSystem(true);
auto StatusOrErr = FS->status("foo.txt");
delete FS;
// This should return a POSIX (generic_category) error code, but if LLVM has
// its own copy of libc++ this will actually be a separate category instance.
assert(!StatusOrErr);
auto EC = StatusOrErr.getError();
return EC.category() != std::generic_category();
}

View File

@ -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

View File

@ -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) {

View File

@ -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});
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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'

View File

@ -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'

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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}'

View File

@ -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(.{})'

View File

@ -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

View File

@ -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
//

View File

@ -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
//

View File

@ -1322,8 +1322,6 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
\\}
, "");
if (@import("builtin").zig_backend == .stage1) {
// https://github.com/ziglang/zig/issues/12264
cases.add("basic vector expressions",
\\#include <stdlib.h>
\\#include <stdint.h>
@ -1331,6 +1329,9 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
\\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};
@ -1343,7 +1344,6 @@ pub fn addCases(cases: *tests.RunTranslatedCContext) void {
\\ return 0;
\\}
, "");
}
cases.add("__builtin_shufflevector",
\\#include <stdlib.h>

View File

@ -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",
},