Commit 6fbe1632d0
Changed files (10)
test
cases
src/ir.cpp
@@ -57,6 +57,7 @@ static TypeTableEntry *ir_analyze_instruction(IrAnalyze *ira, IrInstruction *ins
static IrInstruction *ir_implicit_cast(IrAnalyze *ira, IrInstruction *value, TypeTableEntry *expected_type);
ConstExprValue *const_ptr_pointee(ConstExprValue *const_val) {
+ assert(const_val->type->id == TypeTableEntryIdPointer);
assert(const_val->special == ConstValSpecialStatic);
switch (const_val->data.x_ptr.special) {
case ConstPtrSpecialInvalid:
@@ -10350,10 +10351,21 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira,
if (type_is_invalid(target_value_ptr->value.type))
return ira->codegen->builtin_types.entry_invalid;
+ if (target_value_ptr->value.type->id == TypeTableEntryIdMetaType) {
+ assert(instr_is_comptime(target_value_ptr));
+ TypeTableEntry *ptr_type = target_value_ptr->value.data.x_type;
+ assert(ptr_type->id == TypeTableEntryIdPointer);
+ ConstExprValue *out_val = ir_build_const_from(ira, &switch_target_instruction->base);
+ out_val->type = ira->codegen->builtin_types.entry_type;
+ out_val->data.x_type = ptr_type->data.pointer.child_type;
+ return out_val->type;
+ }
+
assert(target_value_ptr->value.type->id == TypeTableEntryIdPointer);
+
TypeTableEntry *target_type = target_value_ptr->value.type->data.pointer.child_type;
ConstExprValue *pointee_val = nullptr;
- if (target_value_ptr->value.special != ConstValSpecialRuntime) {
+ if (instr_is_comptime(target_value_ptr)) {
pointee_val = const_ptr_pointee(&target_value_ptr->value);
if (pointee_val->special == ConstValSpecialRuntime)
pointee_val = nullptr;
src/main.cpp
@@ -167,9 +167,8 @@ int main(int argc, char **argv) {
ZigList<const char *> args = {0};
args.append(zig_exe_path);
for (int i = 2; i < argc; i += 1) {
- if (strcmp(argv[i], "--verbose") == 0) {
+ if (strcmp(argv[i], "--debug-build-verbose") == 0) {
verbose = true;
- args.append(argv[i]);
} else {
args.append(argv[i]);
}
std/special/build_runner.zig
@@ -1,26 +1,106 @@
const root = @import("@build");
const std = @import("std");
const io = std.io;
+const fmt = std.fmt;
const os = std.os;
const Builder = std.build.Builder;
const mem = std.mem;
+const List = std.list.List;
error InvalidArgs;
pub fn main() -> %void {
- if (os.args.count() < 2) {
- %%io.stderr.printf("Expected first argument to be path to zig compiler\n");
- return error.InvalidArgs;
- }
- const zig_exe = os.args.at(1);
- const leftover_arg_index = 2;
-
// TODO use a more general purpose allocator here
var inc_allocator = %%mem.IncrementingAllocator.init(10 * 1024 * 1024);
defer inc_allocator.deinit();
- var builder = Builder.init(zig_exe, &inc_allocator.allocator);
+ const allocator = &inc_allocator.allocator;
+
+ var builder = Builder.init(allocator);
defer builder.deinit();
+
+ var maybe_zig_exe: ?[]const u8 = null;
+ var targets = List([]const u8).init(allocator);
+
+ var arg_i: usize = 1;
+ while (arg_i < os.args.count(); arg_i += 1) {
+ const arg = os.args.at(arg_i);
+ if (mem.startsWith(u8, arg, "-O")) {
+ const option_contents = arg[2...];
+ if (option_contents.len == 0) {
+ %%io.stderr.printf("Expected option name after '-O'\n\n");
+ return usage(&builder, maybe_zig_exe, false, &io.stderr);
+ }
+ if (const name_end ?= mem.indexOfScalar(u8, option_contents, '=')) {
+ const option_name = option_contents[0...name_end];
+ const option_value = option_contents[name_end...];
+ if (builder.addUserInputOption(option_name, option_value))
+ return usage(&builder, maybe_zig_exe, false, &io.stderr);
+ } else {
+ if (builder.addUserInputFlag(option_contents))
+ return usage(&builder, maybe_zig_exe, false, &io.stderr);
+ }
+ } else if (mem.startsWith(u8, arg, "-")) {
+ if (mem.eql(u8, arg, "--verbose")) {
+ builder.verbose = true;
+ } else if (mem.eql(u8, arg, "--help")) {
+ return usage(&builder, maybe_zig_exe, false, &io.stdout);
+ } else {
+ %%io.stderr.printf("Unrecognized argument: {}\n\n", arg);
+ return usage(&builder, maybe_zig_exe, false, &io.stderr);
+ }
+ } else if (maybe_zig_exe == null) {
+ maybe_zig_exe = arg;
+ } else {
+ %%targets.append(arg);
+ }
+ }
+
+ const zig_exe = maybe_zig_exe ?? return usage(&builder, null, false, &io.stderr);
+
root.build(&builder);
- %return builder.make(leftover_arg_index);
+
+ if (builder.validateUserInputDidItFail())
+ return usage(&builder, maybe_zig_exe, true, &io.stderr);
+
+ %return builder.make(zig_exe, targets.toSliceConst());
+}
+
+fn usage(builder: &Builder, maybe_zig_exe: ?[]const u8, already_ran_build: bool, out_stream: &io.OutStream) -> %void {
+ const zig_exe = maybe_zig_exe ?? {
+ %%out_stream.printf("Expected first argument to be path to zig compiler\n");
+ return error.InvalidArgs;
+ };
+
+ // run the build script to collect the options
+ if (!already_ran_build) {
+ root.build(builder);
+ }
+
+ %%out_stream.printf(
+ \\Usage: {} build [options]
+ \\
+ \\General Options:
+ \\ --help Print this help and exit.
+ \\ --verbose Print commands before executing them.
+ \\ --debug-build-verbose Print verbose debugging information for the build system itself.
+ \\
+ \\Project-Specific Options:
+ \\
+ , zig_exe);
+
+ if (builder.available_options_list.len == 0) {
+ %%out_stream.printf(" (none)\n");
+ } else {
+ const allocator = builder.allocator;
+ for (builder.available_options_list.toSliceConst()) |option| {
+ const name = %%fmt.allocPrint(allocator,
+ " -O{}=({})", option.name, Builder.typeIdName(option.type_id));
+ defer allocator.free(name);
+ %%out_stream.printf("{s24} {}\n", name, option.description);
+ }
+ }
+
+ if (out_stream == &io.stderr)
+ return error.InvalidArgs;
}
std/buf_map.zig
@@ -11,14 +11,13 @@ pub const BufMap = struct {
pub fn init(allocator: &Allocator) -> BufMap {
var self = BufMap {
- .hash_map = undefined,
+ .hash_map = BufMapHashMap.init(allocator),
};
- self.hash_map.init(allocator);
return self;
}
pub fn deinit(self: &BufMap) {
- var it = self.hash_map.entryIterator();
+ var it = self.hash_map.iterator();
while (true) {
const entry = it.next() ?? break;
self.free(entry.key);
@@ -54,7 +53,7 @@ pub const BufMap = struct {
}
pub fn iterator(self: &const BufMap) -> BufMapHashMap.Iterator {
- return self.hash_map.entryIterator();
+ return self.hash_map.iterator();
}
fn free(self: &BufMap, value: []const u8) {
std/buf_set.zig
@@ -9,14 +9,13 @@ pub const BufSet = struct {
pub fn init(allocator: &Allocator) -> BufSet {
var self = BufSet {
- .hash_map = undefined,
+ .hash_map = BufSetHashMap.init(allocator),
};
- self.hash_map.init(allocator);
return self;
}
pub fn deinit(self: &BufSet) {
- var it = self.hash_map.entryIterator();
+ var it = self.hash_map.iterator();
while (true) {
const entry = it.next() ?? break;
self.free(entry.key);
@@ -43,7 +42,7 @@ pub const BufSet = struct {
}
pub fn iterator(self: &const BufSet) -> BufSetHashMap.Iterator {
- return self.hash_map.entryIterator();
+ return self.hash_map.iterator();
}
fn free(self: &BufSet, value: []const u8) {
std/build.zig
@@ -2,6 +2,7 @@ const io = @import("io.zig");
const mem = @import("mem.zig");
const debug = @import("debug.zig");
const List = @import("list.zig").List;
+const HashMap = @import("hash_map.zig").HashMap;
const Allocator = @import("mem.zig").Allocator;
const os = @import("os/index.zig");
const StdIo = os.ChildProcess.StdIo;
@@ -12,21 +13,58 @@ error ExtraArg;
error UncleanExit;
pub const Builder = struct {
- zig_exe: []const u8,
allocator: &Allocator,
exe_list: List(&Exe),
lib_paths: List([]const u8),
include_paths: List([]const u8),
rpaths: List([]const u8),
+ user_input_options: UserInputOptionsMap,
+ available_options_map: AvailableOptionsMap,
+ available_options_list: List(AvailableOption),
+ verbose: bool,
+ invalid_user_input: bool,
+
+ const UserInputOptionsMap = HashMap([]const u8, UserInputOption, mem.hash_slice_u8, mem.eql_slice_u8);
+ const AvailableOptionsMap = HashMap([]const u8, AvailableOption, mem.hash_slice_u8, mem.eql_slice_u8);
+
+ const AvailableOption = struct {
+ name: []const u8,
+ type_id: TypeId,
+ description: []const u8,
+ };
+
+ const UserInputOption = struct {
+ name: []const u8,
+ value: UserValue,
+ used: bool,
+ };
+
+ const UserValue = enum {
+ Flag,
+ Scalar: []const u8,
+ List: List([]const u8),
+ };
+
+ const TypeId = enum {
+ Bool,
+ Int,
+ Float,
+ String,
+ List,
+ };
- pub fn init(zig_exe: []const u8, allocator: &Allocator) -> Builder {
+ pub fn init(allocator: &Allocator) -> Builder {
var self = Builder {
- .zig_exe = zig_exe,
+ .verbose = false,
+ .invalid_user_input = false,
.allocator = allocator,
.exe_list = List(&Exe).init(allocator),
.lib_paths = List([]const u8).init(allocator),
.include_paths = List([]const u8).init(allocator),
.rpaths = List([]const u8).init(allocator),
+ .user_input_options = UserInputOptionsMap.init(allocator),
+ .available_options_map = AvailableOptionsMap.init(allocator),
+ .available_options_list = List(AvailableOption).init(allocator),
};
self.processNixOSEnvVars();
return self;
@@ -46,6 +84,8 @@ pub const Builder = struct {
pub fn addExeErr(self: &Builder, root_src: []const u8, name: []const u8) -> %&Exe {
const exe = %return self.allocator.create(Exe);
*exe = Exe {
+ .verbose = false,
+ .release = false,
.root_src = root_src,
.name = name,
.target = Target.Native,
@@ -68,20 +108,13 @@ pub const Builder = struct {
%%self.lib_paths.append(path);
}
- pub fn make(self: &Builder, leftover_arg_index: usize) -> %void {
+ pub fn make(self: &Builder, zig_exe: []const u8, targets: []const []const u8) -> %void {
+ if (targets.len != 0) {
+ debug.panic("TODO non default targets");
+ }
+
var env_map = %return os.getEnvMap(self.allocator);
- var verbose = false;
- var arg_i: usize = leftover_arg_index;
- while (arg_i < os.args.count(); arg_i += 1) {
- const arg = os.args.at(arg_i);
- if (mem.eql(u8, arg, "--verbose")) {
- verbose = true;
- } else {
- %%io.stderr.printf("Unrecognized argument: '{}'\n", arg);
- return error.ExtraArg;
- }
- }
for (self.exe_list.toSlice()) |exe| {
var zig_args = List([]const u8).init(self.allocator);
defer zig_args.deinit();
@@ -89,10 +122,14 @@ pub const Builder = struct {
%return zig_args.append("build_exe"[0...]); // TODO issue #296
%return zig_args.append(exe.root_src);
- if (verbose) {
+ if (exe.verbose) {
%return zig_args.append("--verbose"[0...]); // TODO issue #296
}
+ if (exe.release) {
+ %return zig_args.append("--release"[0...]); // TODO issue #296
+ }
+
%return zig_args.append("--name"[0...]); // TODO issue #296
%return zig_args.append(exe.name);
@@ -149,22 +186,21 @@ pub const Builder = struct {
%return zig_args.append(lib_path);
}
+ if (self.verbose) {
+ printInvocation(zig_exe, zig_args);
+ }
// TODO issue #301
- var child = os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), &env_map,
+ var child = os.ChildProcess.spawn(zig_exe, zig_args.toSliceConst(), &env_map,
StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, self.allocator)
%% |err| debug.panic("Unable to spawn zig compiler: {}\n", @errorName(err));
const term = %%child.wait();
- const exe_result = switch (term) {
+ switch (term) {
Term.Clean => |code| {
if (code != 0) {
- %%io.stderr.printf("\nCompile failed with code {}. To reproduce:\n", code);
- printInvocation(self.zig_exe, zig_args);
return error.UncleanExit;
}
},
else => {
- %%io.stderr.printf("\nCompile crashed. To reproduce:\n");
- printInvocation(self.zig_exe, zig_args);
return error.UncleanExit;
},
};
@@ -208,6 +244,144 @@ pub const Builder = struct {
}
}
}
+
+ pub fn option(self: &Builder, comptime T: type, name: []const u8, description: []const u8) -> ?T {
+ const type_id = typeToEnum(T);
+ const available_option = AvailableOption {
+ .name = name,
+ .type_id = type_id,
+ .description = description,
+ };
+ if (const _ ?= %%self.available_options_map.put(name, available_option)) {
+ debug.panic("Option '{}' declared twice", name);
+ }
+ %%self.available_options_list.append(available_option);
+
+ const entry = self.user_input_options.get(name) ?? return null;
+ entry.value.used = true;
+ switch (type_id) {
+ TypeId.Bool => switch (entry.value.value) {
+ UserValue.Flag => return true,
+ UserValue.Scalar => |s| {
+ if (mem.eql(u8, s, "true")) {
+ return true;
+ } else if (mem.eql(u8, s, "false")) {
+ return false;
+ } else {
+ %%io.stderr.printf("Expected -O{} to be a boolean, but received '{}'\n", name, s);
+ self.markInvalidUserInput();
+ return null;
+ }
+ },
+ UserValue.List => {
+ %%io.stderr.printf("Expected -O{} to be a boolean, but received a list.\n", name);
+ self.markInvalidUserInput();
+ return null;
+ },
+ },
+ TypeId.Int => debug.panic("TODO integer options to build script"),
+ TypeId.Float => debug.panic("TODO float options to build script"),
+ TypeId.String => debug.panic("TODO string options to build script"),
+ TypeId.List => debug.panic("TODO list options to build script"),
+ }
+ }
+
+ pub fn addUserInputOption(self: &Builder, name: []const u8, value: []const u8) -> bool {
+ if (var prev_value ?= %%self.user_input_options.put(name, UserInputOption {
+ .name = name,
+ .value = UserValue.Scalar { value },
+ .used = false,
+ })) {
+ switch (prev_value.value) {
+ UserValue.Scalar => |s| {
+ var list = List([]const u8).init(self.allocator);
+ %%list.append(s);
+ %%list.append(value);
+ %%self.user_input_options.put(name, UserInputOption {
+ .name = name,
+ .value = UserValue.List { list },
+ .used = false,
+ });
+ },
+ UserValue.List => |*list| {
+ %%list.append(value);
+ %%self.user_input_options.put(name, UserInputOption {
+ .name = name,
+ .value = UserValue.List { *list },
+ .used = false,
+ });
+ },
+ UserValue.Flag => {
+ %%io.stderr.printf("Option '-O{}={}' conflicts with flag '-O{}'.\n", name, value, name);
+ return true;
+ },
+ }
+ }
+ return false;
+ }
+
+ pub fn addUserInputFlag(self: &Builder, name: []const u8) -> bool {
+ if (const prev_value ?= %%self.user_input_options.put(name, UserInputOption {
+ .name = name,
+ .value = UserValue.Flag,
+ .used = false,
+ })) {
+ switch (prev_value.value) {
+ UserValue.Scalar => |s| {
+ %%io.stderr.printf("Flag '-O{}' conflicts with option '-O{}={}'.\n", name, name, s);
+ return true;
+ },
+ UserValue.List => {
+ %%io.stderr.printf("Flag '-O{}' conflicts with multiple options of the same name.\n", name);
+ return true;
+ },
+ UserValue.Flag => {},
+ }
+ }
+ return false;
+ }
+
+ fn typeToEnum(comptime T: type) -> TypeId {
+ if (@isInteger(T)) {
+ TypeId.Int
+ } else if (@isFloat(T)) {
+ TypeId.Float
+ } else switch (T) {
+ bool => TypeId.Bool,
+ []const u8 => TypeId.String,
+ []const []const u8 => TypeId.List,
+ else => @compileError("Unsupported type: " ++ @typeName(T)),
+ }
+ }
+
+ fn markInvalidUserInput(self: &Builder) {
+ self.invalid_user_input = true;
+ }
+
+ pub fn typeIdName(id: TypeId) -> []const u8 {
+ return switch (id) {
+ TypeId.Bool => ([]const u8)("bool"), // TODO issue #125
+ TypeId.Int => ([]const u8)("int"), // TODO issue #125
+ TypeId.Float => ([]const u8)("float"), // TODO issue #125
+ TypeId.String => ([]const u8)("string"), // TODO issue #125
+ TypeId.List => ([]const u8)("list"), // TODO issue #125
+ };
+ }
+
+ pub fn validateUserInputDidItFail(self: &Builder) -> bool {
+ // make sure all args are used
+ var it = self.user_input_options.iterator();
+ while (true) {
+ const entry = it.next() ?? break;
+ if (!entry.value.used) {
+ %%io.stderr.printf("Invalid option: -O{}\n\n", entry.key);
+ self.markInvalidUserInput();
+ }
+ }
+
+ return self.invalid_user_input;
+ }
+
};
const CrossTarget = struct {
@@ -233,12 +407,14 @@ const Exe = struct {
target: Target,
linker_script: LinkerScript,
link_libs: BufSet,
+ verbose: bool,
+ release: bool,
- fn deinit(self: &Exe) {
+ pub fn deinit(self: &Exe) {
self.link_libs.deinit();
}
- fn setTarget(self: &Exe, target_arch: Arch, target_os: Os, target_environ: Environ) {
+ pub fn setTarget(self: &Exe, target_arch: Arch, target_os: Os, target_environ: Environ) {
self.target = Target.Cross {
CrossTarget {
.arch = target_arch,
@@ -250,17 +426,25 @@ const Exe = struct {
/// Exe keeps a reference to script for its lifetime or until this function
/// is called again.
- fn setLinkerScriptContents(self: &Exe, script: []const u8) {
+ pub fn setLinkerScriptContents(self: &Exe, script: []const u8) {
self.linker_script = LinkerScript.Embed { script };
}
- fn setLinkerScriptPath(self: &Exe, path: []const u8) {
+ pub fn setLinkerScriptPath(self: &Exe, path: []const u8) {
self.linker_script = LinkerScript.Path { path };
}
- fn linkLibrary(self: &Exe, name: []const u8) {
+ pub fn linkLibrary(self: &Exe, name: []const u8) {
%%self.link_libs.put(name);
}
+
+ pub fn setVerbose(self: &Exe, value: bool) {
+ self.verbose = value;
+ }
+
+ pub fn setRelease(self: &Exe, value: bool) {
+ self.release = value;
+ }
};
fn handleErr(err: error) -> noreturn {
@@ -401,3 +585,4 @@ fn targetEnvironName(target_environ: Environ) -> []const u8 {
Environ.coreclr => ([]const u8)("coreclr"),
};
}
+
std/fmt.zig
@@ -13,6 +13,8 @@ const State = enum { // TODO put inside format function and make sure the name a
Integer,
IntegerWidth,
Character,
+ Buf,
+ BufWidth,
};
/// Renders fmt string with args, calling output with slices of bytes.
@@ -82,8 +84,21 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
'c' => {
state = State.Character;
},
+ 's' => {
+ state = State.Buf;
+ },
else => @compileError("Unknown format character: " ++ []u8{c}),
},
+ State.Buf => switch (c) {
+ '}' => {
+ return output(context, args[next_arg]);
+ },
+ '0' ... '9' => {
+ width_start = i;
+ state = State.BufWidth;
+ },
+ else => @compileError("Unexpected character in format string: " ++ []u8{c}),
+ },
State.CloseBrace => switch (c) {
'}' => {
state = State.Start;
@@ -117,6 +132,18 @@ pub fn format(context: var, output: fn(@typeOf(context), []const u8)->bool,
'0' ... '9' => {},
else => @compileError("Unexpected character in format string: " ++ []u8{c}),
},
+ State.BufWidth => switch (c) {
+ '}' => {
+ width = comptime %%parseUnsigned(usize, fmt[width_start...i], 10);
+ if (!formatBuf(args[next_arg], width, context, output))
+ return false;
+ next_arg += 1;
+ state = State.Start;
+ start_index = i + 1;
+ },
+ '0' ... '9' => {},
+ else => @compileError("Unexpected character in format string: " ++ []u8{c}),
+ },
State.Character => switch (c) {
'}' => {
if (!formatAsciiChar(args[next_arg], context, output))
@@ -165,6 +192,23 @@ pub fn formatAsciiChar(c: u8, context: var, output: fn(@typeOf(context), []const
return output(context, (&c)[0...1]);
}
+pub fn formatBuf(buf: []const u8, width: usize,
+ context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
+{
+ if (!output(context, buf))
+ return false;
+
+ var leftover_padding = if (width > buf.len) (width - buf.len) else return true;
+ const pad_byte: u8 = ' ';
+ while (leftover_padding > 0; leftover_padding -= 1) {
+ if (!output(context, (&pad_byte)[0...1]))
+ return false;
+ }
+
+ return true;
+}
+
+
pub fn formatInt(value: var, base: u8, uppercase: bool, width: usize,
context: var, output: fn(@typeOf(context), []const u8)->bool) -> bool
{
@@ -291,6 +335,34 @@ fn digitToChar(digit: u8, uppercase: bool) -> u8 {
};
}
+const BufPrintContext = struct {
+ remaining: []u8,
+};
+
+fn bufPrintWrite(context: &BufPrintContext, bytes: []const u8) -> bool {
+ mem.copy(u8, context.remaining, bytes);
+ context.remaining = context.remaining[bytes.len...];
+ return true;
+}
+
+pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) {
+ var context = BufPrintContext { .remaining = buf, };
+ _ = format(&context, bufPrintWrite, fmt, args);
+}
+
+pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) -> %[]u8 {
+ var size: usize = 0;
+ _ = format(&size, countSize, fmt, args);
+ const buf = %return allocator.alloc(u8, size);
+ bufPrint(buf, fmt, args);
+ return buf;
+}
+
+fn countSize(size: &usize, bytes: []const u8) -> bool {
+ *size += bytes.len;
+ return true;
+}
+
test "testBufPrintInt" {
var buffer: [max_int_digits]u8 = undefined;
const buf = buffer[0...];
std/hash_map.zig
@@ -54,13 +54,15 @@ pub fn HashMap(comptime K: type, comptime V: type,
}
};
- pub fn init(hm: &Self, allocator: &Allocator) {
- hm.entries = []Entry{};
- hm.allocator = allocator;
- hm.size = 0;
- hm.max_distance_from_start_index = 0;
- // it doesn't actually matter what we set this to since we use wrapping integer arithmetic
- hm.modification_count = undefined;
+ pub fn init(allocator: &Allocator) -> Self {
+ Self {
+ .entries = []Entry{},
+ .allocator = allocator,
+ .size = 0,
+ .max_distance_from_start_index = 0,
+ // it doesn't actually matter what we set this to since we use wrapping integer arithmetic
+ .modification_count = undefined,
+ }
}
pub fn deinit(hm: &Self) {
@@ -76,7 +78,8 @@ pub fn HashMap(comptime K: type, comptime V: type,
hm.incrementModificationCount();
}
- pub fn put(hm: &Self, key: K, value: V) -> %void {
+ /// Returns the value that was already there.
+ pub fn put(hm: &Self, key: K, value: &const V) -> %?V {
if (hm.entries.len == 0) {
%return hm.initCapacity(16);
}
@@ -89,13 +92,13 @@ pub fn HashMap(comptime K: type, comptime V: type,
// dump all of the old elements into the new table
for (old_entries) |*old_entry| {
if (old_entry.used) {
- hm.internalPut(old_entry.key, old_entry.value);
+ _ = hm.internalPut(old_entry.key, old_entry.value);
}
}
hm.allocator.free(old_entries);
}
- hm.internalPut(key, value);
+ return hm.internalPut(key, value);
}
pub fn get(hm: &Self, key: K) -> ?&Entry {
@@ -134,7 +137,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
return null;
}
- pub fn entryIterator(hm: &const Self) -> Iterator {
+ pub fn iterator(hm: &const Self) -> Iterator {
return Iterator {
.hm = hm,
.count = 0,
@@ -158,9 +161,10 @@ pub fn HashMap(comptime K: type, comptime V: type,
}
}
- fn internalPut(hm: &Self, orig_key: K, orig_value: V) {
+ /// Returns the value that was already there.
+ fn internalPut(hm: &Self, orig_key: K, orig_value: &const V) -> ?V {
var key = orig_key;
- var value = orig_value;
+ var value = *orig_value;
const start_index = hm.keyToIndex(key);
var roll_over: usize = 0;
var distance_from_start_index: usize = 0;
@@ -187,7 +191,10 @@ pub fn HashMap(comptime K: type, comptime V: type,
continue;
}
- if (!entry.used) {
+ var result: ?V = null;
+ if (entry.used) {
+ result = entry.value;
+ } else {
// adding an entry. otherwise overwriting old value with
// same key
hm.size += 1;
@@ -200,7 +207,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
.key = key,
.value = value,
};
- return;
+ return result;
}
unreachable // put into a full map
}
@@ -224,15 +231,18 @@ pub fn HashMap(comptime K: type, comptime V: type,
}
test "basicHashMapTest" {
- var map: HashMap(i32, i32, hash_i32, eql_i32) = undefined;
- map.init(&debug.global_allocator);
+ var map = HashMap(i32, i32, hash_i32, eql_i32).init(&debug.global_allocator);
defer map.deinit();
- %%map.put(1, 11);
- %%map.put(2, 22);
- %%map.put(3, 33);
- %%map.put(4, 44);
- %%map.put(5, 55);
+ // TODO issue #311
+ assert(%%map.put(1, i32(11)) == null);
+ assert(%%map.put(2, i32(22)) == null);
+ assert(%%map.put(3, i32(33)) == null);
+ assert(%%map.put(4, i32(44)) == null);
+ assert(%%map.put(5, i32(55)) == null);
+
+ assert(??%%map.put(5, i32(66)) == 55);
+ assert(??%%map.put(5, i32(55)) == 66);
assert((??map.get(2)).value == 22);
_ = map.remove(2);
@@ -243,6 +253,7 @@ test "basicHashMapTest" {
fn hash_i32(x: i32) -> u32 {
*@ptrcast(&u32, &x)
}
+
fn eql_i32(a: i32, b: i32) -> bool {
a == b
}
std/mem.zig
@@ -208,6 +208,10 @@ pub fn split(s: []const u8, c: u8) -> SplitIterator {
}
}
+pub fn startsWith(comptime T: type, haystack: []const T, needle: []const T) -> bool {
+ return if (needle.len > haystack.len) false else eql(T, haystack[0...needle.len], needle);
+}
+
const SplitIterator = struct {
s: []const u8,
c: u8,
test/cases/switch.zig
@@ -138,3 +138,15 @@ fn returnsFalse() -> bool {
test "switchOnConstEnumWithVar" {
assert(!returnsFalse());
}
+
+test "switch on type" {
+ assert(trueIfBoolFalseOtherwise(bool));
+ assert(!trueIfBoolFalseOtherwise(i32));
+}
+
+fn trueIfBoolFalseOtherwise(comptime T: type) -> bool {
+ switch (T) {
+ bool => true,
+ else => false,
+ }
+}