From 5002b7167041e04adeae85142816b157e8b6a6f8 Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshoej Date: Wed, 15 Apr 2020 19:38:50 +0200 Subject: [PATCH 01/24] use b2 upload-file instead of b2 sync --- .github/workflows/nightly.yml | 28 +++++++++++++++++----------- ci/upload-create-nightly.sh | 11 +++++++++++ 2 files changed, 28 insertions(+), 11 deletions(-) create mode 100644 ci/upload-create-nightly.sh diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 1e1be0963..bb9a7784e 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -1,7 +1,9 @@ name: Nightly + on: schedule: - cron: 0 20 * * * + push: jobs: build_windows: @@ -91,36 +93,40 @@ jobs: needs: [build_windows, build_macos, build_ubuntu] steps: - uses: actions/checkout@v1 + - name: Install B2 CLI shell: bash run: sudo pip install --upgrade b2 + - name: Download Windows artifacts uses: actions/download-artifact@v1 with: name: windows_artifacts + - name: Download Ubuntu artifacts uses: actions/download-artifact@v1 with: name: ubuntu_artifacts + - name: Download macOS artifacts uses: actions/download-artifact@v1 with: name: macos_artifacts - - name: Create archieves - run: | - now=$(date +'%Y-%m-%d') - 7z a output/odin-windows-amd64-nightly+$now.zip -r windows_artifacts/ - 7z a output/odin-ubuntu-amd64-nightly+$now.zip -r ubuntu_artifacts/ - 7z a output/odin-macos-amd64-nightly+$now.zip -r macos_artifact/ - - name: Upload artifacts to b2 + + - name: Create archives and upload shell: bash env: APPID: ${{ secrets.B2_APPID }} APPKEY: ${{ secrets.B2_APPKEY }} + BUCKET: ${{ secrets.B2_BUCKET }} run: | b2 authorize-account "$APPID" "$APPKEY" - b2 sync --keepDays 7 output b2://odin-binaries/nightly + + ./ci/upload-create-nightly.sh "$BUCKET" windows-amd64 windows_artifacts/ + ./ci/upload-create-nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/ + ./ci/upload-create-nightly.sh "$BUCKET" macos-amd64 macos_artifact/ + python3 ci/create_nightly_json.py > nightly.json - b2 upload-file odin-binaries nightly.json nightly.json - b2 clear-account - + b2 upload-file "$BUCKET" nightly.json nightly.json + + b2 clear-account \ No newline at end of file diff --git a/ci/upload-create-nightly.sh b/ci/upload-create-nightly.sh new file mode 100644 index 000000000..0764cf00b --- /dev/null +++ b/ci/upload-create-nightly.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +bucket=$1 +platform=$2 +artifact=$2 + +now=$(date +'%Y-%m-%d') +filename="odin-$platform-nightly+$now.zip" + +7z a "output/$filename" -r "$artifact" +b2 upload-file "$bucket" "$filename" "$filename" \ No newline at end of file From 9170c875e12d9dc64da7abde3afa782fb84e8d69 Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshoej Date: Wed, 15 Apr 2020 19:44:26 +0200 Subject: [PATCH 02/24] chmod +x the script before execution --- .github/workflows/nightly.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index bb9a7784e..b82b6687e 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -122,6 +122,7 @@ jobs: run: | b2 authorize-account "$APPID" "$APPKEY" + chmod +x ./ci/upload-create-nightly.sh ./ci/upload-create-nightly.sh "$BUCKET" windows-amd64 windows_artifacts/ ./ci/upload-create-nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/ ./ci/upload-create-nightly.sh "$BUCKET" macos-amd64 macos_artifact/ From 802a7763308d6d52236117102bf2012b1fc74ef0 Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshoej Date: Wed, 15 Apr 2020 19:49:37 +0200 Subject: [PATCH 03/24] Use the correct path during upload --- ci/upload-create-nightly.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/upload-create-nightly.sh b/ci/upload-create-nightly.sh index 0764cf00b..5f702f9c2 100644 --- a/ci/upload-create-nightly.sh +++ b/ci/upload-create-nightly.sh @@ -8,4 +8,4 @@ now=$(date +'%Y-%m-%d') filename="odin-$platform-nightly+$now.zip" 7z a "output/$filename" -r "$artifact" -b2 upload-file "$bucket" "$filename" "$filename" \ No newline at end of file +b2 upload-file --noProgress "$bucket" "output/$filename" "$filename" \ No newline at end of file From 1bd0e09ae130050be33f99afd3e53e43a069dbab Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshoej Date: Wed, 15 Apr 2020 19:58:11 +0200 Subject: [PATCH 04/24] Upload to nightly path and echo out some info --- ci/upload-create-nightly.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ci/upload-create-nightly.sh b/ci/upload-create-nightly.sh index 5f702f9c2..763123646 100644 --- a/ci/upload-create-nightly.sh +++ b/ci/upload-create-nightly.sh @@ -7,5 +7,7 @@ artifact=$2 now=$(date +'%Y-%m-%d') filename="odin-$platform-nightly+$now.zip" -7z a "output/$filename" -r "$artifact" -b2 upload-file --noProgress "$bucket" "output/$filename" "$filename" \ No newline at end of file +echo "Creating archive $filename from $artifact and uploading to $bucket" + +7z a -bd "output/$filename" -r "$artifact" +b2 upload-file --noProgress "$bucket" "output/$filename" "nightly/$filename" \ No newline at end of file From 020856d91a12279c8d5ee3e3529b0c5af3884e6c Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshoej Date: Wed, 15 Apr 2020 19:59:00 +0200 Subject: [PATCH 05/24] Fix macos artifact typo --- .github/workflows/nightly.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index b82b6687e..58129effd 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -125,7 +125,7 @@ jobs: chmod +x ./ci/upload-create-nightly.sh ./ci/upload-create-nightly.sh "$BUCKET" windows-amd64 windows_artifacts/ ./ci/upload-create-nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/ - ./ci/upload-create-nightly.sh "$BUCKET" macos-amd64 macos_artifact/ + ./ci/upload-create-nightly.sh "$BUCKET" macos-amd64 macos_artifacts/ python3 ci/create_nightly_json.py > nightly.json b2 upload-file "$BUCKET" nightly.json nightly.json From 550df8711f8cd6d91b3c732999da71e3a3031ec0 Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshoej Date: Wed, 15 Apr 2020 20:04:43 +0200 Subject: [PATCH 06/24] Fix typo in upload-create-nightly.sh --- ci/upload-create-nightly.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/upload-create-nightly.sh b/ci/upload-create-nightly.sh index 763123646..754b9b87c 100644 --- a/ci/upload-create-nightly.sh +++ b/ci/upload-create-nightly.sh @@ -2,7 +2,7 @@ bucket=$1 platform=$2 -artifact=$2 +artifact=$3 now=$(date +'%Y-%m-%d') filename="odin-$platform-nightly+$now.zip" From 2dcc986c4c829c505d2e3f70d2b7e289acfae5cf Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshoej Date: Wed, 15 Apr 2020 20:11:00 +0200 Subject: [PATCH 07/24] Update /ci/build_ci.bat --- ci/build_ci.bat | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/ci/build_ci.bat b/ci/build_ci.bat index 53da20680..5d5b30a74 100644 --- a/ci/build_ci.bat +++ b/ci/build_ci.bat @@ -1,8 +1,11 @@ @echo off +:: Make sure this is a decent name and not generic set exe_name=odin.exe -set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -GS- -EHsc- -GR- -O2 -MT -Z7 -DNO_ARRAY_BOUNDS_CHECK +set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF -O2 -MT -Z7 +set compiler_defines= -DLLVM_BACKEND_SUPPORT -DNO_ARRAY_BOUNDS_CHECK + set compiler_warnings= ^ -W4 -WX ^ -wd4100 -wd4101 -wd4127 -wd4189 ^ @@ -12,13 +15,17 @@ set compiler_warnings= ^ set compiler_includes= set libs= ^ - kernel32.lib + kernel32.lib ^ + bin\llvm\windows\LLVM-C.lib set linker_flags= -incremental:no -opt:ref -subsystem:console -debug -set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings% +set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings% %compiler_defines% set linker_settings=%libs% %linker_flags% -cl %compiler_settings% "src\main.cpp" ^ - /link %linker_settings% -OUT:%exe_name% ^ +del *.pdb > NUL 2> NUL +del *.ilk > NUL 2> NUL +cl %compiler_settings% "src\main.cpp" /link %linker_settings% -OUT:%exe_name% + +:end_of_build From df5626cc1f67112840e05ee8f2b9bdf8fa1f3a3b Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshoej Date: Wed, 15 Apr 2020 20:13:44 +0200 Subject: [PATCH 08/24] Pass the bucket to /ci/create_nightly_json.py --- .github/workflows/nightly.yml | 2 +- ci/create_nightly_json.py | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 58129effd..a5dadb7fe 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -127,7 +127,7 @@ jobs: ./ci/upload-create-nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/ ./ci/upload-create-nightly.sh "$BUCKET" macos-amd64 macos_artifacts/ - python3 ci/create_nightly_json.py > nightly.json + python3 ci/create_nightly_json.py "$BUCKET" > nightly.json b2 upload-file "$BUCKET" nightly.json nightly.json b2 clear-account \ No newline at end of file diff --git a/ci/create_nightly_json.py b/ci/create_nightly_json.py index 43193a65d..65be4f7af 100644 --- a/ci/create_nightly_json.py +++ b/ci/create_nightly_json.py @@ -3,18 +3,20 @@ import sys import json import datetime import urllib.parse +import sys def main(): files_by_date = {} + bucket = sys.argv[1] - files_lines = execute_cli("b2 ls --long odin-binaries nightly").split("\n") + files_lines = execute_cli(f"b2 ls --long {bucket} nightly").split("\n") for x in files_lines: parts = x.split(" ", 1) if parts[0]: json_str = execute_cli(f"b2 get-file-info {parts[0]}") data = json.loads(json_str) name = remove_prefix(data['fileName'], "nightly/") - url = f"https://f001.backblazeb2.com/file/odin-binaries/nightly/{urllib.parse.quote_plus(name)}" + url = f"https://f001.backblazeb2.com/file/{bucket}/nightly/{urllib.parse.quote_plus(name)}" sha1 = data['contentSha1'] ts = int(data['fileInfo']['src_last_modified_millis']) date = datetime.datetime.fromtimestamp(ts/1000).strftime('%Y-%m-%d') From d0cee153172dba62c6f74000196e16c6ae41855b Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshoej Date: Wed, 15 Apr 2020 20:44:47 +0200 Subject: [PATCH 09/24] Remove on:push in nightly.yml --- .github/workflows/nightly.yml | 7 +++---- ci/{upload-create-nightly.sh => upload_create_nightly.sh} | 0 2 files changed, 3 insertions(+), 4 deletions(-) rename ci/{upload-create-nightly.sh => upload_create_nightly.sh} (100%) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index a5dadb7fe..11b0aa9a4 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -3,7 +3,6 @@ name: Nightly on: schedule: - cron: 0 20 * * * - push: jobs: build_windows: @@ -123,9 +122,9 @@ jobs: b2 authorize-account "$APPID" "$APPKEY" chmod +x ./ci/upload-create-nightly.sh - ./ci/upload-create-nightly.sh "$BUCKET" windows-amd64 windows_artifacts/ - ./ci/upload-create-nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/ - ./ci/upload-create-nightly.sh "$BUCKET" macos-amd64 macos_artifacts/ + ./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/ + ./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/ + ./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/ python3 ci/create_nightly_json.py "$BUCKET" > nightly.json b2 upload-file "$BUCKET" nightly.json nightly.json diff --git a/ci/upload-create-nightly.sh b/ci/upload_create_nightly.sh similarity index 100% rename from ci/upload-create-nightly.sh rename to ci/upload_create_nightly.sh From a55975bd5a5dd1ff4ffbae083224dc8e15f06235 Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshoej Date: Sun, 19 Apr 2020 18:00:54 +0200 Subject: [PATCH 10/24] Testing --- .github/workflows/nightly.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 11b0aa9a4..671b368b4 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -3,6 +3,7 @@ name: Nightly on: schedule: - cron: 0 20 * * * + push: jobs: build_windows: From 7fddac2c364d31d6b93efc188b94255a8f96c4a7 Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshoej Date: Sun, 19 Apr 2020 18:06:20 +0200 Subject: [PATCH 11/24] Fix typo in nightly.yml --- .github/workflows/nightly.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 671b368b4..3e3c97011 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -122,7 +122,7 @@ jobs: run: | b2 authorize-account "$APPID" "$APPKEY" - chmod +x ./ci/upload-create-nightly.sh + chmod +x ./ci/upload_create_nightly.sh ./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/ ./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/ ./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/ From 3dac1c34fa963d6a4828f36a2b6f1d6fc3722d8b Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshoej Date: Sun, 19 Apr 2020 21:34:04 +0200 Subject: [PATCH 12/24] Script to delete files older than x days --- .github/workflows/nightly.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 3e3c97011..ac4fcb74e 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -119,6 +119,7 @@ jobs: APPID: ${{ secrets.B2_APPID }} APPKEY: ${{ secrets.B2_APPKEY }} BUCKET: ${{ secrets.B2_BUCKET }} + DAYS_TO_KEEP: ${{ secrets.B2_DAYS_TO_KEEP }} run: | b2 authorize-account "$APPID" "$APPKEY" @@ -127,6 +128,8 @@ jobs: ./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/ ./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/ + python3 ci/delete_old_binaries.py "$BUCKET" "$DAYS_TO_KEEP" + python3 ci/create_nightly_json.py "$BUCKET" > nightly.json b2 upload-file "$BUCKET" nightly.json nightly.json From 8eda24f2d180a89905f45f8ae4bf6c29e0c18ae6 Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshoej Date: Sun, 19 Apr 2020 21:38:59 +0200 Subject: [PATCH 13/24] Actually include script to delete binaries --- ci/delete_old_binaries.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 ci/delete_old_binaries.py diff --git a/ci/delete_old_binaries.py b/ci/delete_old_binaries.py new file mode 100644 index 000000000..206d849f5 --- /dev/null +++ b/ci/delete_old_binaries.py @@ -0,0 +1,34 @@ +import subprocess +import sys +import json +import datetime +import urllib.parse +import sys + +def main(): + files_by_date = {} + bucket = sys.argv[1] + days_to_keep = int(sys.argv[2]) + print(f"Looking for binaries to delete older than {days_to_keep} days") + + files_lines = execute_cli(f"b2 ls --long --versions {bucket} nightly").split("\n") + for x in files_lines: + parts = [y for y in x.split(' ') if y] + + if parts and parts[0]: + date = datetime.datetime.strptime(parts[2], '%Y-%m-%d').replace(hour=0, minute=0, second=0, microsecond=0) + now = datetime.datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0) + delta = now - date + + if delta.days > days_to_keep: + print(f'Deleting {parts[5]}') + execute_cli(f'b2 delete-file-version {parts[0]}') + + +def execute_cli(command): + sb = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) + return sb.stdout.read().decode("utf-8"); + +if __name__ == '__main__': + sys.exit(main()) + From 680d723c779a98eb092b4f16f043e9f8e4e1e6b2 Mon Sep 17 00:00:00 2001 From: Mikkel Hjortshoej Date: Sun, 19 Apr 2020 21:44:45 +0200 Subject: [PATCH 14/24] Remove push --- .github/workflows/nightly.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index ac4fcb74e..c6b96b583 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -3,7 +3,6 @@ name: Nightly on: schedule: - cron: 0 20 * * * - push: jobs: build_windows: From 97f7a558faaf206bb7d10eaf3adce99322fd9541 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 19 Apr 2020 21:45:04 +0100 Subject: [PATCH 15/24] `#optional_ok` tag for procedures --- core/container/array.odin | 10 +- core/mem/mem.odin | 15 +- src/check_expr.cpp | 52 +++- src/check_type.cpp | 17 ++ src/common.cpp | 2 +- src/ir.cpp | 608 +++++++++++++++++++------------------- src/llvm_backend.cpp | 8 +- src/parser.cpp | 1 + src/parser.hpp | 3 + src/types.cpp | 9 +- 10 files changed, 417 insertions(+), 308 deletions(-) diff --git a/core/container/array.odin b/core/container/array.odin index 273657eb6..123440b2e 100644 --- a/core/container/array.odin +++ b/core/container/array.odin @@ -47,10 +47,16 @@ array_slice :: proc(a: $A/Array($T)) -> []T { array_get :: proc(a: $A/Array($T), index: int) -> T { + assert(uint(index) < a.len); return (^T)(uintptr(a.data) + size_of(T)*uintptr(index))^; } +array_get_ptr :: proc(a: $A/Array($T), index: int) -> ^T { + assert(uint(index) < a.len); + return (^T)(uintptr(a.data) + size_of(T)*uintptr(index)); +} array_set :: proc(a: ^$A/Array($T), index: int, item: T) { + assert(uint(index) < a.len); (^T)(uintptr(a.data) + size_of(T)*uintptr(index))^ = item; } @@ -122,7 +128,7 @@ array_clear :: proc(q: ^$Q/Queue($T)) { } -array_push :: proc(a: ^$A/Array($T), items: ..T) { +array_push_back_elems :: proc(a: ^$A/Array($T), items: ..T) { if array_space(a^) < len(items) { array_grow(a, a.size + len(items)); } @@ -133,6 +139,8 @@ array_push :: proc(a: ^$A/Array($T), items: ..T) { a.len = offset + n; } +array_push :: proc{array_push_back, array_push_back_elems}; +array_append :: proc{array_push_back, array_push_back_elems}; array_set_capacity :: proc(a: ^$A/Array($T), new_capacity: int) { if new_capacity == a.cap { diff --git a/core/mem/mem.odin b/core/mem/mem.odin index 99a16a6c9..bafac60d5 100644 --- a/core/mem/mem.odin +++ b/core/mem/mem.odin @@ -107,8 +107,12 @@ ptr_sub :: inline proc "contextless" (a, b: $P/^$T) -> int { slice_ptr :: inline proc "contextless" (ptr: ^$T, len: int) -> []T { assert(len >= 0); - slice := Raw_Slice{data = ptr, len = len}; - return transmute([]T)slice; + return transmute([]T)Raw_Slice{data = ptr, len = len}; +} + +slice_ptr_to_bytes :: proc "contextless" (ptr: rawptr, len: int) -> []byte { + assert(len >= 0); + return transmute([]byte)Raw_Slice{data = ptr, len = len}; } slice_to_bytes :: inline proc "contextless" (slice: $E/[]$T) -> []byte { @@ -127,16 +131,19 @@ slice_data_cast :: inline proc "contextless" ($T: typeid/[]$A, slice: $S/[]$B) - } } +slice_to_components :: proc "contextless" (slice: $E/[]$T) -> (data: ^T, len: int) { + s := transmute(Raw_Slice)slice; + return s.data, s.len; +} buffer_from_slice :: inline proc(backing: $T/[]$E) -> [dynamic]E { s := transmute(Raw_Slice)backing; - d := Raw_Dynamic_Array{ + return transmute([dynamic]E)Raw_Dynamic_Array{ data = s.data, len = 0, cap = s.len, allocator = nil_allocator(), }; - return transmute([dynamic]E)d; } ptr_to_bytes :: inline proc "contextless" (ptr: ^$T, len := 1) -> []byte { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 9879d3b8b..357c6b53a 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5750,12 +5750,41 @@ bool check_assignment_arguments(CheckerContext *ctx, Array const &lhs, optional_ok = true; tuple_index += 2; + } else if (o.mode == Addressing_OptionalOk) { + Type *tuple = o.type; + GB_ASSERT(is_type_tuple(tuple)); + GB_ASSERT(tuple->Tuple.variables.count == 2); + Ast *expr = unparen_expr(o.expr); + if (expr->kind == Ast_CallExpr) { + expr->CallExpr.optional_ok_one = true; + } + Operand val = o; + val.type = tuple->Tuple.variables[0]->type; + val.mode = Addressing_Value; + array_add(operands, val); + tuple_index += 1; } else { array_add(operands, o); tuple_index += 1; } } else { TypeTuple *tuple = &o.type->Tuple; + if (o.mode == Addressing_OptionalOk) { + GB_ASSERT(tuple->variables.count == 2); + if (lhs.count == 1) { + Ast *expr = unparen_expr(o.expr); + if (expr->kind == Ast_CallExpr) { + expr->CallExpr.optional_ok_one = true; + } + Operand val = o; + val.type = tuple->variables[0]->type; + val.mode = Addressing_Value; + array_add(operands, val); + tuple_index += 1; + continue; + } + } + for_array(j, tuple->variables) { o.type = tuple->variables[j]->type; array_add(operands, o); @@ -5839,6 +5868,22 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, } } else { TypeTuple *tuple = &o.type->Tuple; + if (o.mode == Addressing_OptionalOk) { + GB_ASSERT(tuple->variables.count == 2); + if (lhs_count == 1) { + Ast *expr = unparen_expr(o.expr); + if (expr->kind == Ast_CallExpr) { + expr->CallExpr.optional_ok_one = true; + } + Operand val = o; + val.type = tuple->variables[0]->type; + val.mode = Addressing_Value; + array_add(operands, val); + tuple_index += 1; + continue; + } + } + for_array(j, tuple->variables) { o.type = tuple->variables[j]->type; array_add(operands, o); @@ -7332,7 +7377,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *t if (pl->inlining == ProcInlining_no_inline) { error(call, "'inline' cannot be applied to a procedure that has be marked as 'no_inline'"); } - } + } } break; } @@ -7342,6 +7387,11 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *t } operand->expr = call; + + if (pt->kind == Type_Proc && pt->Proc.optional_ok) { + operand->mode = Addressing_OptionalOk; + } + return Expr_Expr; } diff --git a/src/check_type.cpp b/src/check_type.cpp index 6df824ec1..73f732b45 100644 --- a/src/check_type.cpp +++ b/src/check_type.cpp @@ -2530,6 +2530,22 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, } GB_ASSERT(cc > 0); + bool optional_ok = (pt->tags & ProcTag_optional_ok) != 0; + if (optional_ok) { + if (result_count != 2) { + error(proc_type_node, "A procedure type with the #optional_ok tag requires 2 return values, got %td", result_count); + } else { + Entity *second = results->Tuple.variables[1]; + if (is_type_polymorphic(second->type)) { + // ignore + } else if (is_type_boolean(second->type)) { + // GOOD + } else { + error(second->token, "Second return value of an #optional_ok procedure must be a boolean, got %s", type_to_string(second->type)); + } + } + } + type->Proc.node = proc_type_node; type->Proc.scope = c->scope; type->Proc.params = params; @@ -2542,6 +2558,7 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node, type->Proc.is_polymorphic = pt->generic; type->Proc.specialization_count = specialization_count; type->Proc.diverging = pt->diverging; + type->Proc.optional_ok = optional_ok; type->Proc.tags = pt->tags; if (param_count > 0) { diff --git a/src/common.cpp b/src/common.cpp index ba9f297a3..af6ceee63 100644 --- a/src/common.cpp +++ b/src/common.cpp @@ -468,8 +468,8 @@ GB_ALLOCATOR_PROC(arena_allocator_proc) { struct StringIntern { - isize len; StringIntern *next; + isize len; char str[1]; }; diff --git a/src/ir.cpp b/src/ir.cpp index 6490995e8..78642a917 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -7081,6 +7081,311 @@ irValue *ir_build_expr(irProcedure *proc, Ast *expr) { return v; } + +irValue *ir_build_call_expr(irProcedure *proc, Ast *expr) { + ast_node(ce, CallExpr, expr); + TypeAndValue tv = type_and_value_of_expr(expr); + TypeAndValue proc_tv = type_and_value_of_expr(ce->proc); + AddressingMode proc_mode = proc_tv.mode; + if (proc_mode == Addressing_Type) { + GB_ASSERT(ce->args.count == 1); + irValue *x = ir_build_expr(proc, ce->args[0]); + irValue *y = ir_emit_conv(proc, x, tv.type); + return y; + } + + Ast *p = unparen_expr(ce->proc); + if (proc_mode == Addressing_Builtin) { + Entity *e = entity_of_node(p); + BuiltinProcId id = BuiltinProc_Invalid; + if (e != nullptr) { + id = cast(BuiltinProcId)e->Builtin.id; + } else { + id = BuiltinProc_DIRECTIVE; + } + return ir_build_builtin_proc(proc, expr, tv, id); + } + + // NOTE(bill): Regular call + irValue *value = nullptr; + Ast *proc_expr = unparen_expr(ce->proc); + if (proc_expr->tav.mode == Addressing_Constant) { + ExactValue v = proc_expr->tav.value; + switch (v.kind) { + case ExactValue_Integer: + { + u64 u = big_int_to_u64(&v.value_integer); + irValue *x = ir_const_uintptr(u); + x = ir_emit_conv(proc, x, t_rawptr); + value = ir_emit_conv(proc, x, proc_expr->tav.type); + break; + } + case ExactValue_Pointer: + { + u64 u = cast(u64)v.value_pointer; + irValue *x = ir_const_uintptr(u); + x = ir_emit_conv(proc, x, t_rawptr); + value = ir_emit_conv(proc, x, proc_expr->tav.type); + break; + } + } + } + + if (value == nullptr) { + value = ir_build_expr(proc, proc_expr); + } + + GB_ASSERT(value != nullptr); + Type *proc_type_ = base_type(ir_type(value)); + GB_ASSERT(proc_type_->kind == Type_Proc); + TypeProc *pt = &proc_type_->Proc; + set_procedure_abi_types(heap_allocator(), proc_type_); + + if (is_call_expr_field_value(ce)) { + auto args = array_make(ir_allocator(), pt->param_count); + + for_array(arg_index, ce->args) { + Ast *arg = ce->args[arg_index]; + ast_node(fv, FieldValue, arg); + GB_ASSERT(fv->field->kind == Ast_Ident); + String name = fv->field->Ident.token.string; + isize index = lookup_procedure_parameter(pt, name); + GB_ASSERT(index >= 0); + TypeAndValue tav = type_and_value_of_expr(fv->value); + if (tav.mode == Addressing_Type) { + args[index] = ir_value_nil(tav.type); + } else { + args[index] = ir_build_expr(proc, fv->value); + } + } + TypeTuple *params = &pt->params->Tuple; + for (isize i = 0; i < args.count; i++) { + Entity *e = params->variables[i]; + if (e->kind == Entity_TypeName) { + args[i] = ir_value_nil(e->type); + } else if (e->kind == Entity_Constant) { + continue; + } else { + GB_ASSERT(e->kind == Entity_Variable); + if (args[i] == nullptr) { + switch (e->Variable.param_value.kind) { + case ParameterValue_Constant: + args[i] = ir_value_constant(e->type, e->Variable.param_value.value); + break; + case ParameterValue_Nil: + args[i] = ir_value_nil(e->type); + break; + case ParameterValue_Location: + args[i] = ir_emit_source_code_location(proc, proc->entity->token.string, ast_token(expr).pos); + break; + case ParameterValue_Value: + args[i] = ir_build_expr(proc, e->Variable.param_value.ast_value); + break; + } + } else { + args[i] = ir_emit_conv(proc, args[i], e->type); + } + } + } + + return ir_emit_call(proc, value, args, ce->inlining, proc->return_ptr_hint_ast == expr); + } + + isize arg_index = 0; + + isize arg_count = 0; + for_array(i, ce->args) { + Ast *arg = ce->args[i]; + TypeAndValue tav = type_and_value_of_expr(arg); + GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s", expr_to_string(arg), expr_to_string(expr)); + GB_ASSERT_MSG(tav.mode != Addressing_ProcGroup, "%s", expr_to_string(arg)); + Type *at = tav.type; + if (at->kind == Type_Tuple) { + arg_count += at->Tuple.variables.count; + } else { + arg_count++; + } + } + + isize param_count = 0; + if (pt->params) { + GB_ASSERT(pt->params->kind == Type_Tuple); + param_count = pt->params->Tuple.variables.count; + } + + auto args = array_make(ir_allocator(), cast(isize)gb_max(param_count, arg_count)); + isize variadic_index = pt->variadic_index; + bool variadic = pt->variadic && variadic_index >= 0; + bool vari_expand = ce->ellipsis.pos.line != 0; + bool is_c_vararg = pt->c_vararg; + + String proc_name = {}; + if (proc->entity != nullptr) { + proc_name = proc->entity->token.string; + } + TokenPos pos = ast_token(ce->proc).pos; + + TypeTuple *param_tuple = nullptr; + if (pt->params) { + GB_ASSERT(pt->params->kind == Type_Tuple); + param_tuple = &pt->params->Tuple; + } + + for_array(i, ce->args) { + Ast *arg = ce->args[i]; + TypeAndValue arg_tv = type_and_value_of_expr(arg); + if (arg_tv.mode == Addressing_Type) { + args[arg_index++] = ir_value_nil(arg_tv.type); + } else { + irValue *a = ir_build_expr(proc, arg); + Type *at = ir_type(a); + if (at->kind == Type_Tuple) { + for_array(i, at->Tuple.variables) { + Entity *e = at->Tuple.variables[i]; + irValue *v = ir_emit_struct_ev(proc, a, cast(i32)i); + args[arg_index++] = v; + } + } else { + args[arg_index++] = a; + } + } + } + + + if (param_count > 0) { + GB_ASSERT_MSG(pt->params != nullptr, "%s %td", expr_to_string(expr), pt->param_count); + GB_ASSERT(param_count < 1000000); + + if (arg_count < param_count) { + isize end = cast(isize)param_count; + if (variadic) { + end = variadic_index; + } + while (arg_index < end) { + Entity *e = param_tuple->variables[arg_index]; + GB_ASSERT(e->kind == Entity_Variable); + + switch (e->Variable.param_value.kind) { + case ParameterValue_Constant: + args[arg_index++] = ir_value_constant(e->type, e->Variable.param_value.value); + break; + case ParameterValue_Nil: + args[arg_index++] = ir_value_nil(e->type); + break; + case ParameterValue_Location: + args[arg_index++] = ir_emit_source_code_location(proc, proc_name, pos); + break; + case ParameterValue_Value: + args[arg_index++] = ir_build_expr(proc, e->Variable.param_value.ast_value); + break; + } + } + } + + if (is_c_vararg) { + GB_ASSERT(variadic); + GB_ASSERT(!vari_expand); + isize i = 0; + for (; i < variadic_index; i++) { + Entity *e = param_tuple->variables[i]; + if (e->kind == Entity_Variable) { + args[i] = ir_emit_conv(proc, args[i], e->type); + } + } + Type *variadic_type = param_tuple->variables[i]->type; + GB_ASSERT(is_type_slice(variadic_type)); + variadic_type = base_type(variadic_type)->Slice.elem; + if (!is_type_any(variadic_type)) { + for (; i < arg_count; i++) { + args[i] = ir_emit_conv(proc, args[i], variadic_type); + } + } else { + for (; i < arg_count; i++) { + args[i] = ir_emit_conv(proc, args[i], default_type(ir_type(args[i]))); + } + } + } else if (variadic) { + isize i = 0; + for (; i < variadic_index; i++) { + Entity *e = param_tuple->variables[i]; + if (e->kind == Entity_Variable) { + args[i] = ir_emit_conv(proc, args[i], e->type); + } + } + if (!vari_expand) { + Type *variadic_type = param_tuple->variables[i]->type; + GB_ASSERT(is_type_slice(variadic_type)); + variadic_type = base_type(variadic_type)->Slice.elem; + for (; i < arg_count; i++) { + args[i] = ir_emit_conv(proc, args[i], variadic_type); + } + } + } else { + for (isize i = 0; i < param_count; i++) { + Entity *e = param_tuple->variables[i]; + if (e->kind == Entity_Variable) { + GB_ASSERT(args[i] != nullptr); + args[i] = ir_emit_conv(proc, args[i], e->type); + } + } + } + + if (variadic && !vari_expand && !is_c_vararg) { + ir_emit_comment(proc, str_lit("variadic call argument generation")); + gbAllocator allocator = ir_allocator(); + Type *slice_type = param_tuple->variables[variadic_index]->type; + Type *elem_type = base_type(slice_type)->Slice.elem; + irValue *slice = ir_add_local_generated(proc, slice_type, true); + isize slice_len = arg_count+1 - (variadic_index+1); + + if (slice_len > 0) { + irValue *base_array = ir_add_local_generated(proc, alloc_type_array(elem_type, slice_len), true); + + for (isize i = variadic_index, j = 0; i < arg_count; i++, j++) { + irValue *addr = ir_emit_array_epi(proc, base_array, cast(i32)j); + ir_emit_store(proc, addr, args[i]); + } + + irValue *base_elem = ir_emit_array_epi(proc, base_array, 0); + irValue *len = ir_const_int(slice_len); + ir_fill_slice(proc, slice, base_elem, len); + } + + arg_count = param_count; + args[variadic_index] = ir_emit_load(proc, slice); + } + } + + if (variadic && variadic_index+1 < param_count) { + for (isize i = variadic_index+1; i < param_count; i++) { + Entity *e = param_tuple->variables[i]; + switch (e->Variable.param_value.kind) { + case ParameterValue_Constant: + args[i] = ir_value_constant(e->type, e->Variable.param_value.value); + break; + case ParameterValue_Nil: + args[i] = ir_value_nil(e->type); + break; + case ParameterValue_Location: + args[i] = ir_emit_source_code_location(proc, proc_name, pos); + break; + case ParameterValue_Value: + args[i] = ir_build_expr(proc, e->Variable.param_value.ast_value); + break; + } + } + } + + isize final_count = param_count; + if (is_c_vararg) { + final_count = arg_count; + } + + auto call_args = array_slice(args, 0, final_count); + return ir_emit_call(proc, value, call_args, ce->inlining, proc->return_ptr_hint_ast == expr); +} + + irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { Ast *original_expr = expr; expr = unparen_expr(expr); @@ -7551,304 +7856,13 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) { case_ast_node(ce, CallExpr, expr); - TypeAndValue proc_tv = type_and_value_of_expr(ce->proc); - AddressingMode proc_mode = proc_tv.mode; - if (proc_mode == Addressing_Type) { - GB_ASSERT(ce->args.count == 1); - irValue *x = ir_build_expr(proc, ce->args[0]); - irValue *y = ir_emit_conv(proc, x, tv.type); - return y; + irValue *res = ir_build_call_expr(proc, expr); + if (ce->optional_ok_one) { // TODO(bill): Minor hack for #optional_ok procedures + GB_ASSERT(is_type_tuple(ir_type(res))); + GB_ASSERT(ir_type(res)->Tuple.variables.count == 2); + return ir_emit_struct_ev(proc, res, 0); } - - Ast *p = unparen_expr(ce->proc); - if (proc_mode == Addressing_Builtin) { - Entity *e = entity_of_node(p); - BuiltinProcId id = BuiltinProc_Invalid; - if (e != nullptr) { - id = cast(BuiltinProcId)e->Builtin.id; - } else { - id = BuiltinProc_DIRECTIVE; - } - return ir_build_builtin_proc(proc, expr, tv, id); - } - - // NOTE(bill): Regular call - irValue *value = nullptr; - Ast *proc_expr = unparen_expr(ce->proc); - if (proc_expr->tav.mode == Addressing_Constant) { - ExactValue v = proc_expr->tav.value; - switch (v.kind) { - case ExactValue_Integer: - { - u64 u = big_int_to_u64(&v.value_integer); - irValue *x = ir_const_uintptr(u); - x = ir_emit_conv(proc, x, t_rawptr); - value = ir_emit_conv(proc, x, proc_expr->tav.type); - break; - } - case ExactValue_Pointer: - { - u64 u = cast(u64)v.value_pointer; - irValue *x = ir_const_uintptr(u); - x = ir_emit_conv(proc, x, t_rawptr); - value = ir_emit_conv(proc, x, proc_expr->tav.type); - break; - } - } - } - - if (value == nullptr) { - value = ir_build_expr(proc, proc_expr); - } - - GB_ASSERT(value != nullptr); - Type *proc_type_ = base_type(ir_type(value)); - GB_ASSERT(proc_type_->kind == Type_Proc); - TypeProc *pt = &proc_type_->Proc; - set_procedure_abi_types(heap_allocator(), proc_type_); - - if (is_call_expr_field_value(ce)) { - auto args = array_make(ir_allocator(), pt->param_count); - - for_array(arg_index, ce->args) { - Ast *arg = ce->args[arg_index]; - ast_node(fv, FieldValue, arg); - GB_ASSERT(fv->field->kind == Ast_Ident); - String name = fv->field->Ident.token.string; - isize index = lookup_procedure_parameter(pt, name); - GB_ASSERT(index >= 0); - TypeAndValue tav = type_and_value_of_expr(fv->value); - if (tav.mode == Addressing_Type) { - args[index] = ir_value_nil(tav.type); - } else { - args[index] = ir_build_expr(proc, fv->value); - } - } - TypeTuple *params = &pt->params->Tuple; - for (isize i = 0; i < args.count; i++) { - Entity *e = params->variables[i]; - if (e->kind == Entity_TypeName) { - args[i] = ir_value_nil(e->type); - } else if (e->kind == Entity_Constant) { - continue; - } else { - GB_ASSERT(e->kind == Entity_Variable); - if (args[i] == nullptr) { - switch (e->Variable.param_value.kind) { - case ParameterValue_Constant: - args[i] = ir_value_constant(e->type, e->Variable.param_value.value); - break; - case ParameterValue_Nil: - args[i] = ir_value_nil(e->type); - break; - case ParameterValue_Location: - args[i] = ir_emit_source_code_location(proc, proc->entity->token.string, ast_token(expr).pos); - break; - case ParameterValue_Value: - args[i] = ir_build_expr(proc, e->Variable.param_value.ast_value); - break; - } - } else { - args[i] = ir_emit_conv(proc, args[i], e->type); - } - } - } - - return ir_emit_call(proc, value, args, ce->inlining, proc->return_ptr_hint_ast == expr); - } - - isize arg_index = 0; - - isize arg_count = 0; - for_array(i, ce->args) { - Ast *arg = ce->args[i]; - TypeAndValue tav = type_and_value_of_expr(arg); - GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s", expr_to_string(arg), expr_to_string(expr)); - GB_ASSERT_MSG(tav.mode != Addressing_ProcGroup, "%s", expr_to_string(arg)); - Type *at = tav.type; - if (at->kind == Type_Tuple) { - arg_count += at->Tuple.variables.count; - } else { - arg_count++; - } - } - - isize param_count = 0; - if (pt->params) { - GB_ASSERT(pt->params->kind == Type_Tuple); - param_count = pt->params->Tuple.variables.count; - } - - auto args = array_make(ir_allocator(), cast(isize)gb_max(param_count, arg_count)); - isize variadic_index = pt->variadic_index; - bool variadic = pt->variadic && variadic_index >= 0; - bool vari_expand = ce->ellipsis.pos.line != 0; - bool is_c_vararg = pt->c_vararg; - - String proc_name = {}; - if (proc->entity != nullptr) { - proc_name = proc->entity->token.string; - } - TokenPos pos = ast_token(ce->proc).pos; - - TypeTuple *param_tuple = nullptr; - if (pt->params) { - GB_ASSERT(pt->params->kind == Type_Tuple); - param_tuple = &pt->params->Tuple; - } - - for_array(i, ce->args) { - Ast *arg = ce->args[i]; - TypeAndValue arg_tv = type_and_value_of_expr(arg); - if (arg_tv.mode == Addressing_Type) { - args[arg_index++] = ir_value_nil(arg_tv.type); - } else { - irValue *a = ir_build_expr(proc, arg); - Type *at = ir_type(a); - if (at->kind == Type_Tuple) { - for_array(i, at->Tuple.variables) { - Entity *e = at->Tuple.variables[i]; - irValue *v = ir_emit_struct_ev(proc, a, cast(i32)i); - args[arg_index++] = v; - } - } else { - args[arg_index++] = a; - } - } - } - - - if (param_count > 0) { - GB_ASSERT_MSG(pt->params != nullptr, "%s %td", expr_to_string(expr), pt->param_count); - GB_ASSERT(param_count < 1000000); - - if (arg_count < param_count) { - isize end = cast(isize)param_count; - if (variadic) { - end = variadic_index; - } - while (arg_index < end) { - Entity *e = param_tuple->variables[arg_index]; - GB_ASSERT(e->kind == Entity_Variable); - - switch (e->Variable.param_value.kind) { - case ParameterValue_Constant: - args[arg_index++] = ir_value_constant(e->type, e->Variable.param_value.value); - break; - case ParameterValue_Nil: - args[arg_index++] = ir_value_nil(e->type); - break; - case ParameterValue_Location: - args[arg_index++] = ir_emit_source_code_location(proc, proc_name, pos); - break; - case ParameterValue_Value: - args[arg_index++] = ir_build_expr(proc, e->Variable.param_value.ast_value); - break; - } - } - } - - if (is_c_vararg) { - GB_ASSERT(variadic); - GB_ASSERT(!vari_expand); - isize i = 0; - for (; i < variadic_index; i++) { - Entity *e = param_tuple->variables[i]; - if (e->kind == Entity_Variable) { - args[i] = ir_emit_conv(proc, args[i], e->type); - } - } - Type *variadic_type = param_tuple->variables[i]->type; - GB_ASSERT(is_type_slice(variadic_type)); - variadic_type = base_type(variadic_type)->Slice.elem; - if (!is_type_any(variadic_type)) { - for (; i < arg_count; i++) { - args[i] = ir_emit_conv(proc, args[i], variadic_type); - } - } else { - for (; i < arg_count; i++) { - args[i] = ir_emit_conv(proc, args[i], default_type(ir_type(args[i]))); - } - } - } else if (variadic) { - isize i = 0; - for (; i < variadic_index; i++) { - Entity *e = param_tuple->variables[i]; - if (e->kind == Entity_Variable) { - args[i] = ir_emit_conv(proc, args[i], e->type); - } - } - if (!vari_expand) { - Type *variadic_type = param_tuple->variables[i]->type; - GB_ASSERT(is_type_slice(variadic_type)); - variadic_type = base_type(variadic_type)->Slice.elem; - for (; i < arg_count; i++) { - args[i] = ir_emit_conv(proc, args[i], variadic_type); - } - } - } else { - for (isize i = 0; i < param_count; i++) { - Entity *e = param_tuple->variables[i]; - if (e->kind == Entity_Variable) { - GB_ASSERT(args[i] != nullptr); - args[i] = ir_emit_conv(proc, args[i], e->type); - } - } - } - - if (variadic && !vari_expand && !is_c_vararg) { - ir_emit_comment(proc, str_lit("variadic call argument generation")); - gbAllocator allocator = ir_allocator(); - Type *slice_type = param_tuple->variables[variadic_index]->type; - Type *elem_type = base_type(slice_type)->Slice.elem; - irValue *slice = ir_add_local_generated(proc, slice_type, true); - isize slice_len = arg_count+1 - (variadic_index+1); - - if (slice_len > 0) { - irValue *base_array = ir_add_local_generated(proc, alloc_type_array(elem_type, slice_len), true); - - for (isize i = variadic_index, j = 0; i < arg_count; i++, j++) { - irValue *addr = ir_emit_array_epi(proc, base_array, cast(i32)j); - ir_emit_store(proc, addr, args[i]); - } - - irValue *base_elem = ir_emit_array_epi(proc, base_array, 0); - irValue *len = ir_const_int(slice_len); - ir_fill_slice(proc, slice, base_elem, len); - } - - arg_count = param_count; - args[variadic_index] = ir_emit_load(proc, slice); - } - } - - if (variadic && variadic_index+1 < param_count) { - for (isize i = variadic_index+1; i < param_count; i++) { - Entity *e = param_tuple->variables[i]; - switch (e->Variable.param_value.kind) { - case ParameterValue_Constant: - args[i] = ir_value_constant(e->type, e->Variable.param_value.value); - break; - case ParameterValue_Nil: - args[i] = ir_value_nil(e->type); - break; - case ParameterValue_Location: - args[i] = ir_emit_source_code_location(proc, proc_name, pos); - break; - case ParameterValue_Value: - args[i] = ir_build_expr(proc, e->Variable.param_value.ast_value); - break; - } - } - } - - isize final_count = param_count; - if (is_c_vararg) { - final_count = arg_count; - } - - auto call_args = array_slice(args, 0, final_count); - return ir_emit_call(proc, value, call_args, ce->inlining, proc->return_ptr_hint_ast == expr); + return res; case_end; case_ast_node(se, SliceExpr, expr); diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index b03f195b9..cc6eccff6 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -8903,7 +8903,13 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) { case_end; case_ast_node(ce, CallExpr, expr); - return lb_build_call_expr(p, expr); + lbValue res = lb_build_call_expr(p, expr); + if (ce->optional_ok_one) { // TODO(bill): Minor hack for #optional_ok procedures + GB_ASSERT(is_type_tuple(res.type)); + GB_ASSERT(res.type->Tuple.variables.count == 2); + return lb_emit_struct_ev(p, res, 0); + } + return res; case_end; case_ast_node(se, SliceExpr, expr); diff --git a/src/parser.cpp b/src/parser.cpp index e0cd60d79..eb5bdf7b6 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1618,6 +1618,7 @@ void parse_proc_tags(AstFile *f, u64 *tags) { } if (false) {} + ELSE_IF_ADD_TAG(optional_ok) ELSE_IF_ADD_TAG(require_results) ELSE_IF_ADD_TAG(bounds_check) ELSE_IF_ADD_TAG(no_bounds_check) diff --git a/src/parser.hpp b/src/parser.hpp index 1b236b921..9ef1a8720 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -165,7 +165,9 @@ enum ProcInlining { enum ProcTag { ProcTag_bounds_check = 1<<0, ProcTag_no_bounds_check = 1<<1, + ProcTag_require_results = 1<<4, + ProcTag_optional_ok = 1<<5, }; enum ProcCallingConvention { @@ -282,6 +284,7 @@ AST_KIND(_ExprBegin, "", bool) \ Token close; \ Token ellipsis; \ ProcInlining inlining; \ + bool optional_ok_one; \ }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(TernaryExpr, "ternary expression", struct { Ast *cond, *x, *y; }) \ diff --git a/src/types.cpp b/src/types.cpp index 530a02df7..1590d0a43 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -232,6 +232,7 @@ struct TypeUnion { Array abi_compat_params; \ Type * abi_compat_result_type; \ i32 variadic_index; \ + /* TODO(bill): Make this a flag set rather than bools */ \ bool variadic; \ bool abi_types_set; \ bool require_results; \ @@ -242,6 +243,7 @@ struct TypeUnion { bool has_named_results; \ bool diverging; /* no return */ \ bool return_by_pointer; \ + bool optional_ok; \ u64 tags; \ isize specialization_count; \ ProcCallingConvention calling_convention; \ @@ -1979,9 +1981,10 @@ bool are_types_identical(Type *x, Type *y) { case Type_Proc: if (y->kind == Type_Proc) { return x->Proc.calling_convention == y->Proc.calling_convention && - x->Proc.c_vararg == y->Proc.c_vararg && - x->Proc.variadic == y->Proc.variadic && - x->Proc.diverging == y->Proc.diverging && + x->Proc.c_vararg == y->Proc.c_vararg && + x->Proc.variadic == y->Proc.variadic && + x->Proc.diverging == y->Proc.diverging && + x->Proc.optional_ok == y->Proc.optional_ok && are_types_identical(x->Proc.params, y->Proc.params) && are_types_identical(x->Proc.results, y->Proc.results); } From 8ee67e41f44f01d2124fbd78639ec48cf7703683 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 19 Apr 2020 23:08:50 +0100 Subject: [PATCH 16/24] Fix `#optional_ok`; Fix `container.Array` --- core/container/array.odin | 26 ++++++------- src/check_expr.cpp | 77 +++++++++++++++++++-------------------- 2 files changed, 51 insertions(+), 52 deletions(-) diff --git a/core/container/array.odin b/core/container/array.odin index 123440b2e..945d2a5c2 100644 --- a/core/container/array.odin +++ b/core/container/array.odin @@ -46,23 +46,23 @@ array_slice :: proc(a: $A/Array($T)) -> []T { } -array_get :: proc(a: $A/Array($T), index: int) -> T { - assert(uint(index) < a.len); +array_get :: proc(a: $A/Array($T), index: int, loc := #caller_location) -> T { + assert(condition=0 <= index && index < a.len, loc=loc); return (^T)(uintptr(a.data) + size_of(T)*uintptr(index))^; } -array_get_ptr :: proc(a: $A/Array($T), index: int) -> ^T { - assert(uint(index) < a.len); +array_get_ptr :: proc(a: $A/Array($T), index: int, loc := #caller_location) -> ^T { + assert(condition=0 <= index && index < a.len, loc=loc); return (^T)(uintptr(a.data) + size_of(T)*uintptr(index)); } -array_set :: proc(a: ^$A/Array($T), index: int, item: T) { - assert(uint(index) < a.len); +array_set :: proc(a: ^$A/Array($T), index: int, item: T, loc := #caller_location) { + assert(condition=0 <= index && index < a.len, loc=loc); (^T)(uintptr(a.data) + size_of(T)*uintptr(index))^ = item; } array_reserve :: proc(a: ^$A/Array, capacity: int) { - if capacity > a.size { + if capacity > a.len { array_set_capacity(a, capacity); } } @@ -81,8 +81,8 @@ array_push_back :: proc(a: ^$A/Array($T), item: T) { array_grow(a); } - a.size += 1; - array_set(a, a.size, item); + a.len += 1; + array_set(a, a.len-1, item); } array_push_front :: proc(a: ^$A/Array($T), item: T) { @@ -114,8 +114,8 @@ array_pop_font :: proc(a: ^$A/Array($T)) -> T { array_consume :: proc(a: ^$A/Array($T), count: int) { - assert(a.size >= count); - a.size -= count; + assert(a.len >= count); + a.len -= count; } @@ -130,7 +130,7 @@ array_clear :: proc(q: ^$Q/Queue($T)) { array_push_back_elems :: proc(a: ^$A/Array($T), items: ..T) { if array_space(a^) < len(items) { - array_grow(a, a.size + len(items)); + array_grow(a, a.len + len(items)); } offset := a.len; a.len += len(items); @@ -163,6 +163,6 @@ array_set_capacity :: proc(a: ^$A/Array($T), new_capacity: int) { a.cap = new_capacity; } array_grow :: proc(a: ^$A/Array, min_capacity: int = 0) { - new_capacity := max(len(a.data)*2 + 8, min_capacity); + new_capacity := max(array_len(a^)*2 + 8, min_capacity); array_set_capacity(a, new_capacity); } diff --git a/src/check_expr.cpp b/src/check_expr.cpp index 357c6b53a..d43d51e21 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5762,36 +5762,34 @@ bool check_assignment_arguments(CheckerContext *ctx, Array const &lhs, val.type = tuple->Tuple.variables[0]->type; val.mode = Addressing_Value; array_add(operands, val); - tuple_index += 1; + tuple_index += tuple->Tuple.variables.count; } else { array_add(operands, o); tuple_index += 1; } } else { TypeTuple *tuple = &o.type->Tuple; - if (o.mode == Addressing_OptionalOk) { + if (o.mode == Addressing_OptionalOk && lhs.count == 1) { GB_ASSERT(tuple->variables.count == 2); - if (lhs.count == 1) { - Ast *expr = unparen_expr(o.expr); - if (expr->kind == Ast_CallExpr) { - expr->CallExpr.optional_ok_one = true; - } - Operand val = o; - val.type = tuple->variables[0]->type; - val.mode = Addressing_Value; - array_add(operands, val); - tuple_index += 1; - continue; + Ast *expr = unparen_expr(o.expr); + if (expr->kind == Ast_CallExpr) { + expr->CallExpr.optional_ok_one = true; } - } + Operand val = o; + val.type = tuple->variables[0]->type; + val.mode = Addressing_Value; + array_add(operands, val); + tuple_index += tuple->variables.count; - for_array(j, tuple->variables) { - o.type = tuple->variables[j]->type; - array_add(operands, o); - } + add_type_and_value(c->info, val.expr, val.mode, val.type, val.value); + } else { + for_array(j, tuple->variables) { + o.type = tuple->variables[j]->type; + array_add(operands, o); + } - isize count = tuple->variables.count; - tuple_index += 2; + tuple_index += tuple->variables.count; + } } } @@ -5868,29 +5866,30 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, } } else { TypeTuple *tuple = &o.type->Tuple; - if (o.mode == Addressing_OptionalOk) { + if (o.mode == Addressing_OptionalOk && lhs_count == 1) { GB_ASSERT(tuple->variables.count == 2); - if (lhs_count == 1) { - Ast *expr = unparen_expr(o.expr); - if (expr->kind == Ast_CallExpr) { - expr->CallExpr.optional_ok_one = true; - } - Operand val = o; - val.type = tuple->variables[0]->type; - val.mode = Addressing_Value; - array_add(operands, val); - tuple_index += 1; - continue; + Ast *expr = unparen_expr(o.expr); + if (expr->kind == Ast_CallExpr) { + expr->CallExpr.optional_ok_one = true; } - } + Operand val = o; + val.type = tuple->variables[0]->type; + val.mode = Addressing_Value; + array_add(operands, val); - for_array(j, tuple->variables) { - o.type = tuple->variables[j]->type; - array_add(operands, o); - } + isize count = tuple->variables.count; + tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count); + + add_type_and_value(c->info, val.expr, val.mode, val.type, val.value); + } else { + for_array(j, tuple->variables) { + o.type = tuple->variables[j]->type; + array_add(operands, o); + } - isize count = tuple->variables.count; - tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count); + isize count = tuple->variables.count; + tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count); + } } } From 52bbdefec47d31096ed72dc86ebd304e8700607f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 19 Apr 2020 23:43:02 +0100 Subject: [PATCH 17/24] `container.Map` --- core/container/array.odin | 52 +++++- core/container/map.odin | 366 ++++++++++++++++++++++++++++++++++++++ core/container/queue.odin | 26 ++- 3 files changed, 438 insertions(+), 6 deletions(-) create mode 100644 core/container/map.odin diff --git a/core/container/array.odin b/core/container/array.odin index 945d2a5c2..1a9e8ac28 100644 --- a/core/container/array.odin +++ b/core/container/array.odin @@ -9,6 +9,38 @@ Array :: struct(T: typeid) { allocator: mem.Allocator, } +/* +array_init :: proc { + array_init_none, + array_init_len, + array_init_len_cap, +} +array_init +array_delete +array_len +array_cap +array_space +array_slice +array_get +array_get_ptr +array_set +array_reserve +array_resize +array_push = array_append :: proc{ + array_push_back, + array_push_back_elems, +} +array_push_front +array_pop_back +array_pop_font +array_consume +array_trim +array_clear +array_clone +array_set_capacity +array_grow +*/ + array_init_none :: proc(a: ^$A/Array, allocator := context.allocator) { array_init_len(a, 0, allocator); } @@ -16,10 +48,10 @@ array_init_len :: proc(a: ^$A/Array, len: int, allocator := context.allocator) { array_init_len_cap(a, 0, 16, allocator); } array_init_len_cap :: proc(a: ^$A/Array($T), len: int, cap: int, allocator := context.allocator) { - a.data = (^T)(mem.alloc(size_of(T)*cap, align_of(T), allocator)); + a.allocator = allocator; + a.data = (^T)(mem.alloc(size_of(T)*cap, align_of(T), a.allocator)); a.len = len; a.cap = cap; - a.allocator = allocator; } array_init :: proc{array_init_none, array_init_len, array_init_len_cap}; @@ -123,8 +155,15 @@ array_trim :: proc(a: ^$A/Array($T)) { array_set_capacity(a, a.len); } -array_clear :: proc(q: ^$Q/Queue($T)) { - array_resize(q, 0); +array_clear :: proc(a: ^$A/Array($T)) { + array_resize(a, 0); +} + +array_clone :: proc(a: $A/Array($T), allocator := context.allocator) -> A { + res: A; + array_init(&res, array_len(a), array_len(a), allocator); + copy(array_slice(res), array_slice(a)); + return res; } @@ -153,12 +192,15 @@ array_set_capacity :: proc(a: ^$A/Array($T), new_capacity: int) { new_data: ^T; if new_capacity > 0 { + if a.allocator.procedure == nil { + a.allocator = context.allocator; + } new_data = (^T)(mem.alloc(size_of(T)*new_capacity, align_of(T), a.allocator)); if new_data != nil { mem.copy(new_data, a.data, size_of(T)*a.len); } } - mem.free(a.data); + mem.free(a.data, a.allocator); a.data = new_data; a.cap = new_capacity; } diff --git a/core/container/map.odin b/core/container/map.odin new file mode 100644 index 000000000..b25b2e31f --- /dev/null +++ b/core/container/map.odin @@ -0,0 +1,366 @@ +package container + +import "core:mem" +import "intrinsics" + + +Map :: struct(Value: typeid) { + hash: Array(int), + entries: Array(Map_Entry(Value)), +} + +Map_Entry :: struct(Value: typeid) { + key: u64, + next: int, + value: Value, +} + + +/* +map_init :: proc{ + map_init_none, + map_init_cap, +} +map_delete + +map_has +map_get +map_get_default +map_get_ptr +map_set +map_remove +map_reserve +map_clear + +// Multi Map + +multi_map_find_first +multi_map_find_next +multi_map_count +multi_map_get :: proc{ + multi_map_get_array, + multi_map_get_slice, +}; +multi_map_get_as_slice +multi_map_insert +multi_map_remove +multi_map_remove_all + +*/ + +map_init :: proc{map_init_none, map_init_cap}; + +map_init_none :: proc(m: ^$M/Map($Value), allocator := context.allocator) { + m.hash.allocator = allocator; + m.entries.allocator = allocator; +} + +map_init_cap :: proc(m: ^$M/Map($Value), cap: int, allocator := context.allocator) { + m.hash.allocator = allocator; + m.entries.allocator = allocator; + map_reserve(m, cap); +} + +map_delete :: proc(m: $M/Map($Value)) { + array_delete(m.hash); + array_delete(m.entries); +} + + +map_has :: proc(m: $M/Map($Value), key: u64) -> bool { + return _map_find_or_fail(m, key) < 0; +} + +map_get :: proc(m: $M/Map($Value), key: u64) -> (res: Value, ok: bool) #optional_ok { + i := _map_find_or_fail(m, key); + if i < 0 { + return {}, false; + } + return array_get(m.entries, i).value, true; +} + +map_get_default :: proc(m: $M/Map($Value), key: u64, default: Value) -> (res: Value, ok: bool) #optional_ok { + i := _map_find_or_fail(m, key); + if i < 0 { + return default, false; + } + return array_get(m.entries, i).value, true; +} + +map_get_ptr :: proc(m: $M/Map($Value), key: u64) -> ^Value { + i := _map_find_or_fail(m, key); + if i < 0 { + return nil; + } + return array_get_ptr(m.entries, i).value; +} + +map_set :: proc(m: ^$M/Map($Value), key: u64, value: Value) { + if array_len(m.hash) == 0 { + _map_grow(m); + } + + i := _map_find_or_make(m, key); + array_get_ptr(m.entries, i).value = value; + if _map_full(m^) { + _map_grow(m); + } +} + +map_remove :: proc(m: ^$M/Map($Value), key: u64) { + fr := _map_find_key(m, key); + if fr.entry_index >= 0 { + _map_erase(m, fr); + } +} + + +map_reserve :: proc(m: ^$M/Map($Value), new_size: int) { + nm: M; + map_init(&nm, m.hash.allocator); + array_resize(&nm.hash, new_size); + array_reserve(&nm.entries, array_len(m.entries)); + + for i in 0.. ^Map_Entry(Value) { + i := _map_find_or_fail(m, key); + if i < 0 { + return nil; + } + return array_get_ptr(m.entries, i); +} + +multi_map_find_next :: proc(m: $M/Map($Value), e: ^Map_Entry(Value)) -> ^Map_Entry(Value) { + i := e.next; + for i >= 0 { + it := array_get_ptr(m.entries, i); + if it.key == e.key { + return it; + } + i = it.next; + } + return nil; +} + +multi_map_count :: proc(m: $M/Map($Value), key: u64) -> int { + n := 0; + e := multi_map_find_first(m, key); + for e != nil { + n += 1; + e = multi_map_find_next(m, e); + } + return n; +} + +multi_map_get :: proc{multi_map_get_array, multi_map_get_slice}; + +multi_map_get_array :: proc(m: $M/Map($Value), key: u64, items: ^Array(Value)) { + if items == nil do return; + e := multi_map_find_first(m, key); + for e != nil { + array_append(items, e.value); + e = multi_map_find_next(m, e); + } +} + +multi_map_get_slice :: proc(m: $M/Map($Value), key: u64, items: []Value) { + e := multi_map_find_first(m, key); + i := 0; + for e != nil && i < len(items) { + items[i] = e.value; + i += 1; + e = multi_map_find_next(m, e); + } +} + +multi_map_get_as_slice :: proc(m: $M/Map($Value), key: u64) -> []Value { + items: Array(Value); + array_init(&items, 0); + + e := multi_map_find_first(m, key); + for e != nil { + array_append(&items, e.value); + e = multi_map_find_next(m, e); + } + + return array_slice(items); +} + + +multi_map_insert :: proc(m: ^$M/Map($Value), key: u64, value: Value) { + if array_len(m.hash) == 0 { + _map_grow(m); + } + + i := _map_make(m, key); + array_get_ptr(m.entries, i).value = value; + if _map_full(m^) { + _map_grow(m); + } +} + +multi_map_remove :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) { + fr := _map_find_entry(m, e); + if fr.entry_index >= 0 { + _map_erase(m, fr); + } +} + +multi_map_remove_all :: proc(m: ^$M/Map($Value), key: u64) { + for map_exist(m^, key) { + map_remove(m, key); + } +} + + +/// Internal + + +Map_Find_Result :: struct { + hash_index: int, + entry_prev: int, + entry_index: int, +} + +_map_add_entry :: proc(m: ^$M/Map($Value), key: u64) -> int { + e: Map_Entry(Value); + e.key = key; + e.next = -1; + idx := array_len(m.entries); + array_push(&m.entries, e); + return idx; +} + +_map_erase :: proc(m: ^$M/Map, fr: Map_Find_Result) { + if fr.entry_index < 0 { + array_set(&m.hash, fr.hash_index, array_get(&m.entry_index).next); + } else { + array_get_ptr(m.entries, fr.entry_prev).next = array_get(&m.entry_index).next; + } + + if fr.entry_index == array_len(m.entries)-1 { + array_pop_back(&m.entries); + return; + } + + array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1)); + last = _map_find_key(m, array_get(&m.entries, fr.entry_index).key); + + if last.entry_prev < 0 { + array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index; + } else { + array_set(&m.hash, last.hash_index, fr.entry_index); + } +} + + +_map_find_key :: proc(m: $M/Map($Value), key: u64) -> Map_Find_Result { + fr: Map_Find_Result; + fr.hash_index = -1; + fr.entry_prev = -1; + fr.entry_index = -1; + + if array_len(m.hash) == 0 { + return fr; + } + + fr.hash_index = int(key % u64(array_len(m.hash))); + fr.entry_index = array_get(m.hash, fr.hash_index); + for fr.entry_index >= 0 { + it := array_get_ptr(m.entries, fr.entry_index); + if it.key == key { + return fr; + } + fr.entry_prev = fr.entry_index; + fr.entry_index = it.next; + } + return fr; +} + +_map_find_entry :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) -> Map_Find_Result { + fr: Map_Find_Result; + fr.hash_index = -1; + fr.entry_prev = -1; + fr.entry_index = -1; + + if array_len(m.hash) == 0 { + return fr; + } + + fr.hash_index = key % u64(array_len(m.hash)); + fr.entry_index = array_get(m.hash, fr.hash_index); + for fr.entry_index >= 0 { + it := array_get_ptr(m.entries, fr.entry_index); + if it == e { + return fr; + } + fr.entry_prev = fr.entry_index; + fr.entry_index = it.next; + } + return fr; +} + +_map_find_or_fail :: proc(m: $M/Map($Value), key: u64) -> int { + return _map_find_key(m, key).entry_index; +} +_map_find_or_make :: proc(m: ^$M/Map($Value), key: u64) -> int { + fr := _map_find_key(m^, key); + if fr.entry_index >= 0 { + return fr.entry_index; + } + + i := _map_add_entry(m, key); + if fr.entry_prev < 0 { + array_set(&m.hash, fr.hash_index, i); + } else { + array_get_ptr(m.entries, fr.entry_prev).next = i; + } + return i; +} + + +_map_make :: proc(m: ^$M/Map($Value), key: u64) -> int { + fr := _map_find_key(m^, key); + i := _map_add_entry(m, key); + + if fr.entry_prev < 0 { + array_set(&m.hash, fr.hash_index, i); + } else { + array_get_ptr(m.entries, fr.entry_prev).next = i; + } + + array_get_ptr(m.entries, i).next = fr.entry_index; + + return i; +} + + +_map_full :: proc(m: $M/Map($Value)) -> bool { + // TODO(bill): Determine good max load factor + return array_len(m.entries) >= (array_len(m.hash) / 4)*3; +} + +_map_grow :: proc(m: ^$M/Map($Value)) { + new_size := array_len(m.entries) * 4 + 7; // TODO(bill): Determine good grow rate + map_reserve(m, new_size); +} + + diff --git a/core/container/queue.odin b/core/container/queue.odin index ce6e75559..6e7e79ad3 100644 --- a/core/container/queue.odin +++ b/core/container/queue.odin @@ -6,6 +6,31 @@ Queue :: struct(T: typeid) { offset: int, } +/* +queue_init :: proc{ + queue_init_none, + queue_init_len, + queue_init_len_cap, +} +queue_delete +queue_clear +queue_len +queue_cap +queue_space +queue_get +queue_set +queue_reserve +queue_resize +queue_push :: proc{ + queue_push_back, + queue_push_elems, +}; +queue_push_front +queue_pop_front +queue_pop_back +queue_consume +*/ + queue_init_none :: proc(q: ^$Q/Queue($T), allocator := context.allocator) { queue_init_len(q, 0, allocator); } @@ -40,7 +65,6 @@ queue_space :: proc(q: $Q/Queue($T)) -> int { return array_len(q.data) - q.len; } - queue_get :: proc(q: $Q/Queue($T), index: int) -> T { i := (index + q.offset) % array_len(q.data); data := array_slice(q.data); From ab6947b2c704307a59218536cfb14563780e06d7 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 19 Apr 2020 23:57:40 +0100 Subject: [PATCH 18/24] `container.Small_Array` --- core/container/array.odin | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/core/container/array.odin b/core/container/array.odin index 1a9e8ac28..302374a6f 100644 --- a/core/container/array.odin +++ b/core/container/array.odin @@ -1,6 +1,7 @@ package container import "core:mem" +import "core:runtime" Array :: struct(T: typeid) { data: ^T, @@ -79,16 +80,16 @@ array_slice :: proc(a: $A/Array($T)) -> []T { array_get :: proc(a: $A/Array($T), index: int, loc := #caller_location) -> T { - assert(condition=0 <= index && index < a.len, loc=loc); + runtime.bounds_check_error_loc(loc, index, array_len(a)); return (^T)(uintptr(a.data) + size_of(T)*uintptr(index))^; } array_get_ptr :: proc(a: $A/Array($T), index: int, loc := #caller_location) -> ^T { - assert(condition=0 <= index && index < a.len, loc=loc); + runtime.bounds_check_error_loc(loc, index, array_len(a)); return (^T)(uintptr(a.data) + size_of(T)*uintptr(index)); } array_set :: proc(a: ^$A/Array($T), index: int, item: T, loc := #caller_location) { - assert(condition=0 <= index && index < a.len, loc=loc); + runtime.bounds_check_error_loc(loc, index, array_len(a^)); (^T)(uintptr(a.data) + size_of(T)*uintptr(index))^ = item; } @@ -128,15 +129,15 @@ array_push_front :: proc(a: ^$A/Array($T), item: T) { data[0] = item; } -array_pop_back :: proc(a: ^$A/Array($T)) -> T { - assert(a.len > 0); +array_pop_back :: proc(a: ^$A/Array($T), loc := #caller_location) -> T { + assert(condition=a.len > 0, loc=loc); item := array_get(a^, a.len-1); a.len -= 1; return item; } -array_pop_font :: proc(a: ^$A/Array($T)) -> T { - assert(a.len > 0); +array_pop_font :: proc(a: ^$A/Array($T), loc := #caller_location) -> T { + assert(condition=a.len > 0, loc=loc); item := array_get(a^, 0); s := array_slice(a^); copy(s[:], s[1:]); @@ -145,8 +146,8 @@ array_pop_font :: proc(a: ^$A/Array($T)) -> T { } -array_consume :: proc(a: ^$A/Array($T), count: int) { - assert(a.len >= count); +array_consume :: proc(a: ^$A/Array($T), count: int, loc := #caller_location) { + assert(condition=a.len >= count, loc=loc); a.len -= count; } @@ -172,10 +173,9 @@ array_push_back_elems :: proc(a: ^$A/Array($T), items: ..T) { array_grow(a, a.len + len(items)); } offset := a.len; - a.len += len(items); data := array_slice(a^); - n := copy(data[offset:], items); - a.len = offset + n; + n := copy(data[a.len:], items); + a.len += n; } array_push :: proc{array_push_back, array_push_back_elems}; From 400d6014d0bfdb00008d76364c5e57ad56175806 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Sun, 19 Apr 2020 23:57:57 +0100 Subject: [PATCH 19/24] `container.Small_Array` --- core/container/small_array.odin | 97 +++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 core/container/small_array.odin diff --git a/core/container/small_array.odin b/core/container/small_array.odin new file mode 100644 index 000000000..1f023ef36 --- /dev/null +++ b/core/container/small_array.odin @@ -0,0 +1,97 @@ +package container + +import "core:mem" + +Small_Array :: struct(N: int, T: typeid) where N >= 0 { + data: [N]T, + len: int, +} + + +small_array_len :: proc(a: $A/Small_Array) -> int { + return a.len; +} + +small_array_cap :: proc(a: $A/Small_Array) -> int { + return len(a.data); +} + +small_array_space :: proc(a: $A/Small_Array) -> int { + return len(a.data) - a.len; +} + +small_array_slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T { + return a.data[:a.len]; +} + + +small_array_get :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> T { + return a.data[index]; +} +small_array_get_ptr :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> ^T { + return &a.data[index]; +} + +small_array_set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T, loc := #caller_location) { + a.data[index] = item; +} + +small_array_resize :: proc(a: ^$A/Small_Array, length: int) { + a.len = min(length, len(a.data)); +} + + +small_array_push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool { + if a.len < len(a.data) { + a.len += 1; + a.data[a.len-1] = item; + return true; + } + return false; +} + +small_array_push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool { + if a.len < len(a.data) { + a.len += 1; + data := small_array_slice(a); + copy(data[1:], data[:]); + data[0] = item; + return true; + } + return false; +} + +small_array_pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T { + assert(condition=a.len > 0, loc=loc); + item := a.data[a.len-1]; + a.len -= 1; + return item; +} + +small_array_pop_font :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T { + assert(condition=a.len > 0, loc=loc); + item := a.data[0]; + s := small_array_slice(a); + copy(s[:], s[1:]); + a.len -= 1; + return item; +} + + +small_array_consume :: proc(a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) { + assert(condition=a.len >= count, loc=loc); + a.len -= count; +} + +small_array_clear :: proc(a: ^$A/Small_Array($N, $T)) { + small_array_resize(a, 0); +} + +small_array_push_back_elems :: proc(a: ^$A/Small_Array($N, $T), items: ..T) { + n := copy(a.data[a.len:], items[:]); + a.len += n; +} + +small_array_push :: proc{small_array_push_back, small_array_push_back_elems}; +small_array_append :: proc{small_array_push_back, small_array_push_back_elems}; + From f141e2868d5d698618c9cd7cb3ac7e8c7bfc8e9f Mon Sep 17 00:00:00 2001 From: gingerBill Date: Mon, 20 Apr 2020 00:12:01 +0100 Subject: [PATCH 20/24] `container.Set` --- core/container/map.odin | 14 +-- core/container/set.odin | 244 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 251 insertions(+), 7 deletions(-) create mode 100644 core/container/set.odin diff --git a/core/container/map.odin b/core/container/map.odin index b25b2e31f..65ac69ca4 100644 --- a/core/container/map.odin +++ b/core/container/map.odin @@ -68,7 +68,7 @@ map_delete :: proc(m: $M/Map($Value)) { map_has :: proc(m: $M/Map($Value), key: u64) -> bool { - return _map_find_or_fail(m, key) < 0; + return _map_find_or_fail(m, key) >= 0; } map_get :: proc(m: $M/Map($Value), key: u64) -> (res: Value, ok: bool) #optional_ok { @@ -108,7 +108,7 @@ map_set :: proc(m: ^$M/Map($Value), key: u64, value: Value) { } map_remove :: proc(m: ^$M/Map($Value), key: u64) { - fr := _map_find_key(m, key); + fr := _map_find_key(m^, key); if fr.entry_index >= 0 { _map_erase(m, fr); } @@ -250,10 +250,10 @@ _map_add_entry :: proc(m: ^$M/Map($Value), key: u64) -> int { } _map_erase :: proc(m: ^$M/Map, fr: Map_Find_Result) { - if fr.entry_index < 0 { - array_set(&m.hash, fr.hash_index, array_get(&m.entry_index).next); + if fr.entry_prev < 0 { + array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next); } else { - array_get_ptr(m.entries, fr.entry_prev).next = array_get(&m.entry_index).next; + array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next; } if fr.entry_index == array_len(m.entries)-1 { @@ -262,7 +262,7 @@ _map_erase :: proc(m: ^$M/Map, fr: Map_Find_Result) { } array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1)); - last = _map_find_key(m, array_get(&m.entries, fr.entry_index).key); + last := _map_find_key(m^, array_get(m.entries, fr.entry_index).key); if last.entry_prev < 0 { array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index; @@ -305,7 +305,7 @@ _map_find_entry :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) -> Map_Find_Re return fr; } - fr.hash_index = key % u64(array_len(m.hash)); + fr.hash_index = int(e.key % u64(array_len(m.hash))); fr.entry_index = array_get(m.hash, fr.hash_index); for fr.entry_index >= 0 { it := array_get_ptr(m.entries, fr.entry_index); diff --git a/core/container/set.odin b/core/container/set.odin new file mode 100644 index 000000000..016a8d808 --- /dev/null +++ b/core/container/set.odin @@ -0,0 +1,244 @@ +package container + +import "core:mem" +import "intrinsics" + + +Set :: struct { + hash: Array(int), + entries: Array(Set_Entry), +} + +Set_Entry :: struct { + key: u64, + next: int, +} + + +/* +set_init :: proc{ + set_init_none, + set_init_cap, +} +set_delete + +set_in +set_not_in +set_add +set_remove +set_reserve +set_clear +*/ + +set_init :: proc{set_init_none, set_init_cap}; + +set_init_none :: proc(m: ^Set, allocator := context.allocator) { + m.hash.allocator = allocator; + m.entries.allocator = allocator; +} + +set_init_cap :: proc(m: ^Set, cap: int, allocator := context.allocator) { + m.hash.allocator = allocator; + m.entries.allocator = allocator; + set_reserve(m, cap); +} + +set_delete :: proc(m: Set) { + array_delete(m.hash); + array_delete(m.entries); +} + + +set_in :: proc(m: Set, key: u64) -> bool { + return _set_find_or_fail(m, key) >= 0; +} +set_not_in :: proc(m: Set, key: u64) -> bool { + return _set_find_or_fail(m, key) < 0; +} + +set_add :: proc(m: ^Set, key: u64) { + if array_len(m.hash) == 0 { + _set_grow(m); + } + + i := _set_find_or_make(m, key); + if _set_full(m^) { + _set_grow(m); + } +} + +set_remove :: proc(m: ^Set, key: u64) { + fr := _set_find_key(m^, key); + if fr.entry_index >= 0 { + _set_erase(m, fr); + } +} + + +set_reserve :: proc(m: ^Set, new_size: int) { + nm: Set; + set_init(&nm, m.hash.allocator); + array_resize(&nm.hash, new_size); + array_reserve(&nm.entries, array_len(m.entries)); + + for i in 0.. bool { + a_entries := array_slice(a.entries); + b_entries := array_slice(b.entries); + if len(a_entries) != len(b_entries) { + return false; + } + for e in a_entries { + if set_not_in(b, e.key) { + return false; + } + } + + return true; +} + + + +/// Internal + +_set_add_entry :: proc(m: ^Set, key: u64) -> int { + e: Set_Entry; + e.key = key; + e.next = -1; + idx := array_len(m.entries); + array_push(&m.entries, e); + return idx; +} + +_set_erase :: proc(m: ^Set, fr: Map_Find_Result) { + if fr.entry_prev < 0 { + array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next); + } else { + array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next; + } + + if fr.entry_index == array_len(m.entries)-1 { + array_pop_back(&m.entries); + return; + } + + array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1)); + last := _set_find_key(m^, array_get(m.entries, fr.entry_index).key); + + if last.entry_prev < 0 { + array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index; + } else { + array_set(&m.hash, last.hash_index, fr.entry_index); + } +} + + +_set_find_key :: proc(m: Set, key: u64) -> Map_Find_Result { + fr: Map_Find_Result; + fr.hash_index = -1; + fr.entry_prev = -1; + fr.entry_index = -1; + + if array_len(m.hash) == 0 { + return fr; + } + + fr.hash_index = int(key % u64(array_len(m.hash))); + fr.entry_index = array_get(m.hash, fr.hash_index); + for fr.entry_index >= 0 { + it := array_get_ptr(m.entries, fr.entry_index); + if it.key == key { + return fr; + } + fr.entry_prev = fr.entry_index; + fr.entry_index = it.next; + } + return fr; +} + +_set_find_entry :: proc(m: ^Set, e: ^Set_Entry) -> Map_Find_Result { + fr: Map_Find_Result; + fr.hash_index = -1; + fr.entry_prev = -1; + fr.entry_index = -1; + + if array_len(m.hash) == 0 { + return fr; + } + + fr.hash_index = int(e.key % u64(array_len(m.hash))); + fr.entry_index = array_get(m.hash, fr.hash_index); + for fr.entry_index >= 0 { + it := array_get_ptr(m.entries, fr.entry_index); + if it == e { + return fr; + } + fr.entry_prev = fr.entry_index; + fr.entry_index = it.next; + } + return fr; +} + +_set_find_or_fail :: proc(m: Set, key: u64) -> int { + return _set_find_key(m, key).entry_index; +} +_set_find_or_make :: proc(m: ^Set, key: u64) -> int { + fr := _set_find_key(m^, key); + if fr.entry_index >= 0 { + return fr.entry_index; + } + + i := _set_add_entry(m, key); + if fr.entry_prev < 0 { + array_set(&m.hash, fr.hash_index, i); + } else { + array_get_ptr(m.entries, fr.entry_prev).next = i; + } + return i; +} + + +_set_make :: proc(m: ^Set, key: u64) -> int { + fr := _set_find_key(m^, key); + i := _set_add_entry(m, key); + + if fr.entry_prev < 0 { + array_set(&m.hash, fr.hash_index, i); + } else { + array_get_ptr(m.entries, fr.entry_prev).next = i; + } + + array_get_ptr(m.entries, i).next = fr.entry_index; + + return i; +} + + +_set_full :: proc(m: Set) -> bool { + // TODO(bill): Determine good max load factor + return array_len(m.entries) >= (array_len(m.hash) / 4)*3; +} + +_set_grow :: proc(m: ^Set) { + new_size := array_len(m.entries) * 4 + 7; // TODO(bill): Determine good grow rate + set_reserve(m, new_size); +} + + From d3f2f9480032f081cfafdb0223e6fb49a6834fd4 Mon Sep 17 00:00:00 2001 From: Tetralux Date: Tue, 21 Apr 2020 15:22:42 +0100 Subject: [PATCH 21/24] Remove outdated comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is what I get for removing the ability to provide a stack at the last minute.... 🤣 --- core/thread/thread_unix.odin | 9 --------- 1 file changed, 9 deletions(-) diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin index 53b8790d7..a11fe1c5d 100644 --- a/core/thread/thread_unix.odin +++ b/core/thread/thread_unix.odin @@ -40,15 +40,6 @@ Thread_Priority :: enum { // Creates a thread which will run the given procedure. // It then waits for `start` to be called. // -// You may provide a slice of bytes to use as the stack for the new thread, -// but if you do, you are expected to set up the guard pages yourself. -// -// The stack must also be aligned appropriately for the platform. -// We require it's at least 16 bytes aligned to help robustness; other -// platforms may require page-size alignment. -// Note also that pthreads requires the stack is at least 6 OS pages in size: -// 4 are required by pthreads, and two extra for guards pages that will be applied. -// create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread { __linux_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr { t := (^Thread)(t); From 8dd1b61aa248d08a14fe7b23dd5616bc6b9ab607 Mon Sep 17 00:00:00 2001 From: Tetralux Date: Tue, 21 Apr 2020 16:07:18 +0000 Subject: [PATCH 22/24] `sync.yield_processor` -> `sync.cpu_relax`; have it call `intrinsics.cpu_relax` --- core/sync/sync.odin | 9 +++++---- core/thread/thread_unix.odin | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/core/sync/sync.odin b/core/sync/sync.odin index 5a0512275..a899fdc8a 100644 --- a/core/sync/sync.odin +++ b/core/sync/sync.odin @@ -1,8 +1,9 @@ package sync -foreign { - @(link_name="llvm.x86.sse2.pause") - yield_processor :: proc() --- +import "core:intrinsics" + +cpu_relax :: inline proc() { + intrinsics.cpu_relax(); } Ticket_Mutex :: struct { @@ -18,7 +19,7 @@ ticket_mutex_init :: proc(m: ^Ticket_Mutex) { ticket_mutex_lock :: inline proc(m: ^Ticket_Mutex) { ticket := atomic_add(&m.ticket, 1, .Relaxed); for ticket != m.serving { - yield_processor(); + intrinsics.cpu_relax(); } } diff --git a/core/thread/thread_unix.odin b/core/thread/thread_unix.odin index 53b8790d7..fe31ce1ff 100644 --- a/core/thread/thread_unix.odin +++ b/core/thread/thread_unix.odin @@ -2,6 +2,7 @@ package thread; import "core:runtime" +import "core:intrinsics" import "core:sync" import "core:sys/unix" @@ -134,7 +135,7 @@ join :: proc(t: ^Thread) { if sync.atomic_swap(&t.already_joined, true, .Sequentially_Consistent) { for { if sync.atomic_load(&t.done, .Sequentially_Consistent) do return; - sync.yield_processor(); + intrinsics.cpu_relax(); } } From 4236e870d7090f09c0ff0b90873125573cb2910d Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 21 Apr 2020 23:26:02 +0100 Subject: [PATCH 23/24] Fix type assertion bug #619 --- core/runtime/internal.odin | 8 ++++++-- src/check_expr.cpp | 5 ++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/core/runtime/internal.odin b/core/runtime/internal.odin index 9dd166d8e..80094f28a 100644 --- a/core/runtime/internal.odin +++ b/core/runtime/internal.odin @@ -235,8 +235,12 @@ print_caller_location :: proc(fd: os.Handle, using loc: Source_Code_Location) { os.write_byte(fd, ')'); } print_typeid :: proc(fd: os.Handle, id: typeid) { - ti := type_info_of(id); - print_type(fd, ti); + if id == nil { + os.write_string(fd, "nil"); + } else { + ti := type_info_of(id); + print_type(fd, ti); + } } print_type :: proc(fd: os.Handle, ti: ^Type_Info) { if ti == nil { diff --git a/src/check_expr.cpp b/src/check_expr.cpp index d43d51e21..af9750ee4 100644 --- a/src/check_expr.cpp +++ b/src/check_expr.cpp @@ -5750,9 +5750,8 @@ bool check_assignment_arguments(CheckerContext *ctx, Array const &lhs, optional_ok = true; tuple_index += 2; - } else if (o.mode == Addressing_OptionalOk) { + } else if (o.mode == Addressing_OptionalOk && is_type_tuple(o.type)) { Type *tuple = o.type; - GB_ASSERT(is_type_tuple(tuple)); GB_ASSERT(tuple->Tuple.variables.count == 2); Ast *expr = unparen_expr(o.expr); if (expr->kind == Ast_CallExpr) { @@ -5769,7 +5768,7 @@ bool check_assignment_arguments(CheckerContext *ctx, Array const &lhs, } } else { TypeTuple *tuple = &o.type->Tuple; - if (o.mode == Addressing_OptionalOk && lhs.count == 1) { + if (o.mode == Addressing_OptionalOk && is_type_tuple(o.type) && lhs.count == 1) { GB_ASSERT(tuple->variables.count == 2); Ast *expr = unparen_expr(o.expr); if (expr->kind == Ast_CallExpr) { From 026bb8ed6f6e626c69ccfe32de5b2323d86b4620 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 21 Apr 2020 23:26:59 +0100 Subject: [PATCH 24/24] Remove unused headers --- core/container/map.odin | 3 --- core/container/set.odin | 4 ---- 2 files changed, 7 deletions(-) diff --git a/core/container/map.odin b/core/container/map.odin index 65ac69ca4..c7ed93ee3 100644 --- a/core/container/map.odin +++ b/core/container/map.odin @@ -1,8 +1,5 @@ package container -import "core:mem" -import "intrinsics" - Map :: struct(Value: typeid) { hash: Array(int), diff --git a/core/container/set.odin b/core/container/set.odin index 016a8d808..f62ea46d4 100644 --- a/core/container/set.odin +++ b/core/container/set.odin @@ -1,9 +1,5 @@ package container -import "core:mem" -import "intrinsics" - - Set :: struct { hash: Array(int), entries: Array(Set_Entry),