diff --git a/lib/std/core/mem.c3 b/lib/std/core/mem.c3 index 71af5ec31..6a735e03d 100644 --- a/lib/std/core/mem.c3 +++ b/lib/std/core/mem.c3 @@ -341,12 +341,12 @@ macro tmalloc(..., usz end_padding = 0, usz alignment = DEFAULT_MEM_ALIGNMENT) @ var $Type = $vatype(0); $if ($vacount == 2): usz size = $vaarg(1); - return (($Type*)temp_allocator().alloc_aligned($Type.sizeof * size + end_padding, alignment))[:size]!!; + return (($Type*)temp().alloc_aligned($Type.sizeof * size + end_padding, alignment))[:size]!!; $else: - return ($Type*)temp_allocator().alloc_aligned($Type.sizeof + end_padding, alignment)!!; + return ($Type*)temp().alloc_aligned($Type.sizeof + end_padding, alignment)!!; $endif; $else: - return temp_allocator().alloc_aligned($vaarg(0) + end_padding, alignment)!!; + return temp().alloc_aligned($vaarg(0) + end_padding, alignment)!!; $endif; } @@ -360,28 +360,37 @@ macro tcalloc(..., usz end_padding = 0, usz alignment = mem::DEFAULT_MEM_ALIGNME var $Type = $vatype(0); $if ($vacount == 2): usz size = $vaarg(1); - return (($Type*)temp_allocator().calloc_aligned($Type.sizeof * size + end_padding, alignment))[:size]!!; + return (($Type*)temp().calloc_aligned($Type.sizeof * size + end_padding, alignment))[:size]!!; $else: - return ($Type*)temp_allocator().calloc_aligned($Type.sizeof + end_padding, alignment)!!; + return ($Type*)temp().calloc_aligned($Type.sizeof + end_padding, alignment)!!; $endif; $else: - return temp_allocator().calloc_aligned($vaarg(0) + end_padding, alignment)!!; + return temp().calloc_aligned($vaarg(0) + end_padding, alignment)!!; $endif; } fn void* trealloc(void* ptr, usz size, usz alignment = mem::DEFAULT_MEM_ALIGNMENT) @builtin @inline { - return temp_allocator().realloc_aligned(ptr, size, alignment)!!; + return temp().realloc_aligned(ptr, size, alignment)!!; } macro void @pool(;@body) @builtin { - TempAllocator* temp = temp_allocator(); - usz mark = temp.used; - defer temp.reset(mark); + TempAllocator* allocator = temp(); + usz mark = allocator.used; + defer allocator.reset(mark); @body(); } +macro void @allocating_pool(Allocator* using; @body(bool is_temp)) @builtin +{ + TempAllocator* allocator = temp(); + usz mark = allocator.used; + bool is_temp = allocator == using; + defer if (!is_temp) allocator.reset(mark); + @body(is_temp); +} + tlocal Allocator* thread_allocator @private = allocator::LIBC_ALLOCATOR; tlocal TempAllocator* thread_temp_allocator @private = null; diff --git a/lib/std/core/private/main_stub.c3 b/lib/std/core/private/main_stub.c3 index 8ba24dabe..7635ce198 100644 --- a/lib/std/core/private/main_stub.c3 +++ b/lib/std/core/private/main_stub.c3 @@ -17,14 +17,14 @@ macro int @main_to_void_main(#m, int, char**) macro String[] args_to_strings(int argc, char** argv) @private { - String *list = malloc(String, argc); + String[] list = malloc(String, argc); for (int i = 0; i < argc; i++) { char* arg = argv[i]; usz len = 0; list[i] = (String)arg[:_strlen(arg)]; } - return list[:argc]; + return list; } @@ -63,12 +63,12 @@ macro String[] win_command_line_to_strings(ushort* cmd_line) @private macro String[] wargs_strings(int argc, Char16** argv) @private { - String *list = malloc(String, argc); + String[] list = malloc(String, argc); for (int i = 0; i < argc; i++) { Char16* arg = argv[i]; Char16[] argstring = arg[:_strlen(arg)]; - list[i] = str::utf16to8(argstring) ?? str::copy("?"); + list[i] = string::from_utf16(argstring) ?? "?".copy(); } return list[:argc]; } diff --git a/lib/std/core/string.c3 b/lib/std/core/string.c3 index 340727aa3..71829568f 100644 --- a/lib/std/core/string.c3 +++ b/lib/std/core/string.c3 @@ -24,19 +24,12 @@ fault NumberConversion macro String printf(String fmt, ..., Allocator* using = mem::heap()) { - if (using == mem::temp()) + @allocating_pool(using; bool is_temp) { DString str; str.tinit(); str.printf(fmt, $vasplat()); - return str.str(); - } - @pool() - { - DString str; - str.tinit(); - str.printf(fmt, $vasplat()); - return str.copy_str(using); + return using == is_temp ? str.str() : str.copy_str(using); }; } diff --git a/lib/std/io/io_path.c3 b/lib/std/io/io_path.c3 index 3cda9ff09..b8ae03479 100644 --- a/lib/std/io/io_path.c3 +++ b/lib/std/io/io_path.c3 @@ -1,19 +1,35 @@ module std::io::path; -const PREFERRED_SEPARATOR = env::os_is_win32() ? '\\' : '/'; +const PathEnv DEFAULT_PATH_ENV = env::os_is_win32() ? PathEnv.WIN32 : PathEnv.POSIX; +const char PREFERRED_SEPARATOR_WIN32 = '\\'; +const char PREFERRED_SEPARATOR_POSIX = '/'; +const char PREFERRED_SEPARATOR = env::os_is_win32() ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX; fault PathResult { - INVALID_PATH + INVALID_PATH, + NO_PARENT, } -typedef Path = distinct String; +struct Path +{ + String path_string; + PathEnv env; +} +enum PathEnv +{ + WIN32, + POSIX +} fn Path! getcwd(Allocator* using = mem::heap()) { - return (Path)os::getcwd(using); + @allocating_pool(using; bool is_temp) + { + return new(os::getcwd(using)); + }; } fn Path! tgetcwd() @@ -21,29 +37,44 @@ fn Path! tgetcwd() return getcwd(mem::temp()) @inline; } - - fn bool is_dir(String path) { return os::native_is_dir(path); } -macro bool is_separator(char c) +macro bool is_separator(char c, PathEnv path_env = DEFAULT_PATH_ENV) { - $if (env::os_is_win32()): - return c == '/' || c == '\\'; - $else: - return c == '/'; - $endif; + return c == '/' || (c == '\\' && path_env == PathEnv.WIN32); } - - -fn Path! new(String path, Allocator* using = mem::heap()) +macro bool is_posix_separator(char c) { - Path copy = (Path)path.copy(using); - copy.normalize()?; - return (Path)copy; + return c == '/' || c == '\\'; +} + +macro bool is_win32_separator(char c) +{ + return c == '/' || c == '\\'; +} + +fn Path! new(String path, Allocator* using = mem::heap(), PathEnv path_env = DEFAULT_PATH_ENV) +{ + return { normalize(path.copy(using), path_env), path_env }; +} + +fn Path! new_windows(String path, Allocator* using = mem::heap()) +{ + return new(path, using, WIN32); +} + +fn Path! new_posix(String path, Allocator* using = mem::heap()) +{ + return new(path, using, POSIX); +} + +fn bool Path.equals(Path p1, Path p2) +{ + return p1.env == p2.env && p1.path_string == p2.path_string; } /** @@ -51,26 +82,22 @@ fn Path! new(String path, Allocator* using = mem::heap()) * * @param [in] path * @param [in] filename - * @ensure return.len >= filename.len + path.len + * @ensure return.path_string.len >= path.path_string.len **/ fn Path! Path.append(Path path, String filename, Allocator* using = mem::heap()) { - if (!path.len) return new(filename, using)?; - if (is_separator(path[^1])) - { - return (Path)path.as_str().concat(filename, using); - } + if (!path.path_string.len) return new(filename, using, path.env)?; + assert(!is_separator(path.path_string[^1], path.env)); + // Handle temp nested temp allocations. - TempAllocator* temp = mem::temp(); - usz mark = temp.used; - defer if (using != temp) temp.reset(mark); - DString dstr = dstring::new_with_capacity(path.len + 1 + filename.len, .using = temp); - dstr.append(path.as_str()); - dstr.append(PREFERRED_SEPARATOR); - dstr.append(filename); - Path p = using == temp ? (Path)dstr.str() : (Path)dstr.copy_str(using); - p.normalize()?; - return p; + @allocating_pool(using; bool is_temp) + { + DString dstr = dstring::tnew_with_capacity(path.path_string.len + 1 + filename.len); + dstr.append(path.path_string); + dstr.append(PREFERRED_SEPARATOR); + dstr.append(filename); + return { normalize(is_temp ? dstr.str() : dstr.copy_str(using), path.env), path.env }; + }; } fn Path! temp_directory(Allocator* using = mem::heap()) @@ -78,127 +105,162 @@ fn Path! temp_directory(Allocator* using = mem::heap()) return os::native_temp_directory(using); } -fn Path Path.root_name(Path path) +fn String Path.volume_name(Path path) { - usz len = root_name_len(path.as_str())!!; - if (!len) return (Path)""; - return path[:len]; + usz len = volume_name_len(path.as_str(), path.env)!!; + if (!len) return ""; + return path.path_string[:len]; } - -fn usz! root_name_len(String path) @local +fn usz! volume_name_len(String path, PathEnv path_env) @local { usz len = path.len; - if (!len) return 0; - $if (env::os_is_win32()): - switch (path[0]) - { - case '\\': - if (len < 2 || path[1] != '\\') break; - if (len == 2 || is_separator(path[2])) return PathResult.INVALID_PATH!; - for (usz i = 2; i < len; i++) - { - char c = path[i]; - if (is_separator(c)) return i; - if (is_reserved_path_char(c)) return PathResult.INVALID_PATH!; - } - return len; - case 'A'..'Z': - case 'a'..'z': - if (len < 2 || path[1] != ':') break; - if (len < 3 || !is_separator(path[2])) return PathResult.INVALID_PATH!; - return 2; - } - $endif; - return 0; + if (len < 2 || path_env != PathEnv.WIN32) return 0; + switch (path[0]) + { + case '\\': + // "\\" paths.. must be longer than 2 + if (len == 2) return 0; + int count = 1; + while (count < len && path[count] == '\\') count++; + // Not 2 => folded paths + if (count != 2) return 0; + // Check that we have a name followed by '/' + for (usz i = 2; i < len; i++) + { + char c = path[i]; + if (is_win32_separator(c)) return i; + if (is_reserved_win32_path_char(c)) return PathResult.INVALID_PATH!; + } + return PathResult.INVALID_PATH!; + case 'A'..'Z': + case 'a'..'z': + return path[1] == ':' ? 2 : 0; + default: + return 0; + } } fn Path! Path.parent(Path path) { - foreach_r(i, c : path) + if (path.path_string.len == 1 && is_separator(path.path_string[0], path.env)) return PathResult.NO_PARENT!; + foreach_r(i, c : path.path_string) { - if (is_separator(c) && i != path.len - 1) + if (is_separator(c, path.env)) { - return (Path)path[:i]; + return { path.path_string[:i], path.env }; } } - return PathResult.INVALID_PATH!; + return PathResult.NO_PARENT!; } -fn void! Path.normalize(Path* path) +fn String! normalize(String path_str, PathEnv path_env = DEFAULT_PATH_ENV) { - String path_str = path.as_str(); - if (!path_str.len) return; - usz path_start = root_name_len(path_str)?; + if (!path_str.len) return path_str; + usz path_start = volume_name_len(path_str, path_env)?; + usz path_len = path_str.len; + if (path_start == path_len) return path_str; + char path_separator = path_env == PathEnv.WIN32 ? PREFERRED_SEPARATOR_WIN32 : PREFERRED_SEPARATOR_POSIX; usz len = path_start; - bool previous_was_separator = false; - usz path_len = path.len; + bool has_root = is_separator(path_str[path_start], path_env); + if (has_root) + { + path_str[len++] = path_separator; + path_start++; + } + // It is safe to write it as true, since we already dealt with /foo. + // This allows us to avoid checking whether it is the start of the path. + bool previous_was_separator = true; + for (usz i = path_start; i < path_len; i++) { char c = path_str[i]; // Fold foo///bar into foo/bar - if (is_separator(c)) + if (is_separator(c, path_env)) { - if (previous_was_separator) - { - continue; - } - path_str.ptr[len++] = PREFERRED_SEPARATOR; + // Fold // + if (previous_was_separator) continue; + + // New /, so mark and rewrite + path_str.ptr[len++] = path_separator; previous_was_separator = true; continue; } - // If we get . we have different things that might happen: - if (c == '.' && i < path_len - 1) + // The rest are names of the path elements, so check that the + // characters are valid. + if (is_reserved_path_char(c, path_env)) return PathResult.INVALID_PATH!; + + // If we have '.' after a separator + if (c == '.' && previous_was_separator) { - // Is this ./ or /./ ? - if ((previous_was_separator || i == path_start) && is_separator(path_str[i + 1])) + // Get the number of dots until next separator, expecting 1 or 2 + bool is_last = i == path_len - 1; + int dots = 1; + if (!is_last && path_str[i + 1] == '.') { - // Then we skip this - i += 2; - continue; - } - // Is this /../ in that case we must walk back and erase(!) - if (i < path_len - 2 && previous_was_separator && path_str[i + 1] == '.' && is_separator(path_str[i + 2])) - { - assert(len > path_start); - len--; - while (len > path_start && !is_separator(path_str[len - 1])) + dots = 2; + is_last = i == path_len - 2; + if (!is_last && !is_separator(path_str[i + 2], path_env)) { - len--; + dots = 0; } - i += 2; - continue; + } + switch (dots) + { + case 1: + // /./abc -> skip to /./abc + // ^ ^ + i++; + continue; + case 2: + // We're walking back, doing so when already at the root is invalid. + if (len == path_start) return PathResult.INVALID_PATH!; + // Step back, now looking at '/' abc/def/. -> abc/def/ + len--; + // Step back until finding a separator or the start. + while (len > path_start && !is_separator(path_str[len - 1], path_env)) + { + len--; + } + // Reading, we go from /../abc to /../abc + // ^ ^ + i += 2; + continue; + default: + break; + } } if (i != len) path_str[len] = c; previous_was_separator = false; len++; } + if (len > path_start + 1 && is_separator(path_str[len - 1], path_env)) len--; path_str.ptr[len] = 0; - *path = (Path)path_str[:len]; + return path_str[:len]; } -fn Path Path.root_directory(Path path) +fn String Path.root_directory(Path path) { String path_str = path.as_str(); usz len = path_str.len; - if (!len) return (Path)""; - $if (env::os_is_win32()): - usz root_len = root_name_len(path_str)!!; - if (root_len == len || !is_separator(path_str[root_len])) return (Path)""; - return (Path)path_str[root_len..root_len]; - $else: - if (!is_separator(path_str[0])) return (Path)""; - for (usz i = 1; i < len; i++) + if (!len) return ""; + if (path.env == PathEnv.WIN32) + { + usz root_len = volume_name_len(path_str, path.env)!!; + if (root_len == len || !is_win32_separator(path_str[root_len])) return ""; + return path_str[root_len..root_len]; + } + if (!is_posix_separator(path_str[0])) return ""; + for (usz i = 1; i < len; i++) + { + if (is_posix_separator(path_str[i])) { - if (is_separator(path_str[i])) - { - return (Path)path_str[:i]; - } + return path_str[:i]; } - return path; - $endif; + } + return path_str; } fn usz! Path.file_size(Path path) @@ -208,7 +270,7 @@ fn usz! Path.file_size(Path path) fn String Path.as_str(Path path) { - return (String)path; + return path.path_string; } fn bool Path.file_or_dir_exists(Path path) @@ -234,7 +296,7 @@ fn bool Path.is_file(Path path) fn void Path.free(Path path) { - free(path.ptr); + free(path.path_string.ptr); } @@ -255,12 +317,15 @@ const bool[256] RESERVED_PATH_CHAR_WIN32 = { ['*'] = true, }; -macro bool is_reserved_path_char(char c) +macro bool is_reserved_win32_path_char(char c) { -$if (env::os_is_win32()): return RESERVED_PATH_CHAR_WIN32[c]; -$else: - return RESERVED_PATH_CHAR_POSIX[c]; -$endif; +} + +macro bool is_reserved_path_char(char c, PathEnv path_env = DEFAULT_PATH_ENV) +{ + return path_env == PathEnv.WIN32 + ? RESERVED_PATH_CHAR_WIN32[c] + : RESERVED_PATH_CHAR_POSIX[c]; } diff --git a/lib/std/io/os/file.c3 b/lib/std/io/os/file.c3 index 8a259d5ca..91429fc5c 100644 --- a/lib/std/io/os/file.c3 +++ b/lib/std/io/os/file.c3 @@ -45,9 +45,7 @@ $else: @pool() { $if (env::os_is_win32()): - CFile file = (CFile)_wfopen( - str::utf8to16(filename, mem::temp())!!, - str::utf8to16(mode, mem::temp())!!); + CFile file = (CFile)_wfopen(filename.to_temp_utf16(), filename.to_temp_utf16())?; $else: CFile file = libc::fopen(filename.zstr_tcopy(), mode.zstr_tcopy()); $endif; @@ -69,10 +67,7 @@ $else: @pool() { $if (env::os_is_win32()): - file = (CFile)_wfreopen( - str::utf8to16(filename, mem::temp())!!, - str::utf8to16(mode, mem::temp())!!, - file); + file = (CFile)_wfreopen(filename.to_temp_utf16(), mode.to_temp_utf16(), file)?; $else: file = libc::freopen(filename.zstr_tcopy(), mode.zstr_tcopy(), file); $endif; diff --git a/lib/std/io/os/fileinfo_win32.c3 b/lib/std/io/os/fileinfo_win32.c3 index 35706f4d8..b79a633a7 100644 --- a/lib/std/io/os/fileinfo_win32.c3 +++ b/lib/std/io/os/fileinfo_win32.c3 @@ -41,21 +41,14 @@ fn bool native_is_dir(String path) fn Path! native_temp_directory(Allocator* using = mem::heap()) { - Char16[256] default_buffer; - Char16* buff = &default_buffer; - // Free if we allocated on the heap. - defer if (buff != &default_buffer) free(buff); - - Win32_DWORD len = files::win32_GetTempPathW(default_buffer.len, buff); - if (!len) return IoError.GENERAL_ERROR!; - // Too long, allocate - if (len > default_buffer.len) + @allocating_pool(using; bool is_temp) { - // Allocate on the heap - buff = tmalloc(Char16, len); + Win32_DWORD len = files::win32_GetTempPathW(0, null); + if (!len) return IoError.GENERAL_ERROR!; + Char16[] buff = tmalloc(Char16, len + 1); if (!files::win32_GetTempPathW(len, buff)) return IoError.GENERAL_ERROR!; - } - return (Path)string::from_utf16(buff[:len], using); + return path::new(string::from_utf16(buff[:len]), using); + }; } /* diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 4030d0616..53425a2b7 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -2012,7 +2012,7 @@ static inline MainType sema_find_main_type(SemaContext *context, Signature *sig, } return MAIN_TYPE_RAW; case 3: - if (!is_win32 || is_winmain) break; + if (!is_win32 || !is_winmain) break; arg_type = type_flatten(params[0]->type); arg_type2 = type_flatten(params[1]->type); if (arg_type != type_voidptr) diff --git a/src/version.h b/src/version.h index db4642b0d..b8ba8eb2f 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.104" \ No newline at end of file +#define COMPILER_VERSION "0.4.105" \ No newline at end of file diff --git a/test/test_suite/stdlib/map.c3t b/test/test_suite/stdlib/map.c3t index 42b7b33e9..2d91e1edf 100644 --- a/test/test_suite/stdlib/map.c3t +++ b/test/test_suite/stdlib/map.c3t @@ -56,18 +56,15 @@ fn void main() /* #expect: test.ll @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @.static_initialize.0, ptr null }] - define internal void @.static_initialize.0() { entry: %0 = load i64, ptr getelementptr inbounds (%"Entry*[]", ptr @std.io.tostring_functions, i32 0, i32 1), align 8 %not = icmp eq i64 %0, 0 br i1 %not, label %if.then, label %if.exit - if.then: ; preds = %entry %1 = load ptr, ptr @std.core.mem.thread_allocator, align 8 call void @"std.collections.map$typeid$p$std.io$ToStringFunction$.HashMap.init"(ptr @std.io.tostring_functions, i32 64, float 7.500000e-01, ptr %1) br label %if.exit - if.exit: ; preds = %if.then, %entry %2 = call i8 @"std.collections.map$typeid$p$std.io$ToStringFunction$.HashMap.set"(ptr @std.io.tostring_functions, i64 ptrtoint (ptr @"$ct.test.Foo" to i64), ptr @test.Foo.to_string) ret void @@ -98,7 +95,6 @@ entry: %14 = load { ptr, i64 }, ptr %result, align 8 ret { ptr, i64 } %14 } - ; Function Attrs: nounwind define void @test.main() #0 { entry: @@ -137,14 +133,14 @@ entry: %retparam44 = alloca i64, align 8 %varargslots45 = alloca [1 x %variant], align 16 %result46 = alloca %"double[]", align 8 - %temp = alloca ptr, align 8 + %allocator = alloca ptr, align 8 %error_var = alloca i64, align 8 - %retparam50 = alloca ptr, align 8 + %retparam49 = alloca ptr, align 8 %mark = alloca i64, align 8 %map3 = alloca %HashMap.2, align 8 - %retparam53 = alloca i64, align 8 - %varargslots54 = alloca [1 x %variant], align 16 - %result55 = alloca %"int[]", align 8 + %retparam52 = alloca i64, align 8 + %varargslots53 = alloca [1 x %variant], align 16 + %result54 = alloca %"int[]", align 8 call void @llvm.memset.p0.i64(ptr align 8 %map, i8 0, i64 40, i1 false) %0 = load ptr, ptr @std.core.mem.thread_allocator, align 8 call void @"std.collections.map$int$test.Foo$.HashMap.init"(ptr %map, i32 16, float 7.500000e-01, ptr %0) @@ -188,7 +184,6 @@ entry: %not_err = icmp eq i64 %26, 0 %27 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) br i1 %27, label %after_check, label %after_check12 - after_check: ; preds = %entry %28 = getelementptr inbounds %Foo, ptr %retparam10, i32 0, i32 0 %29 = insertvalue %variant undef, ptr %28, 0 @@ -199,7 +194,6 @@ after_check: ; preds = %entry %not_err11 = icmp eq i64 %32, 0 %33 = call i1 @llvm.expect.i1(i1 %not_err11, i1 true) br i1 %33, label %after_check12, label %after_check12 - after_check12: ; preds = %entry, %after_check, %after_check %34 = call i8 @"std.collections.map$int$test.Foo$.HashMap.has_key"(ptr %map, i32 1) store i8 %34, ptr %taddr, align 1 @@ -270,35 +264,29 @@ after_check12: ; preds = %entry, %after_check %80 = load ptr, ptr @std.core.mem.thread_temp_allocator, align 8 %not = icmp eq ptr %80, null br i1 %not, label %if.then, label %if.exit - if.then: ; preds = %after_check12 %81 = load ptr, ptr @std.core.mem.thread_allocator, align 8 - %82 = call i64 @std.core.mem.allocator.new_temp(ptr %retparam50, i64 262144, ptr %81) - %not_err51 = icmp eq i64 %82, 0 - %83 = call i1 @llvm.expect.i1(i1 %not_err51, i1 true) - br i1 %83, label %after_check52, label %assign_optional - + %82 = call i64 @std.core.mem.allocator.new_temp(ptr %retparam49, i64 262144, ptr %81) + %not_err50 = icmp eq i64 %82, 0 + %83 = call i1 @llvm.expect.i1(i1 %not_err50, i1 true) + br i1 %83, label %after_check51, label %assign_optional assign_optional: ; preds = %if.then store i64 %82, ptr %error_var, align 8 br label %panic_block - -after_check52: ; preds = %if.then - %84 = load ptr, ptr %retparam50, align 8 +after_check51: ; preds = %if.then + %84 = load ptr, ptr %retparam49, align 8 br label %noerr_block - panic_block: ; preds = %assign_optional %85 = load ptr, ptr @std.core.builtin.panic, align 8 call void %85(ptr @.panic_msg, i64 27, ptr @.file, i64 6, ptr @.func unreachable - -noerr_block: ; preds = %after_check52 +noerr_block: ; preds = %after_check51 store ptr %84, ptr @std.core.mem.thread_temp_allocator, align 8 br label %if.exit - if.exit: ; preds = %noerr_block, %after_check12 %86 = load ptr, ptr @std.core.mem.thread_temp_allocator, align 8 - store ptr %86, ptr %temp, align 8 - %87 = load ptr, ptr %temp, align 8 + store ptr %86, ptr %allocator, align 8 + %87 = load ptr, ptr %allocator, align 8 %88 = getelementptr inbounds %TempAllocator, ptr %87, i32 0, i32 3 %89 = load i64, ptr %88, align 8 store i64 %89, ptr %mark, align 8 @@ -309,13 +297,13 @@ if.exit: ; preds = %noerr_block, %after %92 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map3, i32 7, double 5.200000e+00) %93 = load ptr, ptr @std.core.mem.thread_allocator, align 8 %94 = call { ptr, i64 } @"std.collections.map$int$double$.HashMap.key_list"(ptr %map3, ptr %93) - store { ptr, i64 } %94, ptr %result55, align 8 - %95 = insertvalue %variant undef, ptr %result55, 0 + store { ptr, i64 } %94, ptr %result54, align 8 + %95 = insertvalue %variant undef, ptr %result54, 0 %96 = insertvalue %variant %95, i64 ptrtoint (ptr @"$ct.sa$int" to i64), 1 - %97 = getelementptr inbounds [1 x %variant], ptr %varargslots54, i64 0, i64 0 + %97 = getelementptr inbounds [1 x %variant], ptr %varargslots53, i64 0, i64 0 store %variant %96, ptr %97, align 16 - %98 = call i64 @std.io.printfn(ptr %retparam53, ptr @.str.11, i64 2, ptr %varargslots54, i64 1) - %99 = load ptr, ptr %temp, align 8 + %98 = call i64 @std.io.printfn(ptr %retparam52, ptr @.str.11, i64 2, ptr %varargslots53, i64 1) + %99 = load ptr, ptr %allocator, align 8 %100 = getelementptr inbounds %TempAllocator, ptr %99, i32 0, i32 0 %101 = load i64, ptr %mark, align 8 call void @std.core.mem.allocator.Allocator.reset(ptr %100, i64 %101) diff --git a/test/unit/stdlib/io/path.c3 b/test/unit/stdlib/io/path.c3 index 35a80332d..3d08f81ba 100644 --- a/test/unit/stdlib/io/path.c3 +++ b/test/unit/stdlib/io/path.c3 @@ -5,10 +5,198 @@ fn void! test_parent() { Path p = path::new("")?; assert(catch(p.parent())); - p = path::new("/")?; + p = path::new("/", .path_env = PathEnv.POSIX)?; assert(catch(p.parent())); - p = path::new("/a/b/c")?; - assert(path::new("/a/b")? == p.parent()?); - p = path::new("/a/b/c/")?; - assert(path::new("/a/b")? == p.parent()?); -} \ No newline at end of file + p = path::new("/a/b/c", .path_env = PathEnv.POSIX)?; + assert(p.parent().as_str()? == "/a/b"); + p = path::new("/a/b/c", .path_env = PathEnv.WIN32)?; + assert(p.parent().as_str()? == `\a\b`); +} + +fn void! test_path_normalized() +{ + assert(path::new("", .path_env = PathEnv.WIN32).as_str()? == ""); + assert(catch(path::new("1:\\a\\b\\c.txt", .path_env = PathEnv.WIN32))); + assert(catch(path::new(":", .path_env = PathEnv.WIN32))); + assert(catch(path::new("1:", .path_env = PathEnv.WIN32))); + assert(catch(path::new("1:a", .path_env = PathEnv.WIN32))); +// assert(catch(path::new(`\\\a\b\c.txt`, .path_env = PathEnv.WIN32))); + assert(catch(path::new(`\\server\a\b\..\..\..\c`, .path_env = PathEnv.WIN32))); + + assert(catch(path::new(`\\a`, .path_env = PathEnv.WIN32))); + assert(catch(path::new(`a/b/../../../c`, .path_env = PathEnv.WIN32))); + assert(catch(path::new(`a/b/../../../c`, .path_env = PathEnv.POSIX))); + assert(catch(path::new(`/a/b/../../../c`, .path_env = PathEnv.WIN32))); + assert(catch(path::new(`/a/b/../../../c`, .path_env = PathEnv.POSIX))); + assert(catch(path::new(`a/b/../../..`, .path_env = PathEnv.WIN32))); + assert(catch(path::new(`a/b/../../..`, .path_env = PathEnv.POSIX))); + assert(catch(path::new(`/a/b/../../..`, .path_env = PathEnv.WIN32))); + assert(catch(path::new(`/a/b/../../..`, .path_env = PathEnv.POSIX))); + assert(catch(path::new(`../a`, .path_env = PathEnv.WIN32))); + assert(catch(path::new(`../a`, .path_env = PathEnv.POSIX))); + assert(catch(path::new(`..`, .path_env = PathEnv.WIN32))); + assert(catch(path::new(`..`, .path_env = PathEnv.POSIX))); + assert(catch(path::new(`/../a`, .path_env = PathEnv.WIN32))); + assert(catch(path::new(`/../a`, .path_env = PathEnv.POSIX))); + assert(catch(path::new(`/..`, .path_env = PathEnv.WIN32))); + assert(catch(path::new(`/..`, .path_env = PathEnv.POSIX))); + assert(catch(path::new(`C:/a/b/../../../c`, .path_env = PathEnv.WIN32))); + assert(catch(path::new(`C:/../a`, .path_env = PathEnv.WIN32))); + assert(catch(path::new(`C:/..`, .path_env = PathEnv.WIN32))); + assert(catch(path::new(`C:a/b/../../../c`, .path_env = PathEnv.WIN32))); + assert(catch(path::new(`C:../a`, .path_env = PathEnv.WIN32))); + assert(catch(path::new(`C:..`, .path_env = PathEnv.WIN32))); + + assert(path::new("/", .path_env = PathEnv.POSIX).as_str()? == "/"); + assert(path::new("/./", .path_env = PathEnv.POSIX).as_str()? == "/"); + assert(path::new("/foo/../", .path_env = PathEnv.POSIX).as_str()? == "/"); + assert(path::new("/foo/bar/../", .path_env = PathEnv.POSIX).as_str()? == "/foo"); + assert(path::new("/foo//bar", .path_env = PathEnv.POSIX).as_str()? == "/foo/bar"); + assert(path::new("/foo//bar/../", .path_env = PathEnv.POSIX).as_str()? == "/foo"); + assert(path::new("a\\b/c.txt", .path_env = PathEnv.WIN32).as_str()? == `a\b\c.txt`); + assert(path::new("a\\b/c.txt", .path_env = PathEnv.POSIX).as_str()? == "a\\b/c.txt"); + assert(path::new("C:\\a\\b/c.txt", .path_env = PathEnv.WIN32).as_str()? == `C:\a\b\c.txt`); + assert(path::new("C:\\a\\b/c.txt", .path_env = PathEnv.POSIX).as_str()? == "C:\\a\\b/c.txt"); + assert(path::new(`\\server\a\b/c.txt`, .path_env = PathEnv.WIN32).as_str()? == `\\server\a\b\c.txt`); + assert(path::new(`\\server\a\b/c.txt`, .path_env = PathEnv.POSIX).as_str()? == `\\server\a\b/c.txt`); + assert(path::new(`c:\hello//bar\\\\foo.txt`, .path_env = PathEnv.WIN32).as_str()? == `c:\hello\bar\foo.txt`); + + assert(path::new(`~\a\b/c.txt`, .path_env = PathEnv.WIN32).as_str()? == `~\a\b\c.txt`); + assert(path::new(`~\a\b/c.txt`, .path_env = PathEnv.POSIX).as_str()? == `~\a\b/c.txt`); + + assert(path::new(`a/b/../c`, .path_env = PathEnv.WIN32).as_str()? == `a\c`); + assert(path::new(`a/b/../c`, .path_env = PathEnv.POSIX).as_str()? == `a/c`); + assert(path::new(`a/b/../../c`, .path_env = PathEnv.WIN32).as_str()? == `c`); + assert(path::new(`a/b/../../c`, .path_env = PathEnv.POSIX).as_str()? == `c`); + assert(path::new(`a/b/..`, .path_env = PathEnv.WIN32).as_str()? == `a`); + assert(path::new(`a/b/..`, .path_env = PathEnv.POSIX).as_str()? == `a`); + assert(path::new(`a/b/../`, .path_env = PathEnv.WIN32).as_str()? == `a`); + assert(path::new(`a/b/../`, .path_env = PathEnv.POSIX).as_str()? == `a`); + assert(path::new(`a/b/../..`, .path_env = PathEnv.WIN32).as_str()? == ""); + assert(path::new(`a/b/../..`, .path_env = PathEnv.POSIX).as_str()? == ""); + assert(path::new(`a/b/../../`, .path_env = PathEnv.WIN32).as_str()? == ""); + assert(path::new(`a/b/../../`, .path_env = PathEnv.POSIX).as_str()? == ""); + assert(path::new(`a/b/../c/../d`, .path_env = PathEnv.WIN32).as_str()? == `a\d`); + assert(path::new(`a/b/../c/../d`, .path_env = PathEnv.POSIX).as_str()? == `a/d`); + assert(path::new(`a/b/../c/../d/`, .path_env = PathEnv.WIN32).as_str()? == `a\d`); + assert(path::new(`a/b/../c/../d/`, .path_env = PathEnv.POSIX).as_str()? == `a/d`); + assert(path::new(`a/b//d`, .path_env = PathEnv.WIN32).as_str()? == `a\b\d`); + assert(path::new(`a/b//d`, .path_env = PathEnv.POSIX).as_str()? == `a/b/d`); + assert(path::new(`a/b/././.`, .path_env = PathEnv.WIN32).as_str()? == `a\b`); + assert(path::new(`a/b/././.`, .path_env = PathEnv.POSIX).as_str()? == `a/b`); + assert(path::new(`a/b/./././`, .path_env = PathEnv.WIN32).as_str()? == `a\b`); + assert(path::new(`a/b/./././`, .path_env = PathEnv.POSIX).as_str()? == `a/b`); + assert(path::new(`./a/`, .path_env = PathEnv.WIN32).as_str()? == `a`); + assert(path::new(`./a/`, .path_env = PathEnv.POSIX).as_str()? == `a`); + assert(path::new(`./`, .path_env = PathEnv.WIN32).as_str()? == ``); + assert(path::new(`./`, .path_env = PathEnv.POSIX).as_str()? == ``); + assert(path::new(`.`, .path_env = PathEnv.WIN32).as_str()? == ``); + assert(path::new(`.`, .path_env = PathEnv.POSIX).as_str()? == ``); + assert(path::new(``, .path_env = PathEnv.WIN32).as_str()? == ``); + assert(path::new(``, .path_env = PathEnv.POSIX).as_str()? == ``); + assert(path::new(`/a`, .path_env = PathEnv.WIN32).as_str()? == `\a`); + assert(path::new(`/a`, .path_env = PathEnv.POSIX).as_str()? == `/a`); + assert(path::new(`/a/`, .path_env = PathEnv.WIN32).as_str()? == `\a`); + assert(path::new(`/a/`, .path_env = PathEnv.POSIX).as_str()? == `/a`); + assert(path::new(`/a/b/../c`, .path_env = PathEnv.WIN32).as_str()? == `\a\c`); + assert(path::new(`/a/b/../c`, .path_env = PathEnv.POSIX).as_str()? == `/a/c`); + assert(path::new(`/a/b/../../c`, .path_env = PathEnv.WIN32).as_str()? == `\c`); + assert(path::new(`/a/b/../../c`, .path_env = PathEnv.POSIX).as_str()? == `/c`); + assert(path::new(`/a/b/..`, .path_env = PathEnv.WIN32).as_str()? == `\a`); + assert(path::new(`/a/b/..`, .path_env = PathEnv.POSIX).as_str()? == `/a`); + assert(path::new(`/a/b/../..`, .path_env = PathEnv.WIN32).as_str()? == `\`); + assert(path::new(`/a/b/../..`, .path_env = PathEnv.POSIX).as_str()? == `/`); + assert(path::new(`/a/b/../c/../d`, .path_env = PathEnv.WIN32).as_str()? == `\a\d`); + assert(path::new(`/a/b/../c/../d`, .path_env = PathEnv.POSIX).as_str()? == `/a/d`); + assert(path::new(`/a/b//d`, .path_env = PathEnv.WIN32).as_str()? == `\a\b\d`); + assert(path::new(`/a/b//d`, .path_env = PathEnv.POSIX).as_str()? == `/a/b/d`); + assert(path::new(`/./a/`, .path_env = PathEnv.WIN32).as_str()? == `\a`); + assert(path::new(`/./a/`, .path_env = PathEnv.POSIX).as_str()? == `/a`); + assert(path::new(`/./`, .path_env = PathEnv.WIN32).as_str()? == `\`); + assert(path::new(`/./`, .path_env = PathEnv.POSIX).as_str()? == `/`); + assert(path::new(`/.`, .path_env = PathEnv.WIN32).as_str()? == `\`); + assert(path::new(`/.`, .path_env = PathEnv.POSIX).as_str()? == `/`); + assert(path::new(`/`, .path_env = PathEnv.WIN32).as_str()? == `\`); + assert(path::new(`/`, .path_env = PathEnv.POSIX).as_str()? == `/`); + assert(path::new(`C:/a`, .path_env = PathEnv.WIN32).as_str()? == `C:\a`); + assert(path::new(`C:/a`, .path_env = PathEnv.POSIX).as_str()? == `C:/a`); + assert(path::new(`C:/a/b/../c`, .path_env = PathEnv.WIN32).as_str()? == `C:\a\c`); + assert(path::new(`C:/a/b/../c`, .path_env = PathEnv.POSIX).as_str()? == `C:/a/c`); + assert(path::new(`C:/a/b/../../c`, .path_env = PathEnv.WIN32).as_str()? == `C:\c`); + assert(path::new(`C:/a/b/../../c`, .path_env = PathEnv.POSIX).as_str()? == `C:/c`); + assert(path::new(`C:/a/b/../../../c`, .path_env = PathEnv.POSIX).as_str()? == `c`); + assert(path::new(`C:/a/b/..`, .path_env = PathEnv.WIN32).as_str()? == `C:\a`); + assert(path::new(`C:/a/b/..`, .path_env = PathEnv.POSIX).as_str()? == `C:/a`); + assert(path::new(`C:/a/b/../..`, .path_env = PathEnv.WIN32).as_str()? == `C:\`); + assert(path::new(`C:/a/b/../..`, .path_env = PathEnv.POSIX).as_str()? == `C:`); + assert(path::new(`C:/a/b/../c/../d`, .path_env = PathEnv.WIN32).as_str()? == `C:\a\d`); + assert(path::new(`C:/a/b/../c/../d`, .path_env = PathEnv.POSIX).as_str()? == `C:/a/d`); + assert(path::new(`C:/a/b//d`, .path_env = PathEnv.WIN32).as_str()? == `C:\a\b\d`); + assert(path::new(`C:/a/b//d`, .path_env = PathEnv.POSIX).as_str()? == `C:/a/b/d`); + assert(path::new(`C:/a/b/././.`, .path_env = PathEnv.WIN32).as_str()? == `C:\a\b`); + assert(path::new(`C:/a/b/././.`, .path_env = PathEnv.POSIX).as_str()? == `C:/a/b`); + assert(path::new(`C:/./a`, .path_env = PathEnv.WIN32).as_str()? == `C:\a`); + assert(path::new(`C:/./a`, .path_env = PathEnv.POSIX).as_str()? == `C:/a`); + assert(path::new(`C:/./`, .path_env = PathEnv.WIN32).as_str()? == `C:\`); + assert(path::new(`C:/./`, .path_env = PathEnv.POSIX).as_str()? == `C:`); + assert(path::new(`C:/../a`, .path_env = PathEnv.POSIX).as_str()? == `a`); + assert(path::new(`C:/..`, .path_env = PathEnv.POSIX).as_str()? == ``); + assert(path::new(`C:/`, .path_env = PathEnv.WIN32).as_str()? == `C:\`); + assert(path::new(`C:/`, .path_env = PathEnv.POSIX).as_str()? == `C:`); + assert(path::new(`C:a`, .path_env = PathEnv.WIN32).as_str()? == `C:a`); + assert(path::new(`C:a`, .path_env = PathEnv.POSIX).as_str()? == `C:a`); + assert(path::new(`C:a/`, .path_env = PathEnv.WIN32).as_str()? == `C:a`); + assert(path::new(`C:a/`, .path_env = PathEnv.POSIX).as_str()? == `C:a`); + + assert(path::new(`C:a/b/../c`, .path_env = PathEnv.WIN32).as_str()? == `C:a\c`); + assert(path::new(`C:a/b/../c`, .path_env = PathEnv.POSIX).as_str()? == `C:a/c`); + assert(path::new(`C:a/b/../../c`, .path_env = PathEnv.WIN32).as_str()? == `C:c`); + assert(path::new(`C:a/b/../../c`, .path_env = PathEnv.POSIX).as_str()? == `c`); + assert(path::new(`C:a/b/..`, .path_env = PathEnv.WIN32).as_str()? == `C:a`); + assert(path::new(`C:a/b/..`, .path_env = PathEnv.POSIX).as_str()? == `C:a`); + assert(path::new(`C:a/b/../..`, .path_env = PathEnv.WIN32).as_str()? == `C:`); + assert(path::new(`C:a/b/../..`, .path_env = PathEnv.POSIX).as_str()? == ``); + assert(path::new(`C:a/b/../c/../d`, .path_env = PathEnv.WIN32).as_str()? == `C:a\d`); + assert(path::new(`C:a/b/../c/../d`, .path_env = PathEnv.POSIX).as_str()? == `C:a/d`); + assert(path::new(`C:a/b//d`, .path_env = PathEnv.WIN32).as_str()? == `C:a\b\d`); + assert(path::new(`C:a/b//d`, .path_env = PathEnv.POSIX).as_str()? == `C:a/b/d`); + assert(path::new(`C:a/b/././.`, .path_env = PathEnv.WIN32).as_str()? == `C:a\b`); + assert(path::new(`C:a/b/././.`, .path_env = PathEnv.POSIX).as_str()? == `C:a/b`); + assert(path::new(`C:./a`, .path_env = PathEnv.WIN32).as_str()? == `C:a`); + assert(path::new(`C:./a`, .path_env = PathEnv.POSIX).as_str()? == `C:./a`); + assert(path::new(`C:./`, .path_env = PathEnv.WIN32).as_str()? == `C:`); + assert(path::new(`C:./`, .path_env = PathEnv.POSIX).as_str()? == `C:.`); + assert(path::new(`C:../a`, .path_env = PathEnv.POSIX).as_str()? == `C:../a`); + assert(path::new(`C:..`, .path_env = PathEnv.POSIX).as_str()? == `C:..`); + assert(path::new(`C:`, .path_env = PathEnv.WIN32).as_str()? == `C:`); + assert(path::new(`C:`, .path_env = PathEnv.POSIX).as_str()? == `C:`); + + assert(path::new(`\\server/a`, .path_env = PathEnv.WIN32).as_str()? == `\\server\a`); + assert(path::new(`\\server/a`, .path_env = PathEnv.POSIX).as_str()? == `\\server/a`); + assert(path::new(`\\server\a\b\..\c`, .path_env = PathEnv.WIN32).as_str()? == `\\server\a\c`); + assert(path::new(`\\server\a\b\..\..\c`, .path_env = PathEnv.WIN32).as_str()? == `\\server\c`); + assert(path::new(`\\server\a\b\..`, .path_env = PathEnv.WIN32).as_str()? == `\\server\a`); + assert(path::new(`\\server\a\..`, .path_env = PathEnv.WIN32).as_str()? == `\\server\`); + assert(path::new(`\\server\a\b\..\c\..\d`, .path_env = PathEnv.WIN32).as_str()? == `\\server\a\d`); + assert(path::new(`\\server\a\b\\d`, .path_env = PathEnv.WIN32).as_str()? == `\\server\a\b\d`); + assert(path::new(`\\server\a\b\.\.\.`, .path_env = PathEnv.WIN32).as_str()? == `\\server\a\b`); + assert(path::new(`\\server\.\a`, .path_env = PathEnv.WIN32).as_str()? == `\\server\a`); + assert(path::new(`\\server\.`, .path_env = PathEnv.WIN32).as_str()? == `\\server\`); + assert(path::new(`\\server\`, .path_env = PathEnv.WIN32).as_str()? == `\\server\`); + + +/* + assertEquals(null, FilenameUtils.normalize("//server/../a")); + assertEquals(null, FilenameUtils.normalize("//server/..")); + assertEquals(SEP + SEP + "server" + SEP + "", FilenameUtils.normalize("//server/"));*/ +} + +fn void! test_path_volume() +{ + assert(path::new_windows(`C:\abs`).volume_name()? == `C:`); + assert(path::new_windows(`C:abs`).volume_name()? == `C:`); + assert(path::new_posix(`C:/abs`).volume_name()? == ``); + assert(path::new_posix(`C:abs`).volume_name()? == ``); + assert(path::new_windows(`\\server\`).volume_name()? == `\\server`); + assert(path::new_windows(`\\server\abc`).volume_name()? == `\\server`); +}