mirror of
https://github.com/Ed94/Odin.git
synced 2026-07-12 06:11:26 -07:00
Merge remote-tracking branch 'offical/master'
This commit is contained in:
+14
-14
@@ -8,7 +8,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM
|
||||
run: sudo apt-get install llvm-11 clang-11
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 17
|
||||
echo "/usr/lib/llvm-17/bin" >> $GITHUB_PATH
|
||||
- name: build odin
|
||||
run: ./build_odin.sh release
|
||||
- name: Odin version
|
||||
@@ -63,10 +67,8 @@ jobs:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM, and setup PATH
|
||||
run: |
|
||||
brew install llvm@13
|
||||
echo "/usr/local/opt/llvm@13/bin" >> $GITHUB_PATH
|
||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||
brew install llvm@17
|
||||
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||
- name: build odin
|
||||
run: ./build_odin.sh release
|
||||
- name: Odin version
|
||||
@@ -104,10 +106,8 @@ jobs:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm@13
|
||||
echo "/opt/homebrew/opt/llvm@13/bin" >> $GITHUB_PATH
|
||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||
brew install llvm@17
|
||||
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||
- name: build odin
|
||||
run: ./build_odin.sh release
|
||||
- name: Odin version
|
||||
@@ -128,11 +128,11 @@ jobs:
|
||||
- name: Odin check examples/all
|
||||
run: ./odin check examples/all -strict-style
|
||||
timeout-minutes: 10
|
||||
# - name: Core library tests
|
||||
# run: |
|
||||
# cd tests/core
|
||||
# make
|
||||
# timeout-minutes: 10
|
||||
- name: Core library tests
|
||||
run: |
|
||||
cd tests/core
|
||||
make
|
||||
timeout-minutes: 10
|
||||
- name: Odin internals tests
|
||||
run: |
|
||||
cd tests/internal
|
||||
|
||||
@@ -47,7 +47,11 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: (Linux) Download LLVM
|
||||
run: sudo apt-get install llvm-11 clang-11
|
||||
run: |
|
||||
wget https://apt.llvm.org/llvm.sh
|
||||
chmod +x llvm.sh
|
||||
sudo ./llvm.sh 17
|
||||
echo "/usr/lib/llvm-17/bin" >> $GITHUB_PATH
|
||||
- name: build odin
|
||||
run: make nightly
|
||||
- name: Odin run
|
||||
@@ -78,10 +82,8 @@ jobs:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm@13 dylibbundler
|
||||
echo "/usr/local/opt/llvm@13/bin" >> $GITHUB_PATH
|
||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||
brew install llvm@17 dylibbundler
|
||||
echo "/usr/local/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||
- name: build odin
|
||||
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
||||
# not link with libunwind bundled with LLVM but link with libunwind on the system.
|
||||
@@ -114,10 +116,8 @@ jobs:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Download LLVM and setup PATH
|
||||
run: |
|
||||
brew install llvm@13 dylibbundler
|
||||
echo "/opt/homebrew/opt/llvm@13/bin" >> $GITHUB_PATH
|
||||
TMP_PATH=$(xcrun --show-sdk-path)/user/include
|
||||
echo "CPATH=$TMP_PATH" >> $GITHUB_ENV
|
||||
brew install llvm@17 dylibbundler
|
||||
echo "/opt/homebrew/opt/llvm@17/bin" >> $GITHUB_PATH
|
||||
- name: build odin
|
||||
# These -L makes the linker prioritize system libraries over LLVM libraries, this is mainly to
|
||||
# not link with libunwind bundled with LLVM but link with libunwind on the system.
|
||||
|
||||
@@ -447,12 +447,12 @@ _append_elem :: #force_inline proc(array: ^$T/[dynamic]$E, arg: E, should_zero:
|
||||
}
|
||||
|
||||
@builtin
|
||||
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
return _append_elem(array, arg, true, loc=loc)
|
||||
}
|
||||
|
||||
@builtin
|
||||
non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
return _append_elem(array, arg, false, loc=loc)
|
||||
}
|
||||
|
||||
@@ -496,12 +496,12 @@ _append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, l
|
||||
}
|
||||
|
||||
@builtin
|
||||
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
return _append_elems(array, true, loc, ..args)
|
||||
}
|
||||
|
||||
@builtin
|
||||
non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||
return _append_elems(array, false, loc, ..args)
|
||||
}
|
||||
|
||||
@@ -556,7 +556,7 @@ append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: i
|
||||
|
||||
|
||||
@builtin
|
||||
inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
@@ -574,7 +574,7 @@ inject_at_elem :: proc(array: ^$T/[dynamic]$E, index: int, arg: E, loc := #calle
|
||||
}
|
||||
|
||||
@builtin
|
||||
inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
inject_at_elems :: proc(array: ^$T/[dynamic]$E, index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
|
||||
if array == nil {
|
||||
return
|
||||
}
|
||||
@@ -740,6 +740,9 @@ _resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int,
|
||||
a := (^Raw_Dynamic_Array)(array)
|
||||
|
||||
if length <= a.cap {
|
||||
if should_zero && a.len < length {
|
||||
intrinsics.mem_zero(([^]E)(a.data)[a.len:], (length-a.len)*size_of(E))
|
||||
}
|
||||
a.len = max(length, 0)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -962,9 +962,11 @@ udivmodti4 :: proc "c" (a, b: u128, rem: ^u128) -> u128 {
|
||||
return udivmod128(a, b, rem)
|
||||
}
|
||||
|
||||
@(link_name="__udivti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||
udivti3 :: proc "c" (a, b: u128) -> u128 {
|
||||
return udivmodti4(a, b, nil)
|
||||
when !IS_WASM {
|
||||
@(link_name="__udivti3", linkage=RUNTIME_LINKAGE, require=RUNTIME_REQUIRE)
|
||||
udivti3 :: proc "c" (a, b: u128) -> u128 {
|
||||
return udivmodti4(a, b, nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -7,19 +7,25 @@ ti_int :: struct #raw_union {
|
||||
all: i128,
|
||||
}
|
||||
|
||||
@(private="file")
|
||||
ti_uint :: struct #raw_union {
|
||||
using s: struct { lo, hi: u64 },
|
||||
all: u128,
|
||||
}
|
||||
|
||||
@(link_name="__ashlti3", linkage="strong")
|
||||
__ashlti3 :: proc "contextless" (a: i128, b_: u32) -> i128 {
|
||||
__ashlti3 :: proc "contextless" (la, ha: u64, b_: u32) -> i128 {
|
||||
bits_in_dword :: size_of(u32)*8
|
||||
b := u32(b_)
|
||||
|
||||
input, result: ti_int
|
||||
input.all = a
|
||||
input.lo, input.hi = la, ha
|
||||
if b & bits_in_dword != 0 {
|
||||
result.lo = 0
|
||||
result.hi = input.lo << (b-bits_in_dword)
|
||||
} else {
|
||||
if b == 0 {
|
||||
return a
|
||||
return input.all
|
||||
}
|
||||
result.lo = input.lo<<b
|
||||
result.hi = (input.hi<<b) | (input.lo>>(bits_in_dword-b))
|
||||
@@ -29,12 +35,20 @@ __ashlti3 :: proc "contextless" (a: i128, b_: u32) -> i128 {
|
||||
|
||||
|
||||
@(link_name="__multi3", linkage="strong")
|
||||
__multi3 :: proc "contextless" (a, b: i128) -> i128 {
|
||||
__multi3 :: proc "contextless" (la, ha, lb, hb: u64) -> i128 {
|
||||
x, y, r: ti_int
|
||||
|
||||
x.all = a
|
||||
y.all = b
|
||||
|
||||
x.lo, x.hi = la, ha
|
||||
y.lo, y.hi = lb, hb
|
||||
r.all = i128(x.lo * y.lo) // TODO this is incorrect
|
||||
r.hi += x.hi*y.lo + x.lo*y.hi
|
||||
return r.all
|
||||
}
|
||||
}
|
||||
|
||||
@(link_name="__udivti3", linkage="strong")
|
||||
udivti3 :: proc "c" (la, ha, lb, hb: u64) -> u128 {
|
||||
a, b: ti_uint
|
||||
a.lo, a.hi = la, ha
|
||||
b.lo, b.hi = lb, hb
|
||||
return udivmodti4(a.all, b.all, nil)
|
||||
}
|
||||
|
||||
@@ -226,7 +226,6 @@ writer_to_writer :: proc(b: ^Writer) -> (s: io.Writer) {
|
||||
|
||||
|
||||
|
||||
@(private)
|
||||
_writer_proc :: proc(stream_data: rawptr, mode: io.Stream_Mode, p: []byte, offset: i64, whence: io.Seek_From) -> (n: i64, err: io.Error) {
|
||||
b := (^Writer)(stream_data)
|
||||
#partial switch mode {
|
||||
|
||||
+18
-18
@@ -651,7 +651,7 @@ alpha_add_if_missing :: proc(img: ^Image, alpha_key := Alpha_Key{}, allocator :=
|
||||
// We have keyed alpha.
|
||||
o: GA_Pixel
|
||||
for p in inp {
|
||||
if p == key.r {
|
||||
if p.r == key.r {
|
||||
o = GA_Pixel{0, key.g}
|
||||
} else {
|
||||
o = GA_Pixel{p.r, 255}
|
||||
@@ -710,7 +710,7 @@ alpha_add_if_missing :: proc(img: ^Image, alpha_key := Alpha_Key{}, allocator :=
|
||||
// We have keyed alpha.
|
||||
o: GA_Pixel_16
|
||||
for p in inp {
|
||||
if p == key.r {
|
||||
if p.r == key.r {
|
||||
o = GA_Pixel_16{0, key.g}
|
||||
} else {
|
||||
o = GA_Pixel_16{p.r, 65535}
|
||||
@@ -842,11 +842,11 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
|
||||
bg := G_Pixel{}
|
||||
if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
|
||||
// Background is RGB 16-bit, take just the red channel's topmost byte.
|
||||
bg = u8(temp_bg.r >> 8)
|
||||
bg.r = u8(temp_bg.r >> 8)
|
||||
}
|
||||
|
||||
for p in inp {
|
||||
out[0] = bg if p == key else p
|
||||
out[0] = bg if p.r == key else p
|
||||
out = out[1:]
|
||||
}
|
||||
|
||||
@@ -865,8 +865,8 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
|
||||
for p in inp {
|
||||
a := f32(p.g) / 255.0
|
||||
c := ((1.0 - a) * bg + a * f32(p.r))
|
||||
out[0] = u8(c)
|
||||
out = out[1:]
|
||||
out[0].r = u8(c)
|
||||
out = out[1:]
|
||||
}
|
||||
|
||||
} else if .alpha_premultiply in options {
|
||||
@@ -874,14 +874,14 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
|
||||
for p in inp {
|
||||
a := f32(p.g) / 255.0
|
||||
c := f32(p.r) * a
|
||||
out[0] = u8(c)
|
||||
out = out[1:]
|
||||
out[0].r = u8(c)
|
||||
out = out[1:]
|
||||
}
|
||||
} else {
|
||||
// Just drop alpha on the floor.
|
||||
for p in inp {
|
||||
out[0] = p.r
|
||||
out = out[1:]
|
||||
out[0].r = p.r
|
||||
out = out[1:]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -951,11 +951,11 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
|
||||
bg := G_Pixel_16{}
|
||||
if temp_bg, temp_bg_ok := img.background.(RGB_Pixel_16); temp_bg_ok {
|
||||
// Background is RGB 16-bit, take just the red channel.
|
||||
bg = temp_bg.r
|
||||
bg.r = temp_bg.r
|
||||
}
|
||||
|
||||
for p in inp {
|
||||
out[0] = bg if p == key else p
|
||||
out[0] = bg if p.r == key else p
|
||||
out = out[1:]
|
||||
}
|
||||
|
||||
@@ -974,8 +974,8 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
|
||||
for p in inp {
|
||||
a := f32(p.g) / 65535.0
|
||||
c := ((1.0 - a) * bg + a * f32(p.r))
|
||||
out[0] = u16(c)
|
||||
out = out[1:]
|
||||
out[0].r = u16(c)
|
||||
out = out[1:]
|
||||
}
|
||||
|
||||
} else if .alpha_premultiply in options {
|
||||
@@ -983,14 +983,14 @@ alpha_drop_if_present :: proc(img: ^Image, options := Options{}, alpha_key := Al
|
||||
for p in inp {
|
||||
a := f32(p.g) / 65535.0
|
||||
c := f32(p.r) * a
|
||||
out[0] = u16(c)
|
||||
out = out[1:]
|
||||
out[0].r = u16(c)
|
||||
out = out[1:]
|
||||
}
|
||||
} else {
|
||||
// Just drop alpha on the floor.
|
||||
for p in inp {
|
||||
out[0] = p.r
|
||||
out = out[1:]
|
||||
out[0].r = p.r
|
||||
out = out[1:]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ Multi_Reader :: struct {
|
||||
readers: [dynamic]Reader,
|
||||
}
|
||||
|
||||
@(private)
|
||||
_multi_reader_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
|
||||
if mode == .Query {
|
||||
return query_utility({.Read, .Query})
|
||||
@@ -58,7 +57,6 @@ Multi_Writer :: struct {
|
||||
writers: [dynamic]Writer,
|
||||
}
|
||||
|
||||
@(private)
|
||||
_multi_writer_proc :: proc(stream_data: rawptr, mode: Stream_Mode, p: []byte, offset: i64, whence: Seek_From) -> (n: i64, err: Error) {
|
||||
if mode == .Query {
|
||||
return query_utility({.Write, .Query})
|
||||
|
||||
@@ -1112,7 +1112,7 @@ internal_int_prime_next_prime :: proc(a: ^Int, trials: int, bbs_style: bool, all
|
||||
Generate the restable.
|
||||
*/
|
||||
for x := 1; x < _PRIME_TAB_SIZE; x += 1 {
|
||||
res_tab = internal_mod(a, _private_prime_table[x]) or_return
|
||||
res_tab = cast(type_of(res_tab))(internal_mod(a, _private_prime_table[x]) or_return)
|
||||
}
|
||||
|
||||
for {
|
||||
|
||||
@@ -326,6 +326,7 @@ ln :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
|
||||
@(require_results)
|
||||
log2 :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
INVLN2 :: 1.4426950408889634073599246810018921374266459541529859341354494069
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = INVLN2 * math.ln(x[i])
|
||||
@@ -338,6 +339,7 @@ log2 :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
|
||||
@(require_results)
|
||||
log10 :: proc "contextless" (x: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
INVLN10 :: 0.4342944819032518276511289189166050822943970058036665661144537831
|
||||
when IS_ARRAY(T) {
|
||||
for i in 0..<len(T) {
|
||||
out[i] = INVLN10 * math.ln(x[i])
|
||||
@@ -355,7 +357,7 @@ log :: proc "contextless" (x, b: $T) -> (out: T) where IS_FLOAT(ELEM_TYPE(T)) {
|
||||
out[i] = math.ln(x[i]) / math.ln(cast(ELEM_TYPE(T))b[i])
|
||||
}
|
||||
} else {
|
||||
out = INVLN10 * math.ln(x) / math.ln(cast(ELEM_TYPE(T))b)
|
||||
out = math.ln(x) / math.ln(cast(ELEM_TYPE(T))b)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
+10
-6
@@ -597,6 +597,7 @@ Field_Flag :: enum {
|
||||
Any_Int,
|
||||
Subtype,
|
||||
By_Ptr,
|
||||
No_Broadcast,
|
||||
|
||||
Results,
|
||||
Tags,
|
||||
@@ -616,6 +617,7 @@ field_flag_strings := [Field_Flag]string{
|
||||
.Any_Int = "#any_int",
|
||||
.Subtype = "#subtype",
|
||||
.By_Ptr = "#by_ptr",
|
||||
.No_Broadcast ="#no_broadcast",
|
||||
|
||||
.Results = "results",
|
||||
.Tags = "field tag",
|
||||
@@ -624,12 +626,13 @@ field_flag_strings := [Field_Flag]string{
|
||||
}
|
||||
|
||||
field_hash_flag_strings := []struct{key: string, flag: Field_Flag}{
|
||||
{"no_alias", .No_Alias},
|
||||
{"c_vararg", .C_Vararg},
|
||||
{"const", .Const},
|
||||
{"any_int", .Any_Int},
|
||||
{"subtype", .Subtype},
|
||||
{"by_ptr", .By_Ptr},
|
||||
{"no_alias", .No_Alias},
|
||||
{"c_vararg", .C_Vararg},
|
||||
{"const", .Const},
|
||||
{"any_int", .Any_Int},
|
||||
{"subtype", .Subtype},
|
||||
{"by_ptr", .By_Ptr},
|
||||
{"no_broadcast", .No_Broadcast},
|
||||
}
|
||||
|
||||
|
||||
@@ -650,6 +653,7 @@ Field_Flags_Signature :: Field_Flags{
|
||||
.Const,
|
||||
.Any_Int,
|
||||
.By_Ptr,
|
||||
.No_Broadcast,
|
||||
.Default_Parameters,
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ String :: distinct Array(byte)
|
||||
|
||||
Version_Type_Major :: 0
|
||||
Version_Type_Minor :: 3
|
||||
Version_Type_Patch :: 0
|
||||
Version_Type_Patch :: 1
|
||||
|
||||
Version_Type :: struct {
|
||||
major, minor, patch: u8,
|
||||
@@ -102,13 +102,15 @@ Entity_Flag :: enum u32le {
|
||||
Foreign = 0,
|
||||
Export = 1,
|
||||
|
||||
Param_Using = 2, // using
|
||||
Param_Const = 3, // #const
|
||||
Param_Auto_Cast = 4, // auto_cast
|
||||
Param_Ellipsis = 5, // Variadic parameter
|
||||
Param_CVararg = 6, // #c_vararg
|
||||
Param_No_Alias = 7, // #no_alias
|
||||
Param_Any_Int = 8, // #any_int
|
||||
Param_Using = 2, // using
|
||||
Param_Const = 3, // #const
|
||||
Param_Auto_Cast = 4, // auto_cast
|
||||
Param_Ellipsis = 5, // Variadic parameter
|
||||
Param_CVararg = 6, // #c_vararg
|
||||
Param_No_Alias = 7, // #no_alias
|
||||
Param_Any_Int = 8, // #any_int
|
||||
Param_By_Ptr = 9, // #by_ptr
|
||||
Param_No_Broadcast = 10, // #no_broadcast
|
||||
|
||||
Bit_Field_Field = 19,
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ create_and_start_with_data :: proc(data: rawptr, fn: proc(data: rawptr), init_co
|
||||
t := create(thread_proc, priority)
|
||||
t.data = rawptr(fn)
|
||||
t.user_index = 1
|
||||
t.user_args = data
|
||||
t.user_args[0] = data
|
||||
if self_cleanup {
|
||||
t.flags += {.Self_Cleanup}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package datetime
|
||||
|
||||
// Ordinal 1 = Midnight Monday, January 1, 1 A.D. (Gregorian)
|
||||
// | Midnight Monday, January 3, 1 A.D. (Julian)
|
||||
Ordinal :: i64
|
||||
EPOCH :: Ordinal(1)
|
||||
|
||||
// Minimum and maximum dates and ordinals. Chosen for safe roundtripping.
|
||||
MIN_DATE :: Date{year = -25_252_734_927_766_552, month = 1, day = 1}
|
||||
MAX_DATE :: Date{year = 25_252_734_927_766_552, month = 12, day = 31}
|
||||
MIN_ORD :: Ordinal(-9_223_372_036_854_775_234)
|
||||
MAX_ORD :: Ordinal( 9_223_372_036_854_774_869)
|
||||
|
||||
Error :: enum {
|
||||
None,
|
||||
Invalid_Year,
|
||||
Invalid_Month,
|
||||
Invalid_Day,
|
||||
Invalid_Hour,
|
||||
Invalid_Minute,
|
||||
Invalid_Second,
|
||||
Invalid_Nano,
|
||||
Invalid_Ordinal,
|
||||
Invalid_Delta,
|
||||
}
|
||||
|
||||
Date :: struct {
|
||||
year: i64,
|
||||
month: i8,
|
||||
day: i8,
|
||||
}
|
||||
|
||||
Time :: struct {
|
||||
hour: i8,
|
||||
minute: i8,
|
||||
second: i8,
|
||||
nano: i32,
|
||||
}
|
||||
|
||||
DateTime :: struct {
|
||||
using date: Date,
|
||||
using time: Time,
|
||||
}
|
||||
|
||||
Delta :: struct {
|
||||
days: i64, // These are all i64 because we can also use it to add a number of seconds or nanos to a moment,
|
||||
seconds: i64, // that are then normalized within their respective ranges.
|
||||
nanos: i64,
|
||||
}
|
||||
|
||||
Month :: enum i8 {
|
||||
January = 1,
|
||||
February,
|
||||
March,
|
||||
April,
|
||||
May,
|
||||
June,
|
||||
July,
|
||||
August,
|
||||
September,
|
||||
October,
|
||||
November,
|
||||
December,
|
||||
}
|
||||
|
||||
Weekday :: enum i8 {
|
||||
Sunday = 0,
|
||||
Monday,
|
||||
Tuesday,
|
||||
Wednesday,
|
||||
Thursday,
|
||||
Friday,
|
||||
Saturday,
|
||||
}
|
||||
|
||||
@(private)
|
||||
MONTH_DAYS :: [?]i8{-1, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
|
||||
@@ -0,0 +1,272 @@
|
||||
/*
|
||||
Calendrical conversions using a proleptic Gregorian calendar.
|
||||
|
||||
Implemented using formulas from: Calendrical Calculations Ultimate Edition, Reingold & Dershowitz
|
||||
*/
|
||||
package datetime
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
// Procedures that return an Ordinal
|
||||
|
||||
date_to_ordinal :: proc "contextless" (date: Date) -> (ordinal: Ordinal, err: Error) {
|
||||
validate(date) or_return
|
||||
return unsafe_date_to_ordinal(date), .None
|
||||
}
|
||||
|
||||
components_to_ordinal :: proc "contextless" (#any_int year, #any_int month, #any_int day: i64) -> (ordinal: Ordinal, err: Error) {
|
||||
validate(year, month, day) or_return
|
||||
return unsafe_date_to_ordinal({year, i8(month), i8(day)}), .None
|
||||
}
|
||||
|
||||
// Procedures that return a Date
|
||||
|
||||
ordinal_to_date :: proc "contextless" (ordinal: Ordinal) -> (date: Date, err: Error) {
|
||||
validate(ordinal) or_return
|
||||
return unsafe_ordinal_to_date(ordinal), .None
|
||||
}
|
||||
|
||||
components_to_date :: proc "contextless" (#any_int year, #any_int month, #any_int day: i64) -> (date: Date, err: Error) {
|
||||
validate(year, month, day) or_return
|
||||
return Date{i64(year), i8(month), i8(day)}, .None
|
||||
}
|
||||
|
||||
components_to_time :: proc "contextless" (#any_int hour, #any_int minute, #any_int second: i64, #any_int nanos := i64(0)) -> (time: Time, err: Error) {
|
||||
validate(hour, minute, second, nanos) or_return
|
||||
return Time{i8(hour), i8(minute), i8(second), i32(nanos)}, .None
|
||||
}
|
||||
|
||||
components_to_datetime :: proc "contextless" (#any_int year, #any_int month, #any_int day, #any_int hour, #any_int minute, #any_int second: i64, #any_int nanos := i64(0)) -> (datetime: DateTime, err: Error) {
|
||||
date := components_to_date(year, month, day) or_return
|
||||
time := components_to_time(hour, minute, second, nanos) or_return
|
||||
return {date, time}, .None
|
||||
}
|
||||
|
||||
ordinal_to_datetime :: proc "contextless" (ordinal: Ordinal) -> (datetime: DateTime, err: Error) {
|
||||
d := ordinal_to_date(ordinal) or_return
|
||||
return {Date(d), {}}, .None
|
||||
}
|
||||
|
||||
day_of_week :: proc "contextless" (ordinal: Ordinal) -> (day: Weekday) {
|
||||
return Weekday((ordinal - EPOCH) %% 7)
|
||||
}
|
||||
|
||||
subtract_dates :: proc "contextless" (a, b: Date) -> (delta: Delta, err: Error) {
|
||||
ord_a := date_to_ordinal(a) or_return
|
||||
ord_b := date_to_ordinal(b) or_return
|
||||
|
||||
delta = Delta{days=ord_a - ord_b}
|
||||
return
|
||||
}
|
||||
|
||||
subtract_datetimes :: proc "contextless" (a, b: DateTime) -> (delta: Delta, err: Error) {
|
||||
ord_a := date_to_ordinal(a) or_return
|
||||
ord_b := date_to_ordinal(b) or_return
|
||||
|
||||
validate(a.time) or_return
|
||||
validate(b.time) or_return
|
||||
|
||||
seconds_a := i64(a.hour) * 3600 + i64(a.minute) * 60 + i64(a.second)
|
||||
seconds_b := i64(b.hour) * 3600 + i64(b.minute) * 60 + i64(b.second)
|
||||
|
||||
delta = Delta{ord_a - ord_b, seconds_a - seconds_b, i64(a.nano) - i64(b.nano)}
|
||||
return
|
||||
}
|
||||
|
||||
subtract_deltas :: proc "contextless" (a, b: Delta) -> (delta: Delta, err: Error) {
|
||||
delta = Delta{a.days - b.days, a.seconds - b.seconds, a.nanos - b.nanos}
|
||||
delta = normalize_delta(delta) or_return
|
||||
return
|
||||
}
|
||||
sub :: proc{subtract_datetimes, subtract_dates, subtract_deltas}
|
||||
|
||||
add_days_to_date :: proc "contextless" (a: Date, days: i64) -> (date: Date, err: Error) {
|
||||
ord := date_to_ordinal(a) or_return
|
||||
ord += days
|
||||
return ordinal_to_date(ord)
|
||||
}
|
||||
|
||||
add_delta_to_date :: proc "contextless" (a: Date, delta: Delta) -> (date: Date, err: Error) {
|
||||
ord := date_to_ordinal(a) or_return
|
||||
// Because the input is a Date, we add only the days from the Delta.
|
||||
ord += delta.days
|
||||
return ordinal_to_date(ord)
|
||||
}
|
||||
|
||||
add_delta_to_datetime :: proc "contextless" (a: DateTime, delta: Delta) -> (datetime: DateTime, err: Error) {
|
||||
days := date_to_ordinal(a) or_return
|
||||
|
||||
a_seconds := i64(a.hour) * 3600 + i64(a.minute) * 60 + i64(a.second)
|
||||
a_delta := Delta{days=days, seconds=a_seconds, nanos=i64(a.nano)}
|
||||
|
||||
sum_delta := Delta{days=a_delta.days + delta.days, seconds=a_delta.seconds + delta.seconds, nanos=a_delta.nanos + delta.nanos}
|
||||
sum_delta = normalize_delta(sum_delta) or_return
|
||||
|
||||
datetime.date = ordinal_to_date(sum_delta.days) or_return
|
||||
|
||||
hour, rem := divmod(sum_delta.seconds, 3600)
|
||||
minute, second := divmod(rem, 60)
|
||||
|
||||
datetime.time = components_to_time(hour, minute, second, sum_delta.nanos) or_return
|
||||
return
|
||||
}
|
||||
add :: proc{add_days_to_date, add_delta_to_date, add_delta_to_datetime}
|
||||
|
||||
day_number :: proc "contextless" (date: Date) -> (day_number: i64, err: Error) {
|
||||
validate(date) or_return
|
||||
|
||||
ord := unsafe_date_to_ordinal(date)
|
||||
_, day_number = unsafe_ordinal_to_year(ord)
|
||||
return
|
||||
}
|
||||
|
||||
days_remaining :: proc "contextless" (date: Date) -> (days_remaining: i64, err: Error) {
|
||||
// Alternative formulation `day_number` subtracted from 365 or 366 depending on leap year
|
||||
validate(date) or_return
|
||||
delta := sub(date, Date{date.year, 12, 31}) or_return
|
||||
return delta.days, .None
|
||||
}
|
||||
|
||||
last_day_of_month :: proc "contextless" (#any_int year: i64, #any_int month: i8) -> (day: i64, err: Error) {
|
||||
// Not using formula 2.27 from the book. This is far simpler and gives the same answer.
|
||||
|
||||
validate(Date{year, month, 1}) or_return
|
||||
month_days := MONTH_DAYS
|
||||
|
||||
day = i64(month_days[month])
|
||||
if month == 2 && is_leap_year(year) {
|
||||
day += 1
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
new_year :: proc "contextless" (#any_int year: i64) -> (new_year: Date, err: Error) {
|
||||
validate(year, 1, 1) or_return
|
||||
return {year, 1, 1}, .None
|
||||
}
|
||||
|
||||
year_end :: proc "contextless" (#any_int year: i64) -> (year_end: Date, err: Error) {
|
||||
validate(year, 12, 31) or_return
|
||||
return {year, 12, 31}, .None
|
||||
}
|
||||
|
||||
year_range :: proc (#any_int year: i64, allocator := context.allocator) -> (range: []Date) {
|
||||
is_leap := is_leap_year(year)
|
||||
|
||||
days := 366 if is_leap else 365
|
||||
range = make([]Date, days, allocator)
|
||||
|
||||
month_days := MONTH_DAYS
|
||||
if is_leap {
|
||||
month_days[2] = 29
|
||||
}
|
||||
|
||||
i := 0
|
||||
for month in 1..=len(month_days) {
|
||||
for day in 1..=month_days[month] {
|
||||
range[i], _ = components_to_date(year, month, day)
|
||||
i += 1
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
normalize_delta :: proc "contextless" (delta: Delta) -> (normalized: Delta, err: Error) {
|
||||
// Distribute nanos into seconds and remainder
|
||||
seconds, nanos := divmod(delta.nanos, 1e9)
|
||||
|
||||
// Add original seconds to rolled over seconds.
|
||||
seconds += delta.seconds
|
||||
days: i64
|
||||
|
||||
// Distribute seconds into number of days and remaining seconds.
|
||||
days, seconds = divmod(seconds, 24 * 3600)
|
||||
|
||||
// Add original days
|
||||
days += delta.days
|
||||
|
||||
if days <= MIN_ORD || days >= MAX_ORD {
|
||||
return {}, .Invalid_Delta
|
||||
}
|
||||
return Delta{days, seconds, nanos}, .None
|
||||
}
|
||||
|
||||
// The following procedures don't check whether their inputs are in a valid range.
|
||||
// They're still exported for those who know their inputs have been validated.
|
||||
|
||||
unsafe_date_to_ordinal :: proc "contextless" (date: Date) -> (ordinal: Ordinal) {
|
||||
year_minus_one := date.year - 1
|
||||
|
||||
// Day before epoch
|
||||
ordinal = EPOCH - 1
|
||||
|
||||
// Add non-leap days
|
||||
ordinal += 365 * year_minus_one
|
||||
|
||||
// Add leap days
|
||||
ordinal += floor_div(year_minus_one, 4) // Julian-rule leap days
|
||||
ordinal -= floor_div(year_minus_one, 100) // Prior century years
|
||||
ordinal += floor_div(year_minus_one, 400) // Prior 400-multiple years
|
||||
ordinal += floor_div(367 * i64(date.month) - 362, 12) // Prior days this year
|
||||
|
||||
// Apply correction
|
||||
if date.month <= 2 {
|
||||
ordinal += 0
|
||||
} else if is_leap_year(date.year) {
|
||||
ordinal -= 1
|
||||
} else {
|
||||
ordinal -= 2
|
||||
}
|
||||
|
||||
// Add days
|
||||
ordinal += i64(date.day)
|
||||
return
|
||||
}
|
||||
|
||||
unsafe_ordinal_to_year :: proc "contextless" (ordinal: Ordinal) -> (year: i64, day_ordinal: i64) {
|
||||
// Days after epoch
|
||||
d0 := ordinal - EPOCH
|
||||
|
||||
// Number of 400-year cycles and remainder
|
||||
n400, d1 := divmod(d0, 146097)
|
||||
|
||||
// Number of 100-year cycles and remainder
|
||||
n100, d2 := divmod(d1, 36524)
|
||||
|
||||
// Number of 4-year cycles and remainder
|
||||
n4, d3 := divmod(d2, 1461)
|
||||
|
||||
// Number of remaining days
|
||||
n1, d4 := divmod(d3, 365)
|
||||
|
||||
year = 400 * n400 + 100 * n100 + 4 * n4 + n1
|
||||
|
||||
if n1 != 4 && n100 != 4 {
|
||||
day_ordinal = d4 + 1
|
||||
} else {
|
||||
day_ordinal = 366
|
||||
}
|
||||
|
||||
if n100 == 4 || n1 == 4 {
|
||||
return year, day_ordinal
|
||||
}
|
||||
return year + 1, day_ordinal
|
||||
}
|
||||
|
||||
unsafe_ordinal_to_date :: proc "contextless" (ordinal: Ordinal) -> (date: Date) {
|
||||
year, _ := unsafe_ordinal_to_year(ordinal)
|
||||
|
||||
prior_days := ordinal - unsafe_date_to_ordinal(Date{year, 1, 1})
|
||||
correction := Ordinal(2)
|
||||
|
||||
if ordinal < unsafe_date_to_ordinal(Date{year, 3, 1}) {
|
||||
correction = 0
|
||||
} else if is_leap_year(year) {
|
||||
correction = 1
|
||||
}
|
||||
|
||||
month := i8(floor_div((12 * (prior_days + correction) + 373), 367))
|
||||
day := i8(ordinal - unsafe_date_to_ordinal(Date{year, month, 1}) + 1)
|
||||
|
||||
return {year, month, day}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package datetime
|
||||
|
||||
// Internal helper functions for calendrical conversions
|
||||
|
||||
import "base:intrinsics"
|
||||
|
||||
sign :: proc "contextless" (v: i64) -> (res: i64) {
|
||||
if v == 0 {
|
||||
return 0
|
||||
} else if v > 0 {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
// Caller has to ensure y != 0
|
||||
divmod :: proc "contextless" (x, y: $T, loc := #caller_location) -> (a: T, r: T)
|
||||
where intrinsics.type_is_integer(T) {
|
||||
a = x / y
|
||||
r = x % y
|
||||
if (r > 0 && y < 0) || (r < 0 && y > 0) {
|
||||
a -= 1
|
||||
r += y
|
||||
}
|
||||
return a, r
|
||||
}
|
||||
|
||||
// Divides and floors
|
||||
floor_div :: proc "contextless" (x, y: $T) -> (res: T)
|
||||
where intrinsics.type_is_integer(T) {
|
||||
res = x / y
|
||||
r := x % y
|
||||
if (r > 0 && y < 0) || (r < 0 && y > 0) {
|
||||
res -= 1
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// Half open: x mod [1..b]
|
||||
interval_mod :: proc "contextless" (x, a, b: i64) -> (res: i64) {
|
||||
if a == b {
|
||||
return x
|
||||
}
|
||||
return a + ((x - a) %% (b - a))
|
||||
}
|
||||
|
||||
// x mod [1..b]
|
||||
adjusted_remainder :: proc "contextless" (x, b: i64) -> (res: i64) {
|
||||
m := x %% b
|
||||
return b if m == 0 else m
|
||||
}
|
||||
|
||||
gcd :: proc "contextless" (x, y: i64) -> (res: i64) {
|
||||
if y == 0 {
|
||||
return x
|
||||
}
|
||||
|
||||
m := x %% y
|
||||
return gcd(y, m)
|
||||
}
|
||||
|
||||
lcm :: proc "contextless" (x, y: i64) -> (res: i64) {
|
||||
return x * y / gcd(x, y)
|
||||
}
|
||||
|
||||
sum :: proc "contextless" (i: i64, f: proc "contextless" (n: i64) -> i64, cond: proc "contextless" (n: i64) -> bool) -> (res: i64) {
|
||||
for idx := i; cond(idx); idx += 1 {
|
||||
res += f(idx)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
product :: proc "contextless" (i: i64, f: proc "contextless" (n: i64) -> i64, cond: proc "contextless" (n: i64) -> bool) -> (res: i64) {
|
||||
res = 1
|
||||
for idx := i; cond(idx); idx += 1 {
|
||||
res *= f(idx)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
smallest :: proc "contextless" (k: i64, cond: proc "contextless" (n: i64) -> bool) -> (d: i64) {
|
||||
k := k
|
||||
for !cond(k) {
|
||||
k += 1
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
biggest :: proc "contextless" (k: i64, cond: proc "contextless" (n: i64) -> bool) -> (d: i64) {
|
||||
k := k
|
||||
for !cond(k) {
|
||||
k -= 1
|
||||
}
|
||||
return k
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package datetime
|
||||
|
||||
// Validation helpers
|
||||
is_leap_year :: proc "contextless" (#any_int year: i64) -> (leap: bool) {
|
||||
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
|
||||
}
|
||||
|
||||
validate_date :: proc "contextless" (date: Date) -> (err: Error) {
|
||||
return validate(date.year, date.month, date.day)
|
||||
}
|
||||
|
||||
validate_year_month_day :: proc "contextless" (#any_int year, #any_int month, #any_int day: i64) -> (err: Error) {
|
||||
if year < MIN_DATE.year || year > MAX_DATE.year {
|
||||
return .Invalid_Year
|
||||
}
|
||||
if month < 1 || month > 12 {
|
||||
return .Invalid_Month
|
||||
}
|
||||
|
||||
month_days := MONTH_DAYS
|
||||
days_this_month := month_days[month]
|
||||
if month == 2 && is_leap_year(year) {
|
||||
days_this_month = 29
|
||||
}
|
||||
|
||||
if day < 1 || day > i64(days_this_month) {
|
||||
return .Invalid_Day
|
||||
}
|
||||
return .None
|
||||
}
|
||||
|
||||
validate_ordinal :: proc "contextless" (ordinal: Ordinal) -> (err: Error) {
|
||||
if ordinal < MIN_ORD || ordinal > MAX_ORD {
|
||||
return .Invalid_Ordinal
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
validate_time :: proc "contextless" (time: Time) -> (err: Error) {
|
||||
return validate(time.hour, time.minute, time.second, time.nano)
|
||||
}
|
||||
|
||||
validate_hour_minute_second :: proc "contextless" (#any_int hour, #any_int minute, #any_int second, #any_int nano: i64) -> (err: Error) {
|
||||
if hour < 0 || hour > 23 {
|
||||
return .Invalid_Hour
|
||||
}
|
||||
if minute < 0 || minute > 59 {
|
||||
return .Invalid_Minute
|
||||
}
|
||||
if second < 0 || second > 59 {
|
||||
return .Invalid_Second
|
||||
}
|
||||
if nano < 0 || nano > 1e9 {
|
||||
return .Invalid_Nano
|
||||
}
|
||||
return .None
|
||||
}
|
||||
|
||||
validate_datetime :: proc "contextless" (using datetime: DateTime) -> (err: Error) {
|
||||
validate(date) or_return
|
||||
validate(time) or_return
|
||||
return .None
|
||||
}
|
||||
|
||||
validate :: proc{
|
||||
validate_date,
|
||||
validate_year_month_day,
|
||||
validate_ordinal,
|
||||
validate_hour_minute_second,
|
||||
validate_time,
|
||||
validate_datetime,
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
package time
|
||||
// Parsing RFC 3339 date/time strings into time.Time.
|
||||
// See https://www.rfc-editor.org/rfc/rfc3339 for the definition
|
||||
|
||||
import dt "core:time/datetime"
|
||||
|
||||
// Parses an RFC 3339 string and returns Time in UTC, with any UTC offset applied to it.
|
||||
// Only 4-digit years are accepted.
|
||||
// Optional pointer to boolean `is_leap` will return `true` if the moment was a leap second.
|
||||
// Leap seconds are smeared into 23:59:59.
|
||||
rfc3339_to_time_utc :: proc(rfc_datetime: string, is_leap: ^bool = nil) -> (res: Time, consumed: int) {
|
||||
offset: int
|
||||
|
||||
res, offset, consumed = rfc3339_to_time_and_offset(rfc_datetime, is_leap)
|
||||
res._nsec += (i64(-offset) * i64(Minute))
|
||||
return res, consumed
|
||||
}
|
||||
|
||||
// Parses an RFC 3339 string and returns Time and a UTC offset in minutes.
|
||||
// e.g. 1985-04-12T23:20:50.52Z
|
||||
// Note: Only 4-digit years are accepted.
|
||||
// Optional pointer to boolean `is_leap` will return `true` if the moment was a leap second.
|
||||
// Leap seconds are smeared into 23:59:59.
|
||||
rfc3339_to_time_and_offset :: proc(rfc_datetime: string, is_leap: ^bool = nil) -> (res: Time, utc_offset: int, consumed: int) {
|
||||
moment, offset, leap_second, count := rfc3339_to_components(rfc_datetime)
|
||||
if count == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
if is_leap != nil {
|
||||
is_leap^ = leap_second
|
||||
}
|
||||
|
||||
if _res, ok := datetime_to_time(moment.year, moment.month, moment.day, moment.hour, moment.minute, moment.second, moment.nano); !ok {
|
||||
return {}, 0, 0
|
||||
} else {
|
||||
return _res, offset, count
|
||||
}
|
||||
}
|
||||
|
||||
// Parses an RFC 3339 string and returns Time and a UTC offset in minutes.
|
||||
// e.g. 1985-04-12T23:20:50.52Z
|
||||
// Performs no validation on whether components are valid, e.g. it'll return hour = 25 if that's what it's given
|
||||
rfc3339_to_components :: proc(rfc_datetime: string) -> (res: dt.DateTime, utc_offset: int, is_leap: bool, consumed: int) {
|
||||
moment, offset, count, leap_second, ok := _rfc3339_to_components(rfc_datetime)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
return moment, offset, leap_second, count
|
||||
}
|
||||
|
||||
// Parses an RFC 3339 string and returns datetime.DateTime.
|
||||
// Performs no validation on whether components are valid, e.g. it'll return hour = 25 if that's what it's given
|
||||
@(private)
|
||||
_rfc3339_to_components :: proc(rfc_datetime: string) -> (res: dt.DateTime, utc_offset: int, consumed: int, is_leap: bool, ok: bool) {
|
||||
// A compliant date is at minimum 20 characters long, e.g. YYYY-MM-DDThh:mm:ssZ
|
||||
(len(rfc_datetime) >= 20) or_return
|
||||
|
||||
// Scan and eat YYYY-MM-DD[Tt], then scan and eat HH:MM:SS, leave separator
|
||||
year := scan_digits(rfc_datetime[0:], "-", 4) or_return
|
||||
month := scan_digits(rfc_datetime[5:], "-", 2) or_return
|
||||
day := scan_digits(rfc_datetime[8:], "Tt", 2) or_return
|
||||
hour := scan_digits(rfc_datetime[11:], ":", 2) or_return
|
||||
minute := scan_digits(rfc_datetime[14:], ":", 2) or_return
|
||||
second := scan_digits(rfc_datetime[17:], "", 2) or_return
|
||||
nanos := 0
|
||||
count := 19
|
||||
|
||||
if rfc_datetime[count] == '.' {
|
||||
// Scan hundredths. The string must be at least 4 bytes long (.hhZ)
|
||||
(len(rfc_datetime[count:]) >= 4) or_return
|
||||
hundredths := scan_digits(rfc_datetime[count+1:], "", 2) or_return
|
||||
count += 3
|
||||
nanos = 10_000_000 * hundredths
|
||||
}
|
||||
|
||||
// Leap second handling
|
||||
if minute == 59 && second == 60 {
|
||||
second = 59
|
||||
is_leap = true
|
||||
}
|
||||
|
||||
err: dt.Error
|
||||
if res, err = dt.components_to_datetime(year, month, day, hour, minute, second, nanos); err != .None {
|
||||
return {}, 0, 0, false, false
|
||||
}
|
||||
|
||||
// Scan UTC offset
|
||||
switch rfc_datetime[count] {
|
||||
case 'Z':
|
||||
utc_offset = 0
|
||||
count += 1
|
||||
case '+', '-':
|
||||
(len(rfc_datetime[count:]) >= 6) or_return
|
||||
offset_hour := scan_digits(rfc_datetime[count+1:], ":", 2) or_return
|
||||
offset_minute := scan_digits(rfc_datetime[count+4:], "", 2) or_return
|
||||
|
||||
utc_offset = 60 * offset_hour + offset_minute
|
||||
utc_offset *= -1 if rfc_datetime[count] == '-' else 1
|
||||
count += 6
|
||||
}
|
||||
return res, utc_offset, count, is_leap, true
|
||||
}
|
||||
|
||||
@(private)
|
||||
scan_digits :: proc(s: string, sep: string, count: int) -> (res: int, ok: bool) {
|
||||
needed := count + min(1, len(sep))
|
||||
(len(s) >= needed) or_return
|
||||
|
||||
#no_bounds_check for i in 0..<count {
|
||||
if v := s[i]; v >= '0' && v <= '9' {
|
||||
res = res * 10 + int(v - '0')
|
||||
} else {
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
found_sep := len(sep) == 0
|
||||
#no_bounds_check for v in sep {
|
||||
found_sep |= rune(s[count]) == v
|
||||
}
|
||||
return res, found_sep
|
||||
}
|
||||
+27
-52
@@ -1,6 +1,7 @@
|
||||
package time
|
||||
|
||||
import "base:intrinsics"
|
||||
import "base:intrinsics"
|
||||
import dt "core:time/datetime"
|
||||
|
||||
Duration :: distinct i64
|
||||
|
||||
@@ -299,10 +300,6 @@ _time_abs :: proc "contextless" (t: Time) -> u64 {
|
||||
|
||||
@(private)
|
||||
_abs_date :: proc "contextless" (abs: u64, full: bool) -> (year: int, month: Month, day: int, yday: int) {
|
||||
_is_leap_year :: proc "contextless" (year: int) -> bool {
|
||||
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
|
||||
}
|
||||
|
||||
d := abs / SECONDS_PER_DAY
|
||||
|
||||
// 400 year cycles
|
||||
@@ -335,7 +332,7 @@ _abs_date :: proc "contextless" (abs: u64, full: bool) -> (year: int, month: Mon
|
||||
|
||||
day = yday
|
||||
|
||||
if _is_leap_year(year) {
|
||||
if is_leap_year(year) {
|
||||
switch {
|
||||
case day > 31+29-1:
|
||||
day -= 1
|
||||
@@ -360,57 +357,35 @@ _abs_date :: proc "contextless" (abs: u64, full: bool) -> (year: int, month: Mon
|
||||
return
|
||||
}
|
||||
|
||||
datetime_to_time :: proc "contextless" (year, month, day, hour, minute, second: int, nsec := int(0)) -> (t: Time, ok: bool) {
|
||||
divmod :: proc "contextless" (year: int, divisor: int) -> (div: int, mod: int) {
|
||||
if divisor <= 0 {
|
||||
intrinsics.debug_trap()
|
||||
}
|
||||
div = int(year / divisor)
|
||||
mod = year % divisor
|
||||
components_to_time :: proc "contextless" (#any_int year, #any_int month, #any_int day, #any_int hour, #any_int minute, #any_int second: i64, #any_int nsec := i64(0)) -> (t: Time, ok: bool) {
|
||||
this_date, err := dt.components_to_datetime(year, month, day, hour, minute, second, nsec)
|
||||
if err != .None {
|
||||
return
|
||||
}
|
||||
_is_leap_year :: proc "contextless" (year: int) -> bool {
|
||||
return year%4 == 0 && (year%100 != 0 || year%400 == 0)
|
||||
return compound_to_time(this_date)
|
||||
}
|
||||
|
||||
compound_to_time :: proc "contextless" (datetime: dt.DateTime) -> (t: Time, ok: bool) {
|
||||
unix_epoch := dt.DateTime{{1970, 1, 1}, {0, 0, 0, 0}}
|
||||
delta, err := dt.sub(datetime, unix_epoch)
|
||||
ok = err == .None
|
||||
|
||||
seconds := delta.days * 86_400 + delta.seconds
|
||||
nanoseconds := i128(seconds) * 1e9 + i128(delta.nanos)
|
||||
|
||||
// Can this moment be represented in i64 worth of nanoseconds?
|
||||
// min(Time): 1677-09-21 00:12:44.145224192 +0000 UTC
|
||||
// max(Time): 2262-04-11 23:47:16.854775807 +0000 UTC
|
||||
if nanoseconds < i128(min(i64)) || nanoseconds > i128(max(i64)) {
|
||||
return {}, false
|
||||
}
|
||||
return Time{_nsec=i64(nanoseconds)}, true
|
||||
}
|
||||
|
||||
datetime_to_time :: proc{components_to_time, compound_to_time}
|
||||
|
||||
ok = true
|
||||
|
||||
_y := year - 1970
|
||||
_m := month - 1
|
||||
_d := day - 1
|
||||
|
||||
if month < 1 || month > 12 {
|
||||
_m %= 12; ok = false
|
||||
}
|
||||
if day < 1 || day > 31 {
|
||||
_d %= 31; ok = false
|
||||
}
|
||||
|
||||
s := i64(0)
|
||||
div, mod := divmod(_y, 400)
|
||||
days := div * DAYS_PER_400_YEARS
|
||||
|
||||
div, mod = divmod(mod, 100)
|
||||
days += div * DAYS_PER_100_YEARS
|
||||
|
||||
div, mod = divmod(mod, 4)
|
||||
days += (div * DAYS_PER_4_YEARS) + (mod * 365)
|
||||
|
||||
days += int(days_before[_m]) + _d
|
||||
|
||||
if _is_leap_year(year) && _m >= 2 {
|
||||
days += 1
|
||||
}
|
||||
|
||||
s += i64(days) * SECONDS_PER_DAY
|
||||
s += i64(hour) * SECONDS_PER_HOUR
|
||||
s += i64(minute) * SECONDS_PER_MINUTE
|
||||
s += i64(second)
|
||||
|
||||
t._nsec = (s * 1e9) + i64(nsec)
|
||||
|
||||
return
|
||||
is_leap_year :: proc "contextless" (year: int) -> (leap: bool) {
|
||||
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
|
||||
}
|
||||
|
||||
days_before := [?]i32{
|
||||
|
||||
@@ -117,6 +117,7 @@ import table "core:text/table"
|
||||
import edit "core:text/edit"
|
||||
import thread "core:thread"
|
||||
import time "core:time"
|
||||
import datetime "core:time/datetime"
|
||||
|
||||
import sysinfo "core:sys/info"
|
||||
|
||||
@@ -225,6 +226,7 @@ _ :: table
|
||||
_ :: edit
|
||||
_ :: thread
|
||||
_ :: time
|
||||
_ :: datetime
|
||||
_ :: sysinfo
|
||||
_ :: unicode
|
||||
_ :: utf8
|
||||
|
||||
+20
-14
@@ -510,7 +510,7 @@ gb_global TargetMetrics target_darwin_amd64 = {
|
||||
TargetOs_darwin,
|
||||
TargetArch_amd64,
|
||||
8, 8, 8, 16,
|
||||
str_lit("x86_64-apple-darwin"),
|
||||
str_lit("x86_64-apple-macosx"), // NOTE: Changes during initialization based on build flags.
|
||||
str_lit("e-m:o-i64:64-f80:128-n8:16:32:64-S128"),
|
||||
};
|
||||
|
||||
@@ -518,7 +518,7 @@ gb_global TargetMetrics target_darwin_arm64 = {
|
||||
TargetOs_darwin,
|
||||
TargetArch_arm64,
|
||||
8, 8, 8, 16,
|
||||
str_lit("arm64-apple-macosx11.0.0"),
|
||||
str_lit("arm64-apple-macosx"), // NOTE: Changes during initialization based on build flags.
|
||||
str_lit("e-m:o-i64:64-i128:128-n32:64-S128"),
|
||||
};
|
||||
|
||||
@@ -1418,19 +1418,25 @@ gb_internal void init_build_context(TargetMetrics *cross_target, Subtarget subta
|
||||
}
|
||||
|
||||
bc->metrics = *metrics;
|
||||
switch (subtarget) {
|
||||
case Subtarget_Default:
|
||||
break;
|
||||
case Subtarget_iOS:
|
||||
GB_ASSERT(metrics->os == TargetOs_darwin);
|
||||
if (metrics->arch == TargetArch_arm64) {
|
||||
bc->metrics.target_triplet = str_lit("arm64-apple-ios");
|
||||
} else if (metrics->arch == TargetArch_amd64) {
|
||||
bc->metrics.target_triplet = str_lit("x86_64-apple-ios");
|
||||
} else {
|
||||
GB_PANIC("Unknown architecture for darwin");
|
||||
if (metrics->os == TargetOs_darwin) {
|
||||
if (bc->minimum_os_version_string.len == 0) {
|
||||
bc->minimum_os_version_string = str_lit("11.0.0");
|
||||
}
|
||||
|
||||
switch (subtarget) {
|
||||
case Subtarget_Default:
|
||||
bc->metrics.target_triplet = concatenate_strings(permanent_allocator(), bc->metrics.target_triplet, bc->minimum_os_version_string);
|
||||
break;
|
||||
case Subtarget_iOS:
|
||||
if (metrics->arch == TargetArch_arm64) {
|
||||
bc->metrics.target_triplet = str_lit("arm64-apple-ios");
|
||||
} else if (metrics->arch == TargetArch_amd64) {
|
||||
bc->metrics.target_triplet = str_lit("x86_64-apple-ios");
|
||||
} else {
|
||||
GB_PANIC("Unknown architecture for darwin");
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
bc->ODIN_OS = target_os_names[metrics->os];
|
||||
|
||||
@@ -89,6 +89,7 @@ gb_internal void check_or_else_split_types(CheckerContext *c, Operand *x, String
|
||||
|
||||
|
||||
gb_internal void check_or_else_expr_no_value_error(CheckerContext *c, String const &name, Operand const &x, Type *type_hint) {
|
||||
ERROR_BLOCK();
|
||||
gbString t = type_to_string(x.type);
|
||||
error(x.expr, "'%.*s' does not return a value, value is of type %s", LIT(name), t);
|
||||
if (is_type_union(type_deref(x.type))) {
|
||||
@@ -1565,6 +1566,7 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
|
||||
}
|
||||
|
||||
if (!operand->value.value_bool) {
|
||||
ERROR_BLOCK();
|
||||
gbString arg1 = expr_to_string(ce->args[0]);
|
||||
gbString arg2 = {};
|
||||
|
||||
@@ -1590,6 +1592,7 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
|
||||
operand->type = t_untyped_bool;
|
||||
operand->mode = Addressing_Constant;
|
||||
} else if (name == "panic") {
|
||||
ERROR_BLOCK();
|
||||
if (ce->args.count != 1) {
|
||||
error(call, "'#panic' expects 1 argument, got %td", ce->args.count);
|
||||
return false;
|
||||
@@ -3390,6 +3393,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
elem->Struct.tags = gb_alloc_array(permanent_allocator(), String, fields.count);
|
||||
elem->Struct.node = dummy_node_struct;
|
||||
type_set_offsets(elem);
|
||||
wait_signal_set(&elem->Struct.fields_wait_signal);
|
||||
}
|
||||
|
||||
Type *soa_type = make_soa_struct_slice(c, dummy_node_soa, nullptr, elem);
|
||||
@@ -3763,6 +3767,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
soa_struct->Struct.tags[i] = old_struct->Struct.tags[i];
|
||||
}
|
||||
}
|
||||
wait_signal_set(&soa_struct->Struct.fields_wait_signal);
|
||||
|
||||
Token token = {};
|
||||
token.string = str_lit("Base_Type");
|
||||
|
||||
@@ -1630,6 +1630,7 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
|
||||
Entity *uvar = entry.uvar;
|
||||
Entity *prev = scope_insert_no_mutex(ctx->scope, uvar);
|
||||
if (prev != nullptr) {
|
||||
ERROR_BLOCK();
|
||||
error(e->token, "Namespace collision while 'using' procedure argument '%.*s' of: %.*s", LIT(e->token.string), LIT(prev->token.string));
|
||||
error_line("%.*s != %.*s\n", LIT(uvar->token.string), LIT(prev->token.string));
|
||||
break;
|
||||
|
||||
+121
-48
@@ -79,7 +79,6 @@ gb_internal Type * check_type_expr (CheckerContext *c, Ast *exp
|
||||
gb_internal Type * make_optional_ok_type (Type *value, bool typed=true);
|
||||
gb_internal Entity * check_selector (CheckerContext *c, Operand *operand, Ast *node, Type *type_hint);
|
||||
gb_internal Entity * check_ident (CheckerContext *c, Operand *o, Ast *n, Type *named_type, Type *type_hint, bool allow_import_name);
|
||||
gb_internal Entity * find_polymorphic_record_entity (CheckerContext *c, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure);
|
||||
gb_internal void check_not_tuple (CheckerContext *c, Operand *operand);
|
||||
gb_internal void convert_to_typed (CheckerContext *c, Operand *operand, Type *target_type);
|
||||
gb_internal gbString expr_to_string (Ast *expression);
|
||||
@@ -121,6 +120,8 @@ gb_internal isize get_procedure_param_count_excluding_defaults(Type *pt, isize *
|
||||
|
||||
gb_internal bool is_expr_inferred_fixed_array(Ast *type_expr);
|
||||
|
||||
gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array<Operand> const &ordered_operands);
|
||||
|
||||
enum LoadDirectiveResult {
|
||||
LoadDirective_Success = 0,
|
||||
LoadDirective_Error = 1,
|
||||
@@ -623,7 +624,7 @@ gb_internal bool check_cast_internal(CheckerContext *c, Operand *x, Type *type);
|
||||
|
||||
#define MAXIMUM_TYPE_DISTANCE 10
|
||||
|
||||
gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type) {
|
||||
gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type, bool allow_array_programming) {
|
||||
if (c == nullptr) {
|
||||
GB_ASSERT(operand->mode == Addressing_Value);
|
||||
GB_ASSERT(is_type_typed(operand->type));
|
||||
@@ -832,7 +833,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
|
||||
|
||||
if (dst->Union.variants.count == 1) {
|
||||
Type *vt = dst->Union.variants[0];
|
||||
i64 score = check_distance_between_types(c, operand, vt);
|
||||
i64 score = check_distance_between_types(c, operand, vt, allow_array_programming);
|
||||
if (score >= 0) {
|
||||
return score+2;
|
||||
}
|
||||
@@ -840,7 +841,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
|
||||
i64 prev_lowest_score = -1;
|
||||
i64 lowest_score = -1;
|
||||
for (Type *vt : dst->Union.variants) {
|
||||
i64 score = check_distance_between_types(c, operand, vt);
|
||||
i64 score = check_distance_between_types(c, operand, vt, allow_array_programming);
|
||||
if (score >= 0) {
|
||||
if (lowest_score < 0) {
|
||||
lowest_score = score;
|
||||
@@ -863,14 +864,14 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
|
||||
}
|
||||
|
||||
if (is_type_relative_pointer(dst)) {
|
||||
i64 score = check_distance_between_types(c, operand, dst->RelativePointer.pointer_type);
|
||||
i64 score = check_distance_between_types(c, operand, dst->RelativePointer.pointer_type, allow_array_programming);
|
||||
if (score >= 0) {
|
||||
return score+2;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type_relative_multi_pointer(dst)) {
|
||||
i64 score = check_distance_between_types(c, operand, dst->RelativeMultiPointer.pointer_type);
|
||||
i64 score = check_distance_between_types(c, operand, dst->RelativeMultiPointer.pointer_type, allow_array_programming);
|
||||
if (score >= 0) {
|
||||
return score+2;
|
||||
}
|
||||
@@ -896,19 +897,21 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type_array(dst)) {
|
||||
Type *elem = base_array_type(dst);
|
||||
i64 distance = check_distance_between_types(c, operand, elem);
|
||||
if (distance >= 0) {
|
||||
return distance + 6;
|
||||
if (allow_array_programming) {
|
||||
if (is_type_array(dst)) {
|
||||
Type *elem = base_array_type(dst);
|
||||
i64 distance = check_distance_between_types(c, operand, elem, allow_array_programming);
|
||||
if (distance >= 0) {
|
||||
return distance + 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type_simd_vector(dst)) {
|
||||
Type *dst_elem = base_array_type(dst);
|
||||
i64 distance = check_distance_between_types(c, operand, dst_elem);
|
||||
if (distance >= 0) {
|
||||
return distance + 6;
|
||||
if (is_type_simd_vector(dst)) {
|
||||
Type *dst_elem = base_array_type(dst);
|
||||
i64 distance = check_distance_between_types(c, operand, dst_elem, allow_array_programming);
|
||||
if (distance >= 0) {
|
||||
return distance + 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -918,7 +921,7 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
|
||||
}
|
||||
if (dst->Matrix.row_count == dst->Matrix.column_count) {
|
||||
Type *dst_elem = base_array_type(dst);
|
||||
i64 distance = check_distance_between_types(c, operand, dst_elem);
|
||||
i64 distance = check_distance_between_types(c, operand, dst_elem, allow_array_programming);
|
||||
if (distance >= 0) {
|
||||
return distance + 7;
|
||||
}
|
||||
@@ -966,9 +969,9 @@ gb_internal i64 assign_score_function(i64 distance, bool is_variadic=false) {
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false) {
|
||||
gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *operand, Type *type, i64 *score_, bool is_variadic=false, bool allow_array_programming=true) {
|
||||
i64 score = 0;
|
||||
i64 distance = check_distance_between_types(c, operand, type);
|
||||
i64 distance = check_distance_between_types(c, operand, type, allow_array_programming);
|
||||
bool ok = distance >= 0;
|
||||
if (ok) {
|
||||
score = assign_score_function(distance, is_variadic);
|
||||
@@ -978,9 +981,9 @@ gb_internal bool check_is_assignable_to_with_score(CheckerContext *c, Operand *o
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type) {
|
||||
gb_internal bool check_is_assignable_to(CheckerContext *c, Operand *operand, Type *type, bool allow_array_programming=true) {
|
||||
i64 score = 0;
|
||||
return check_is_assignable_to_with_score(c, operand, type, &score);
|
||||
return check_is_assignable_to_with_score(c, operand, type, &score, /*is_variadic*/false, allow_array_programming);
|
||||
}
|
||||
|
||||
gb_internal bool internal_check_is_assignable_to(Type *src, Type *dst) {
|
||||
@@ -1172,6 +1175,16 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
|
||||
type_str, type_extra,
|
||||
LIT(context_name));
|
||||
check_assignment_error_suggestion(c, operand, type);
|
||||
|
||||
if (context_name == "procedure argument") {
|
||||
Type *src = base_type(operand->type);
|
||||
Type *dst = base_type(type);
|
||||
if (is_type_slice(src) && are_types_identical(src->Slice.elem, dst)) {
|
||||
gbString a = expr_to_string(operand->expr);
|
||||
error_line("\tSuggestion: Did you mean to pass the slice into the variadic parameter with ..%s?\n\n", a);
|
||||
gb_string_free(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1597,7 +1610,7 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
|
||||
|
||||
for (CIdentSuggestion const &suggestion : c_ident_suggestions) {
|
||||
if (name == suggestion.name) {
|
||||
error_line("\tSuggestion: Did you mean %s\n", LIT(suggestion.msg));
|
||||
error_line("\tSuggestion: Did you mean %.*s\n", LIT(suggestion.msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1725,7 +1738,7 @@ gb_internal Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *nam
|
||||
if (check_cycle(c, e, true)) {
|
||||
o->type = t_invalid;
|
||||
}
|
||||
if (o->type != nullptr && type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) {
|
||||
if (o->type != nullptr && o->type->kind == Type_Named && o->type->Named.type_name->TypeName.is_type_alias) {
|
||||
o->type = base_type(o->type);
|
||||
}
|
||||
|
||||
@@ -1798,6 +1811,21 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
|
||||
}
|
||||
break;
|
||||
|
||||
case Token_Mul:
|
||||
{
|
||||
ERROR_BLOCK();
|
||||
error(op, "Operator '%.*s' is not a valid unary operator in Odin", LIT(op.string));
|
||||
if (is_type_pointer(o->type)) {
|
||||
str = expr_to_string(o->expr);
|
||||
error_line("\tSuggestion: Did you mean '%s^'?\n", str);
|
||||
o->type = type_deref(o->type);
|
||||
} else if (is_type_multi_pointer(o->type)) {
|
||||
str = expr_to_string(o->expr);
|
||||
error_line("\tSuggestion: The value is a multi-pointer, did you mean '%s[0]'?\n", str);
|
||||
o->type = type_deref(o->type, true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
error(op, "Unknown operator '%.*s'", LIT(op.string));
|
||||
return false;
|
||||
@@ -3142,6 +3170,14 @@ gb_internal bool check_is_castable_to(CheckerContext *c, Operand *operand, Type
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
if (is_type_array(dst)) {
|
||||
Type *elem = base_array_type(dst);
|
||||
if (check_is_castable_to(c, operand, elem)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_type_simd_vector(src) && is_type_simd_vector(dst)) {
|
||||
if (src->SimdVector.count != dst->SimdVector.count) {
|
||||
return false;
|
||||
@@ -4875,10 +4911,18 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
|
||||
Selection sel = {}; // NOTE(bill): Not used if it's an import name
|
||||
|
||||
if (!c->allow_arrow_right_selector_expr && se->token.kind == Token_ArrowRight) {
|
||||
ERROR_BLOCK();
|
||||
error(node, "Illegal use of -> selector shorthand outside of a call");
|
||||
operand->mode = Addressing_Invalid;
|
||||
operand->expr = node;
|
||||
return nullptr;
|
||||
gbString x = expr_to_string(se->expr);
|
||||
gbString y = expr_to_string(se->selector);
|
||||
error_line("\tSuggestion: Did you mean '%s.%s'?\n", x, y);
|
||||
gb_string_free(y);
|
||||
gb_string_free(x);
|
||||
|
||||
// TODO(bill): Should this terminate here or propagate onwards?
|
||||
// operand->mode = Addressing_Invalid;
|
||||
// operand->expr = node;
|
||||
// return nullptr;
|
||||
}
|
||||
|
||||
operand->expr = node;
|
||||
@@ -5787,10 +5831,14 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
|
||||
Operand *variadic_operand = &ordered_operands[pt->variadic_index];
|
||||
|
||||
if (vari_expand) {
|
||||
GB_ASSERT(variadic_operands.count != 0);
|
||||
*variadic_operand = variadic_operands[0];
|
||||
variadic_operand->type = default_type(variadic_operand->type);
|
||||
actually_variadic = true;
|
||||
if (variadic_operands.count == 0) {
|
||||
error(call, "'..' in the wrong position");
|
||||
} else {
|
||||
GB_ASSERT(variadic_operands.count != 0);
|
||||
*variadic_operand = variadic_operands[0];
|
||||
variadic_operand->type = default_type(variadic_operand->type);
|
||||
actually_variadic = true;
|
||||
}
|
||||
} else {
|
||||
AstFile *f = call->file();
|
||||
|
||||
@@ -5854,31 +5902,28 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
|
||||
}
|
||||
|
||||
auto eval_param_and_score = [](CheckerContext *c, Operand *o, Type *param_type, CallArgumentError &err, bool param_is_variadic, Entity *e, bool show_error) -> i64 {
|
||||
bool allow_array_programming = !(e && (e->flags & EntityFlag_NoBroadcast));
|
||||
i64 s = 0;
|
||||
if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic)) {
|
||||
if (!check_is_assignable_to_with_score(c, o, param_type, &s, param_is_variadic, allow_array_programming)) {
|
||||
bool ok = false;
|
||||
if (e && e->flags & EntityFlag_AnyInt) {
|
||||
if (e && (e->flags & EntityFlag_AnyInt)) {
|
||||
if (is_type_integer(param_type)) {
|
||||
ok = check_is_castable_to(c, o, param_type);
|
||||
}
|
||||
}
|
||||
if (!allow_array_programming && check_is_assignable_to_with_score(c, o, param_type, nullptr, param_is_variadic, !allow_array_programming)) {
|
||||
if (show_error) {
|
||||
error(o->expr, "'#no_broadcast' disallows automatic broadcasting a value across all elements of an array-like type in a procedure argument");
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
s = assign_score_function(MAXIMUM_TYPE_DISTANCE);
|
||||
} else {
|
||||
if (show_error) {
|
||||
check_assignment(c, o, param_type, str_lit("procedure argument"));
|
||||
|
||||
Type *src = base_type(o->type);
|
||||
Type *dst = base_type(param_type);
|
||||
if (is_type_slice(src) && are_types_identical(src->Slice.elem, dst)) {
|
||||
gbString a = expr_to_string(o->expr);
|
||||
error_line("\tSuggestion: Did you mean to pass the slice into the variadic parameter with ..%s?\n\n", a);
|
||||
gb_string_free(a);
|
||||
}
|
||||
}
|
||||
err = CallArgumentError_WrongTypes;
|
||||
}
|
||||
|
||||
} else if (show_error) {
|
||||
check_assignment(c, o, param_type, str_lit("procedure argument"));
|
||||
}
|
||||
@@ -5963,12 +6008,13 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
|
||||
if (param_is_variadic) {
|
||||
continue;
|
||||
}
|
||||
score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error);
|
||||
score += eval_param_and_score(c, o, e->type, err, false, e, show_error);
|
||||
}
|
||||
}
|
||||
|
||||
if (variadic) {
|
||||
Type *slice = pt->params->Tuple.variables[pt->variadic_index]->type;
|
||||
Entity *var_entity = pt->params->Tuple.variables[pt->variadic_index];
|
||||
Type *slice = var_entity->type;
|
||||
GB_ASSERT(is_type_slice(slice));
|
||||
Type *elem = base_type(slice)->Slice.elem;
|
||||
Type *t = elem;
|
||||
@@ -5994,7 +6040,7 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
|
||||
return CallArgumentError_MultipleVariadicExpand;
|
||||
}
|
||||
}
|
||||
score += eval_param_and_score(c, o, t, err, true, nullptr, show_error);
|
||||
score += eval_param_and_score(c, o, t, err, true, var_entity, show_error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6122,7 +6168,10 @@ gb_internal bool evaluate_where_clauses(CheckerContext *ctx, Ast *call_expr, Sco
|
||||
}
|
||||
}
|
||||
|
||||
if (call_expr) error(call_expr, "at caller location");
|
||||
if (call_expr) {
|
||||
TokenPos pos = ast_token(call_expr).pos;
|
||||
error_line("%s at caller location\n", token_pos_to_string(pos));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -7123,8 +7172,12 @@ gb_internal CallArgumentError check_polymorphic_record_type(CheckerContext *c, O
|
||||
}
|
||||
|
||||
{
|
||||
bool failure = false;
|
||||
Entity *found_entity = find_polymorphic_record_entity(c, original_type, param_count, ordered_operands, &failure);
|
||||
GenTypesData *found_gen_types = ensure_polymorphic_record_entity_has_gen_types(c, original_type);
|
||||
|
||||
mutex_lock(&found_gen_types->mutex);
|
||||
defer (mutex_unlock(&found_gen_types->mutex));
|
||||
Entity *found_entity = find_polymorphic_record_entity(found_gen_types, param_count, ordered_operands);
|
||||
|
||||
if (found_entity) {
|
||||
operand->mode = Addressing_Type;
|
||||
operand->type = found_entity->type;
|
||||
@@ -8417,6 +8470,7 @@ gb_internal ExprKind check_or_return_expr(CheckerContext *c, Operand *o, Ast *no
|
||||
// NOTE(bill): allow implicit conversion between boolean types
|
||||
// within 'or_return' to improve the experience using third-party code
|
||||
} else if (!check_is_assignable_to(c, &rhs, end_type)) {
|
||||
ERROR_BLOCK();
|
||||
// TODO(bill): better error message
|
||||
gbString a = type_to_string(right_type);
|
||||
gbString b = type_to_string(end_type);
|
||||
@@ -8824,6 +8878,7 @@ gb_internal ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *
|
||||
break;
|
||||
}
|
||||
|
||||
wait_signal_until_available(&t->Struct.fields_wait_signal);
|
||||
isize field_count = t->Struct.fields.count;
|
||||
isize min_field_count = t->Struct.fields.count;
|
||||
for (isize i = min_field_count-1; i >= 0; i--) {
|
||||
@@ -9988,6 +10043,7 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node,
|
||||
bool ok = check_index_value(c, t, false, ie->index, max_count, &index, index_type_hint);
|
||||
if (is_const) {
|
||||
if (index < 0) {
|
||||
ERROR_BLOCK();
|
||||
gbString str = expr_to_string(o->expr);
|
||||
error(o->expr, "Cannot index a constant '%s'", str);
|
||||
if (!build_context.terse_errors) {
|
||||
@@ -10004,6 +10060,7 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node,
|
||||
bool finish = false;
|
||||
o->value = get_constant_field_single(c, value, cast(i32)index, &success, &finish);
|
||||
if (!success) {
|
||||
ERROR_BLOCK();
|
||||
gbString str = expr_to_string(o->expr);
|
||||
error(o->expr, "Cannot index a constant '%s' with index %lld", str, cast(long long)index);
|
||||
if (!build_context.terse_errors) {
|
||||
@@ -10194,6 +10251,7 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
|
||||
}
|
||||
}
|
||||
if (!all_constant) {
|
||||
ERROR_BLOCK();
|
||||
gbString str = expr_to_string(o->expr);
|
||||
error(o->expr, "Cannot slice '%s' with non-constant indices", str);
|
||||
if (!build_context.terse_errors) {
|
||||
@@ -11148,6 +11206,9 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
|
||||
if (f->flags&FieldFlag_any_int) {
|
||||
str = gb_string_appendc(str, "#any_int ");
|
||||
}
|
||||
if (f->flags&FieldFlag_no_broadcast) {
|
||||
str = gb_string_appendc(str, "#no_broadcast ");
|
||||
}
|
||||
if (f->flags&FieldFlag_const) {
|
||||
str = gb_string_appendc(str, "#const ");
|
||||
}
|
||||
@@ -11214,6 +11275,18 @@ gb_internal gbString write_expr_to_string(gbString str, Ast *node, bool shorthan
|
||||
if (field->flags&FieldFlag_c_vararg) {
|
||||
str = gb_string_appendc(str, "#c_vararg ");
|
||||
}
|
||||
if (field->flags&FieldFlag_any_int) {
|
||||
str = gb_string_appendc(str, "#any_int ");
|
||||
}
|
||||
if (field->flags&FieldFlag_no_broadcast) {
|
||||
str = gb_string_appendc(str, "#no_broadcast ");
|
||||
}
|
||||
if (field->flags&FieldFlag_const) {
|
||||
str = gb_string_appendc(str, "#const ");
|
||||
}
|
||||
if (field->flags&FieldFlag_subtype) {
|
||||
str = gb_string_appendc(str, "#subtype ");
|
||||
}
|
||||
|
||||
str = write_expr_to_string(str, field->type, shorthand);
|
||||
}
|
||||
|
||||
+58
-55
@@ -883,6 +883,7 @@ gb_internal void check_inline_range_stmt(CheckerContext *ctx, Ast *node, u32 mod
|
||||
}
|
||||
|
||||
if (ctx->inline_for_depth >= MAX_INLINE_FOR_DEPTH && prev_inline_for_depth < MAX_INLINE_FOR_DEPTH) {
|
||||
ERROR_BLOCK();
|
||||
if (prev_inline_for_depth > 0) {
|
||||
error(node, "Nested '#unroll for' loop cannot be inlined as it exceeds the maximum '#unroll for' depth (%lld levels >= %lld maximum levels)", v, MAX_INLINE_FOR_DEPTH);
|
||||
} else {
|
||||
@@ -1365,6 +1366,8 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
|
||||
}
|
||||
|
||||
if (unhandled.count > 0) {
|
||||
ERROR_BLOCK();
|
||||
|
||||
if (unhandled.count == 1) {
|
||||
gbString s = type_to_string(unhandled[0]);
|
||||
error_no_newline(node, "Unhandled switch case: %s", s);
|
||||
@@ -1463,25 +1466,6 @@ gb_internal bool check_stmt_internal_builtin_proc_id(Ast *expr, BuiltinProcId *i
|
||||
return id != BuiltinProc_Invalid;
|
||||
}
|
||||
|
||||
gb_internal bool check_expr_is_stack_variable(Ast *expr) {
|
||||
/*
|
||||
expr = unparen_expr(expr);
|
||||
Entity *e = entity_of_node(expr);
|
||||
if (e && e->kind == Entity_Variable) {
|
||||
if (e->flags & (EntityFlag_Static|EntityFlag_Using|EntityFlag_ImplicitReference|EntityFlag_ForValue)) {
|
||||
// okay
|
||||
} else if (e->Variable.thread_local_model.len != 0) {
|
||||
// okay
|
||||
} else if (e->scope) {
|
||||
if ((e->scope->flags & (ScopeFlag_Global|ScopeFlag_File|ScopeFlag_Type)) == 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
|
||||
ast_node(rs, RangeStmt, node);
|
||||
|
||||
@@ -1544,10 +1528,13 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
|
||||
}
|
||||
} else if (operand.mode != Addressing_Invalid) {
|
||||
if (operand.mode == Addressing_OptionalOk || operand.mode == Addressing_OptionalOkPtr) {
|
||||
Type *end_type = nullptr;
|
||||
check_promote_optional_ok(ctx, &operand, nullptr, &end_type, false);
|
||||
if (is_type_boolean(end_type)) {
|
||||
check_promote_optional_ok(ctx, &operand, nullptr, &end_type, true);
|
||||
Ast *expr = unparen_expr(operand.expr);
|
||||
if (expr->kind != Ast_TypeAssertion) { // Only for procedure calls
|
||||
Type *end_type = nullptr;
|
||||
check_promote_optional_ok(ctx, &operand, nullptr, &end_type, false);
|
||||
if (is_type_boolean(end_type)) {
|
||||
check_promote_optional_ok(ctx, &operand, nullptr, &end_type, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
bool is_ptr = is_type_pointer(operand.type);
|
||||
@@ -1606,6 +1593,7 @@ gb_internal void check_range_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags)
|
||||
{
|
||||
isize count = t->Tuple.variables.count;
|
||||
if (count < 1 || count > 3) {
|
||||
ERROR_BLOCK();
|
||||
check_not_tuple(ctx, &operand);
|
||||
error_line("\tMultiple return valued parameters in a range statement are limited to a maximum of 2 usable values with a trailing boolean for the conditional\n");
|
||||
break;
|
||||
@@ -2099,6 +2087,9 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ERROR_BLOCK();
|
||||
|
||||
gbString expr_str = expr_to_string(operand.expr);
|
||||
error(node, "Expression is not used: '%s'", expr_str);
|
||||
gb_string_free(expr_str);
|
||||
@@ -2292,29 +2283,6 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
|
||||
if (is_type_untyped(o->type)) {
|
||||
update_untyped_expr_type(ctx, o->expr, e->type, true);
|
||||
}
|
||||
|
||||
|
||||
// NOTE(bill): This is very basic escape analysis
|
||||
// This needs to be improved tremendously, and a lot of it done during the
|
||||
// middle-end (or LLVM side) to improve checks and error messages
|
||||
Ast *expr = unparen_expr(o->expr);
|
||||
if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) {
|
||||
Ast *x = unparen_expr(expr->UnaryExpr.expr);
|
||||
if (x->kind == Ast_CompoundLit) {
|
||||
error(expr, "Cannot return the address to a stack value from a procedure");
|
||||
} else if (x->kind == Ast_IndexExpr) {
|
||||
Ast *array = x->IndexExpr.expr;
|
||||
if (is_type_array_like(type_of_expr(array)) && check_expr_is_stack_variable(array)) {
|
||||
gbString t = type_to_string(type_of_expr(array));
|
||||
error(expr, "Cannot return the address to an element of stack variable from a procedure, of type %s", t);
|
||||
gb_string_free(t);
|
||||
}
|
||||
} else {
|
||||
if (check_expr_is_stack_variable(x)) {
|
||||
error(expr, "Cannot return the address to a stack variable from a procedure");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2322,16 +2290,51 @@ gb_internal void check_return_stmt(CheckerContext *ctx, Ast *node) {
|
||||
if (o.expr == nullptr) {
|
||||
continue;
|
||||
}
|
||||
if (o.expr->kind != Ast_CompoundLit || !is_type_slice(o.type)) {
|
||||
continue;
|
||||
Ast *expr = unparen_expr(o.expr);
|
||||
|
||||
auto unsafe_return_error = [](Operand const &o, char const *msg, Type *extra_type=nullptr) {
|
||||
gbString s = expr_to_string(o.expr);
|
||||
if (extra_type) {
|
||||
gbString t = type_to_string(extra_type);
|
||||
error(o.expr, "It is unsafe to return %s ('%s') of type ('%s') from a procedure, as it uses the current stack frame's memory", msg, s, t);
|
||||
gb_string_free(t);
|
||||
} else {
|
||||
error(o.expr, "It is unsafe to return %s ('%s') from a procedure, as it uses the current stack frame's memory", msg, s);
|
||||
}
|
||||
gb_string_free(s);
|
||||
};
|
||||
|
||||
|
||||
// NOTE(bill): This is very basic escape analysis
|
||||
// This needs to be improved tremendously, and a lot of it done during the
|
||||
// middle-end (or LLVM side) to improve checks and error messages
|
||||
if (expr->kind == Ast_CompoundLit && is_type_slice(o.type)) {
|
||||
ast_node(cl, CompoundLit, expr);
|
||||
if (cl->elems.count == 0) {
|
||||
continue;
|
||||
}
|
||||
unsafe_return_error(o, "a compound literal of a slice");
|
||||
} else if (expr->kind == Ast_UnaryExpr && expr->UnaryExpr.op.kind == Token_And) {
|
||||
Ast *x = unparen_expr(expr->UnaryExpr.expr);
|
||||
Entity *e = entity_of_node(x);
|
||||
if (is_entity_local_variable(e)) {
|
||||
unsafe_return_error(o, "the address of a local variable");
|
||||
} else if(x->kind == Ast_CompoundLit) {
|
||||
unsafe_return_error(o, "the address of a compound literal");
|
||||
} else if (x->kind == Ast_IndexExpr) {
|
||||
Entity *f = entity_of_node(x->IndexExpr.expr);
|
||||
if (is_type_array_like(f->type) || is_type_matrix(f->type)) {
|
||||
if (is_entity_local_variable(f)) {
|
||||
unsafe_return_error(o, "the address of an indexed variable", f->type);
|
||||
}
|
||||
}
|
||||
} else if (x->kind == Ast_MatrixIndexExpr) {
|
||||
Entity *f = entity_of_node(x->MatrixIndexExpr.expr);
|
||||
if (is_type_matrix(f->type) && is_entity_local_variable(f)) {
|
||||
unsafe_return_error(o, "the address of an indexed variable", f->type);
|
||||
}
|
||||
}
|
||||
}
|
||||
ast_node(cl, CompoundLit, o.expr);
|
||||
if (cl->elems.count == 0) {
|
||||
continue;
|
||||
}
|
||||
gbString s = type_to_string(o.type);
|
||||
error(o.expr, "It is unsafe to return a compound literal of a slice ('%s') with elements from a procedure, as the contents of the slice uses the current stack frame's memory", s);
|
||||
gb_string_free(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+134
-95
@@ -260,84 +260,21 @@ gb_internal bool check_custom_align(CheckerContext *ctx, Ast *node, i64 *align_,
|
||||
}
|
||||
|
||||
|
||||
gb_internal Entity *find_polymorphic_record_entity(CheckerContext *ctx, Type *original_type, isize param_count, Array<Operand> const &ordered_operands, bool *failure) {
|
||||
rw_mutex_shared_lock(&ctx->info->gen_types_mutex); // @@global
|
||||
gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(CheckerContext *ctx, Type *original_type) {
|
||||
mutex_lock(&ctx->info->gen_types_mutex); // @@global
|
||||
|
||||
auto *found_gen_types = map_get(&ctx->info->gen_types, original_type);
|
||||
if (found_gen_types == nullptr) {
|
||||
rw_mutex_shared_unlock(&ctx->info->gen_types_mutex); // @@global
|
||||
return nullptr;
|
||||
GenTypesData *found_gen_types = nullptr;
|
||||
auto *found_gen_types_ptr = map_get(&ctx->info->gen_types, original_type);
|
||||
if (found_gen_types_ptr == nullptr) {
|
||||
GenTypesData *gen_types = gb_alloc_item(permanent_allocator(), GenTypesData);
|
||||
gen_types->types = array_make<Entity *>(heap_allocator());
|
||||
map_set(&ctx->info->gen_types, original_type, gen_types);
|
||||
found_gen_types_ptr = map_get(&ctx->info->gen_types, original_type);
|
||||
}
|
||||
|
||||
rw_mutex_shared_lock(&found_gen_types->mutex); // @@local
|
||||
defer (rw_mutex_shared_unlock(&found_gen_types->mutex)); // @@local
|
||||
|
||||
rw_mutex_shared_unlock(&ctx->info->gen_types_mutex); // @@global
|
||||
|
||||
for (Entity *e : found_gen_types->types) {
|
||||
Type *t = base_type(e->type);
|
||||
TypeTuple *tuple = nullptr;
|
||||
switch (t->kind) {
|
||||
case Type_Struct:
|
||||
if (t->Struct.polymorphic_params) {
|
||||
tuple = &t->Struct.polymorphic_params->Tuple;
|
||||
}
|
||||
break;
|
||||
case Type_Union:
|
||||
if (t->Union.polymorphic_params) {
|
||||
tuple = &t->Union.polymorphic_params->Tuple;
|
||||
}
|
||||
break;
|
||||
}
|
||||
GB_ASSERT_MSG(tuple != nullptr, "%s :: %s", type_to_string(e->type), type_to_string(t));
|
||||
GB_ASSERT(param_count == tuple->variables.count);
|
||||
|
||||
bool skip = false;
|
||||
|
||||
for (isize j = 0; j < param_count; j++) {
|
||||
Entity *p = tuple->variables[j];
|
||||
Operand o = {};
|
||||
if (j < ordered_operands.count) {
|
||||
o = ordered_operands[j];
|
||||
}
|
||||
if (o.expr == nullptr) {
|
||||
continue;
|
||||
}
|
||||
Entity *oe = entity_of_node(o.expr);
|
||||
if (p == oe) {
|
||||
// NOTE(bill): This is the same type, make sure that it will be be same thing and use that
|
||||
// Saves on a lot of checking too below
|
||||
continue;
|
||||
}
|
||||
|
||||
if (p->kind == Entity_TypeName) {
|
||||
if (is_type_polymorphic(o.type)) {
|
||||
// NOTE(bill): Do not add polymorphic version to the gen_types
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
if (!are_types_identical(o.type, p->type)) {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
} else if (p->kind == Entity_Constant) {
|
||||
if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
if (!are_types_identical(o.type, p->type)) {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
GB_PANIC("Unknown entity kind");
|
||||
}
|
||||
}
|
||||
if (!skip) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
found_gen_types = *found_gen_types_ptr;
|
||||
GB_ASSERT(found_gen_types != nullptr);
|
||||
mutex_unlock(&ctx->info->gen_types_mutex); // @@global
|
||||
return found_gen_types;
|
||||
}
|
||||
|
||||
|
||||
@@ -367,19 +304,16 @@ gb_internal void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, T
|
||||
// TODO(bill): Is this even correct? Or should the metadata be copied?
|
||||
e->TypeName.objc_metadata = original_type->Named.type_name->TypeName.objc_metadata;
|
||||
|
||||
rw_mutex_lock(&ctx->info->gen_types_mutex);
|
||||
auto *found_gen_types = map_get(&ctx->info->gen_types, original_type);
|
||||
if (found_gen_types) {
|
||||
rw_mutex_lock(&found_gen_types->mutex);
|
||||
array_add(&found_gen_types->types, e);
|
||||
rw_mutex_unlock(&found_gen_types->mutex);
|
||||
} else {
|
||||
GenTypesData gen_types = {};
|
||||
gen_types.types = array_make<Entity *>(heap_allocator());
|
||||
array_add(&gen_types.types, e);
|
||||
map_set(&ctx->info->gen_types, original_type, gen_types);
|
||||
auto *found_gen_types = ensure_polymorphic_record_entity_has_gen_types(ctx, original_type);
|
||||
mutex_lock(&found_gen_types->mutex);
|
||||
defer (mutex_unlock(&found_gen_types->mutex));
|
||||
|
||||
for (Entity *prev : found_gen_types->types) {
|
||||
if (prev == e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
rw_mutex_unlock(&ctx->info->gen_types_mutex);
|
||||
array_add(&found_gen_types->types, e);
|
||||
}
|
||||
|
||||
|
||||
@@ -612,6 +546,73 @@ gb_internal bool check_record_poly_operand_specialization(CheckerContext *ctx, T
|
||||
return true;
|
||||
}
|
||||
|
||||
gb_internal Entity *find_polymorphic_record_entity(GenTypesData *found_gen_types, isize param_count, Array<Operand> const &ordered_operands) {
|
||||
for (Entity *e : found_gen_types->types) {
|
||||
Type *t = base_type(e->type);
|
||||
TypeTuple *tuple = nullptr;
|
||||
switch (t->kind) {
|
||||
case Type_Struct:
|
||||
if (t->Struct.polymorphic_params) {
|
||||
tuple = &t->Struct.polymorphic_params->Tuple;
|
||||
}
|
||||
break;
|
||||
case Type_Union:
|
||||
if (t->Union.polymorphic_params) {
|
||||
tuple = &t->Union.polymorphic_params->Tuple;
|
||||
}
|
||||
break;
|
||||
}
|
||||
GB_ASSERT_MSG(tuple != nullptr, "%s :: %s", type_to_string(e->type), type_to_string(t));
|
||||
GB_ASSERT(param_count == tuple->variables.count);
|
||||
|
||||
bool skip = false;
|
||||
|
||||
for (isize j = 0; j < param_count; j++) {
|
||||
Entity *p = tuple->variables[j];
|
||||
Operand o = {};
|
||||
if (j < ordered_operands.count) {
|
||||
o = ordered_operands[j];
|
||||
}
|
||||
if (o.expr == nullptr) {
|
||||
continue;
|
||||
}
|
||||
Entity *oe = entity_of_node(o.expr);
|
||||
if (p == oe) {
|
||||
// NOTE(bill): This is the same type, make sure that it will be be same thing and use that
|
||||
// Saves on a lot of checking too below
|
||||
continue;
|
||||
}
|
||||
|
||||
if (p->kind == Entity_TypeName) {
|
||||
if (is_type_polymorphic(o.type)) {
|
||||
// NOTE(bill): Do not add polymorphic version to the gen_types
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
if (!are_types_identical(o.type, p->type)) {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
} else if (p->kind == Entity_Constant) {
|
||||
if (!compare_exact_values(Token_CmpEq, o.value, p->Constant.value)) {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
if (!are_types_identical(o.type, p->type)) {
|
||||
skip = true;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
GB_PANIC("Unknown entity kind");
|
||||
}
|
||||
}
|
||||
if (!skip) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
|
||||
gb_internal void check_struct_type(CheckerContext *ctx, Type *struct_type, Ast *node, Array<Operand> *poly_operands, Type *named_type, Type *original_type_for_poly) {
|
||||
GB_ASSERT(is_type_struct(struct_type));
|
||||
@@ -1869,6 +1870,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
error(name, "'#any_int' can only be applied to variable fields");
|
||||
p->flags &= ~FieldFlag_any_int;
|
||||
}
|
||||
if (p->flags&FieldFlag_no_broadcast) {
|
||||
error(name, "'#no_broadcast' can only be applied to variable fields");
|
||||
p->flags &= ~FieldFlag_no_broadcast;
|
||||
}
|
||||
if (p->flags&FieldFlag_by_ptr) {
|
||||
error(name, "'#by_ptr' can only be applied to variable fields");
|
||||
p->flags &= ~FieldFlag_by_ptr;
|
||||
@@ -1926,7 +1931,13 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
}
|
||||
}
|
||||
}
|
||||
if (type != t_invalid && !check_is_assignable_to(ctx, &op, type)) {
|
||||
|
||||
bool allow_array_programming = true;
|
||||
if (p->flags&FieldFlag_no_broadcast) {
|
||||
allow_array_programming = false;
|
||||
}
|
||||
|
||||
if (type != t_invalid && !check_is_assignable_to(ctx, &op, type, allow_array_programming)) {
|
||||
bool ok = true;
|
||||
if (p->flags&FieldFlag_any_int) {
|
||||
if ((!is_type_integer(op.type) && !is_type_enum(op.type)) || (!is_type_integer(type) && !is_type_enum(type))) {
|
||||
@@ -2002,6 +2013,10 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
if (p->flags&FieldFlag_no_alias) {
|
||||
param->flags |= EntityFlag_NoAlias;
|
||||
}
|
||||
if (p->flags&FieldFlag_no_broadcast) {
|
||||
param->flags |= EntityFlag_NoBroadcast;
|
||||
}
|
||||
|
||||
if (p->flags&FieldFlag_any_int) {
|
||||
if (!is_type_integer(param->type) && !is_type_enum(param->type)) {
|
||||
gbString str = type_to_string(param->type);
|
||||
@@ -2382,6 +2397,8 @@ gb_internal i64 check_array_count(CheckerContext *ctx, Operand *o, Ast *e) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ERROR_BLOCK();
|
||||
|
||||
gbString s = expr_to_string(o->expr);
|
||||
error(e, "Array count must be a constant integer, got %s", s);
|
||||
gb_string_free(s);
|
||||
@@ -2474,6 +2491,7 @@ gb_internal Type *get_map_cell_type(Type *type) {
|
||||
s->Struct.fields[0] = alloc_entity_field(scope, make_token_ident("v"), alloc_type_array(type, len), false, 0, EntityState_Resolved);
|
||||
s->Struct.fields[1] = alloc_entity_field(scope, make_token_ident("_"), alloc_type_array(t_u8, padding), false, 1, EntityState_Resolved);
|
||||
s->Struct.scope = scope;
|
||||
wait_signal_set(&s->Struct.fields_wait_signal);
|
||||
gb_unused(type_size_of(s));
|
||||
|
||||
return s;
|
||||
@@ -2504,6 +2522,7 @@ gb_internal void init_map_internal_types(Type *type) {
|
||||
metadata_type->Struct.fields[4] = alloc_entity_field(metadata_scope, make_token_ident("value_cell"), value_cell, false, 4, EntityState_Resolved);
|
||||
metadata_type->Struct.scope = metadata_scope;
|
||||
metadata_type->Struct.node = nullptr;
|
||||
wait_signal_set(&metadata_type->Struct.fields_wait_signal);
|
||||
|
||||
gb_unused(type_size_of(metadata_type));
|
||||
|
||||
@@ -2521,6 +2540,7 @@ gb_internal void init_map_internal_types(Type *type) {
|
||||
debug_type->Struct.fields[3] = alloc_entity_field(scope, make_token_ident("__metadata"), metadata_type, false, 3, EntityState_Resolved);
|
||||
debug_type->Struct.scope = scope;
|
||||
debug_type->Struct.node = nullptr;
|
||||
wait_signal_set(&debug_type->Struct.fields_wait_signal);
|
||||
|
||||
gb_unused(type_size_of(debug_type));
|
||||
|
||||
@@ -2816,6 +2836,7 @@ gb_internal Type *make_soa_struct_internal(CheckerContext *ctx, Ast *array_typ_e
|
||||
add_entity(ctx, scope, nullptr, base_type_entity);
|
||||
|
||||
add_type_info_type(ctx, soa_struct);
|
||||
wait_signal_set(&soa_struct->Struct.fields_wait_signal);
|
||||
|
||||
return soa_struct;
|
||||
}
|
||||
@@ -2864,6 +2885,8 @@ gb_internal void check_array_type_internal(CheckerContext *ctx, Ast *e, Type **t
|
||||
}
|
||||
|
||||
if (!is_sparse && t->EnumeratedArray.count > bt->Enum.fields.count) {
|
||||
ERROR_BLOCK();
|
||||
|
||||
error(e, "Non-contiguous enumeration used as an index in an enumerated array");
|
||||
long long ea_count = cast(long long)t->EnumeratedArray.count;
|
||||
long long enum_count = cast(long long)bt->Enum.fields.count;
|
||||
@@ -3094,20 +3117,22 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
|
||||
|
||||
Type *elem = t_invalid;
|
||||
Operand o = {};
|
||||
|
||||
check_expr_or_type(&c, &o, pt->type);
|
||||
if (o.mode != Addressing_Invalid && o.mode != Addressing_Type) {
|
||||
// NOTE(bill): call check_type_expr again to get a consistent error message
|
||||
ERROR_BLOCK();
|
||||
elem = check_type_expr(&c, pt->type, nullptr);
|
||||
if (o.mode == Addressing_Variable) {
|
||||
gbString s = expr_to_string(pt->type);
|
||||
error_line("\tSuggestion: ^ is used for pointer types, did you mean '&%s'?\n", s);
|
||||
error(e, "^ is used for pointer types, did you mean '&%s'?", s);
|
||||
gb_string_free(s);
|
||||
} else {
|
||||
// NOTE(bill): call check_type_expr again to get a consistent error message
|
||||
elem = check_type_expr(&c, pt->type, nullptr);
|
||||
}
|
||||
} else {
|
||||
elem = o.type;
|
||||
}
|
||||
|
||||
|
||||
if (pt->tag != nullptr) {
|
||||
GB_ASSERT(pt->tag->kind == Ast_BasicDirective);
|
||||
String name = pt->tag->BasicDirective.name.string;
|
||||
@@ -3355,7 +3380,9 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type)
|
||||
type = t_invalid;
|
||||
|
||||
|
||||
// NOTE(bill): Check for common mistakes from C programmers e.g. T[] and T[N]
|
||||
// NOTE(bill): Check for common mistakes from C programmers
|
||||
// e.g. T[] and T[N]
|
||||
// e.g. *T
|
||||
Ast *node = unparen_expr(e);
|
||||
if (node && node->kind == Ast_IndexExpr) {
|
||||
gbString index_str = nullptr;
|
||||
@@ -3367,7 +3394,7 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type)
|
||||
gbString type_str = expr_to_string(node->IndexExpr.expr);
|
||||
defer (gb_string_free(type_str));
|
||||
|
||||
error_line("\tSuggestion: Did you mean '[%s]%s'?", index_str ? index_str : "", type_str);
|
||||
error_line("\tSuggestion: Did you mean '[%s]%s'?\n", index_str ? index_str : "", type_str);
|
||||
end_error_block();
|
||||
|
||||
// NOTE(bill): Minimize error propagation of bad array syntax by treating this like a type
|
||||
@@ -3375,6 +3402,18 @@ gb_internal Type *check_type_expr(CheckerContext *ctx, Ast *e, Type *named_type)
|
||||
Ast *pseudo_array_expr = ast_array_type(e->file(), ast_token(node->IndexExpr.expr), node->IndexExpr.index, node->IndexExpr.expr);
|
||||
check_array_type_internal(ctx, pseudo_array_expr, &type, nullptr);
|
||||
}
|
||||
} else if (node && node->kind == Ast_UnaryExpr && node->UnaryExpr.op.kind == Token_Mul) {
|
||||
gbString type_str = expr_to_string(node->UnaryExpr.expr);
|
||||
defer (gb_string_free(type_str));
|
||||
|
||||
error_line("\tSuggestion: Did you mean '^%s'?\n", type_str);
|
||||
end_error_block();
|
||||
|
||||
// NOTE(bill): Minimize error propagation of bad array syntax by treating this like a type
|
||||
if (node->UnaryExpr.expr != nullptr) {
|
||||
Ast *pseudo_pointer_expr = ast_pointer_type(e->file(), ast_token(node->UnaryExpr.expr), node->UnaryExpr.expr);
|
||||
return check_type_expr(ctx, pseudo_pointer_expr, named_type);
|
||||
}
|
||||
} else {
|
||||
end_error_block();
|
||||
}
|
||||
|
||||
+37
-3
@@ -1115,7 +1115,16 @@ gb_internal void init_universal(void) {
|
||||
|
||||
add_global_constant("ODIN_COMPILE_TIMESTAMP", t_untyped_integer, exact_value_i64(odin_compile_timestamp()));
|
||||
|
||||
add_global_bool_constant("__ODIN_LLVM_F16_SUPPORTED", lb_use_new_pass_system() && !is_arch_wasm());
|
||||
{
|
||||
bool f16_supported = lb_use_new_pass_system();
|
||||
if (is_arch_wasm()) {
|
||||
f16_supported = false;
|
||||
} else if (build_context.metrics.os == TargetOs_darwin && build_context.metrics.arch == TargetArch_amd64) {
|
||||
// NOTE(laytan): See #3222 for my ramblings on this.
|
||||
f16_supported = false;
|
||||
}
|
||||
add_global_bool_constant("__ODIN_LLVM_F16_SUPPORTED", f16_supported);
|
||||
}
|
||||
|
||||
{
|
||||
GlobalEnumValue values[3] = {
|
||||
@@ -3180,6 +3189,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
|
||||
linkage == "link_once") {
|
||||
ac->linkage = linkage;
|
||||
} else {
|
||||
ERROR_BLOCK();
|
||||
error(elem, "Invalid linkage '%.*s'. Valid kinds:", LIT(linkage));
|
||||
error_line("\tinternal\n");
|
||||
error_line("\tstrong\n");
|
||||
@@ -3428,6 +3438,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
|
||||
} else if (mode == "speed") {
|
||||
ac->optimization_mode = ProcedureOptimizationMode_Speed;
|
||||
} else {
|
||||
ERROR_BLOCK();
|
||||
error(elem, "Invalid optimization_mode for '%.*s'. Valid modes:", LIT(name));
|
||||
error_line("\tnone\n");
|
||||
error_line("\tminimal\n");
|
||||
@@ -3558,6 +3569,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
|
||||
model == "localexec") {
|
||||
ac->thread_local_model = model;
|
||||
} else {
|
||||
ERROR_BLOCK();
|
||||
error(elem, "Invalid thread local model '%.*s'. Valid models:", LIT(model));
|
||||
error_line("\tdefault\n");
|
||||
error_line("\tlocaldynamic\n");
|
||||
@@ -3608,6 +3620,7 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) {
|
||||
linkage == "link_once") {
|
||||
ac->linkage = linkage;
|
||||
} else {
|
||||
ERROR_BLOCK();
|
||||
error(elem, "Invalid linkage '%.*s'. Valid kinds:", LIT(linkage));
|
||||
error_line("\tinternal\n");
|
||||
error_line("\tstrong\n");
|
||||
@@ -3662,6 +3675,15 @@ gb_internal DECL_ATTRIBUTE_PROC(const_decl_attribute) {
|
||||
} else if (name == "private") {
|
||||
// NOTE(bill): Handled elsewhere `check_collect_value_decl`
|
||||
return true;
|
||||
} else if (name == "static" ||
|
||||
name == "thread_local" ||
|
||||
name == "require" ||
|
||||
name == "linkage" ||
|
||||
name == "link_name" ||
|
||||
name == "link_prefix" ||
|
||||
false) {
|
||||
error(elem, "@(%.*s) is not supported for compile time constant value declarations", LIT(name));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -3762,6 +3784,7 @@ gb_internal void check_decl_attributes(CheckerContext *c, Array<Ast *> const &at
|
||||
|
||||
if (!proc(c, elem, name, value, ac)) {
|
||||
if (!build_context.ignore_unknown_attributes) {
|
||||
ERROR_BLOCK();
|
||||
error(elem, "Unknown attribute element name '%.*s'", LIT(name));
|
||||
error_line("\tDid you forget to use build flag '-ignore-unknown-attributes'?\n");
|
||||
}
|
||||
@@ -3831,6 +3854,8 @@ gb_internal bool check_arity_match(CheckerContext *c, AstValueDecl *vd, bool is_
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
} else if (is_global) {
|
||||
ERROR_BLOCK();
|
||||
|
||||
Ast *n = vd->values[rhs-1];
|
||||
error(n, "Expected %td expressions on the right hand side, got %td", lhs, rhs);
|
||||
error_line("Note: Global declarations do not allow for multi-valued expressions");
|
||||
@@ -4050,6 +4075,7 @@ gb_internal void check_collect_value_decl(CheckerContext *c, Ast *decl) {
|
||||
Entity *e = alloc_entity_variable(c->scope, name->Ident.token, nullptr);
|
||||
e->identifier = name;
|
||||
e->file = c->file;
|
||||
e->Variable.is_global = true;
|
||||
|
||||
if (entity_visibility_kind != EntityVisiblity_Public) {
|
||||
e->flags |= EntityFlag_NotExported;
|
||||
@@ -5448,7 +5474,10 @@ gb_internal void check_procedure_later_from_entity(Checker *c, Entity *e, char c
|
||||
return;
|
||||
}
|
||||
Type *type = base_type(e->type);
|
||||
GB_ASSERT(type->kind == Type_Proc);
|
||||
if (type == t_invalid) {
|
||||
return;
|
||||
}
|
||||
GB_ASSERT_MSG(type->kind == Type_Proc, "%s", type_to_string(e->type));
|
||||
|
||||
if (is_type_polymorphic(type) && !type->Proc.is_poly_specialized) {
|
||||
return;
|
||||
@@ -6051,11 +6080,14 @@ gb_internal void check_unique_package_names(Checker *c) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
begin_error_block();
|
||||
error(curr, "Duplicate declaration of 'package %.*s'", LIT(name));
|
||||
error_line("\tA package name must be unique\n"
|
||||
"\tThere is no relation between a package name and the directory that contains it, so they can be completely different\n"
|
||||
"\tA package name is required for link name prefixing to have a consistent ABI\n");
|
||||
error(prev, "found at previous location");
|
||||
error_line("%s found at previous location\n", token_pos_to_string(ast_token(prev).pos));
|
||||
end_error_block();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6320,6 +6352,8 @@ gb_internal void check_parsed_files(Checker *c) {
|
||||
|
||||
error(token, "Undefined entry point procedure 'main'");
|
||||
}
|
||||
} else if (build_context.build_mode == BuildMode_DynamicLibrary && build_context.no_entry_point) {
|
||||
c->info.entry_point = nullptr;
|
||||
}
|
||||
|
||||
thread_pool_wait();
|
||||
|
||||
+6
-3
@@ -360,7 +360,7 @@ struct GenProcsData {
|
||||
|
||||
struct GenTypesData {
|
||||
Array<Entity *> types;
|
||||
RwMutex mutex;
|
||||
RecursiveMutex mutex;
|
||||
};
|
||||
|
||||
// CheckerInfo stores all the symbol information for a type-checked program
|
||||
@@ -400,8 +400,8 @@ struct CheckerInfo {
|
||||
|
||||
RecursiveMutex lazy_mutex; // Mutex required for lazy type checking of specific files
|
||||
|
||||
RwMutex gen_types_mutex;
|
||||
PtrMap<Type *, GenTypesData > gen_types;
|
||||
BlockingMutex gen_types_mutex;
|
||||
PtrMap<Type *, GenTypesData *> gen_types;
|
||||
|
||||
BlockingMutex type_info_mutex; // NOT recursive
|
||||
Array<Type *> type_info_types;
|
||||
@@ -560,3 +560,6 @@ gb_internal void init_core_context(Checker *c);
|
||||
gb_internal void init_mem_allocator(Checker *c);
|
||||
|
||||
gb_internal void add_untyped_expressions(CheckerInfo *cinfo, UntypedExprInfoMap *untyped);
|
||||
|
||||
|
||||
gb_internal GenTypesData *ensure_polymorphic_record_entity_has_gen_types(CheckerContext *ctx, Type *original_type);
|
||||
+10
-8
@@ -15,7 +15,7 @@ struct OdinDocVersionType {
|
||||
|
||||
#define OdinDocVersionType_Major 0
|
||||
#define OdinDocVersionType_Minor 3
|
||||
#define OdinDocVersionType_Patch 0
|
||||
#define OdinDocVersionType_Patch 1
|
||||
|
||||
struct OdinDocHeaderBase {
|
||||
u8 magic[8];
|
||||
@@ -163,13 +163,15 @@ enum OdinDocEntityFlag : u64 {
|
||||
OdinDocEntityFlag_Foreign = 1ull<<0,
|
||||
OdinDocEntityFlag_Export = 1ull<<1,
|
||||
|
||||
OdinDocEntityFlag_Param_Using = 1ull<<2,
|
||||
OdinDocEntityFlag_Param_Const = 1ull<<3,
|
||||
OdinDocEntityFlag_Param_AutoCast = 1ull<<4,
|
||||
OdinDocEntityFlag_Param_Ellipsis = 1ull<<5,
|
||||
OdinDocEntityFlag_Param_CVararg = 1ull<<6,
|
||||
OdinDocEntityFlag_Param_NoAlias = 1ull<<7,
|
||||
OdinDocEntityFlag_Param_AnyInt = 1ull<<8,
|
||||
OdinDocEntityFlag_Param_Using = 1ull<<2,
|
||||
OdinDocEntityFlag_Param_Const = 1ull<<3,
|
||||
OdinDocEntityFlag_Param_AutoCast = 1ull<<4,
|
||||
OdinDocEntityFlag_Param_Ellipsis = 1ull<<5,
|
||||
OdinDocEntityFlag_Param_CVararg = 1ull<<6,
|
||||
OdinDocEntityFlag_Param_NoAlias = 1ull<<7,
|
||||
OdinDocEntityFlag_Param_AnyInt = 1ull<<8,
|
||||
OdinDocEntityFlag_Param_ByPtr = 1ull<<9,
|
||||
OdinDocEntityFlag_Param_NoBroadcast = 1ull<<10,
|
||||
|
||||
OdinDocEntityFlag_BitField_Field = 1ull<<19,
|
||||
|
||||
|
||||
+7
-5
@@ -925,11 +925,13 @@ gb_internal OdinDocEntityIndex odin_doc_add_entity(OdinDocWriter *w, Entity *e)
|
||||
break;
|
||||
}
|
||||
|
||||
if (e->flags & EntityFlag_Using) { flags |= OdinDocEntityFlag_Param_Using; }
|
||||
if (e->flags & EntityFlag_ConstInput) { flags |= OdinDocEntityFlag_Param_Const; }
|
||||
if (e->flags & EntityFlag_Ellipsis) { flags |= OdinDocEntityFlag_Param_Ellipsis; }
|
||||
if (e->flags & EntityFlag_NoAlias) { flags |= OdinDocEntityFlag_Param_NoAlias; }
|
||||
if (e->flags & EntityFlag_AnyInt) { flags |= OdinDocEntityFlag_Param_AnyInt; }
|
||||
if (e->flags & EntityFlag_Using) { flags |= OdinDocEntityFlag_Param_Using; }
|
||||
if (e->flags & EntityFlag_ConstInput) { flags |= OdinDocEntityFlag_Param_Const; }
|
||||
if (e->flags & EntityFlag_Ellipsis) { flags |= OdinDocEntityFlag_Param_Ellipsis; }
|
||||
if (e->flags & EntityFlag_NoAlias) { flags |= OdinDocEntityFlag_Param_NoAlias; }
|
||||
if (e->flags & EntityFlag_AnyInt) { flags |= OdinDocEntityFlag_Param_AnyInt; }
|
||||
if (e->flags & EntityFlag_ByPtr) { flags |= OdinDocEntityFlag_Param_ByPtr; }
|
||||
if (e->flags & EntityFlag_NoBroadcast) { flags |= OdinDocEntityFlag_Param_NoBroadcast; }
|
||||
|
||||
if (e->scope && (e->scope->flags & (ScopeFlag_File|ScopeFlag_Pkg)) && !is_entity_exported(e)) {
|
||||
flags |= OdinDocEntityFlag_Private;
|
||||
|
||||
+24
-1
@@ -60,7 +60,7 @@ enum EntityFlag : u64 {
|
||||
EntityFlag_ProcBodyChecked = 1ull<<21,
|
||||
|
||||
EntityFlag_CVarArg = 1ull<<22,
|
||||
|
||||
EntityFlag_NoBroadcast = 1ull<<23,
|
||||
EntityFlag_AnyInt = 1ull<<24,
|
||||
|
||||
EntityFlag_Disabled = 1ull<<25,
|
||||
@@ -229,6 +229,7 @@ struct Entity {
|
||||
CommentGroup *comment;
|
||||
bool is_foreign;
|
||||
bool is_export;
|
||||
bool is_global;
|
||||
} Variable;
|
||||
struct {
|
||||
Type * type_parameter_specialization;
|
||||
@@ -480,3 +481,25 @@ gb_internal Entity *strip_entity_wrapping(Ast *expr) {
|
||||
Entity *e = entity_from_expr(expr);
|
||||
return strip_entity_wrapping(e);
|
||||
}
|
||||
|
||||
|
||||
gb_internal bool is_entity_local_variable(Entity *e) {
|
||||
if (e == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (e->kind != Entity_Variable) {
|
||||
return false;
|
||||
}
|
||||
if (e->Variable.is_global) {
|
||||
return false;
|
||||
}
|
||||
if (e->scope == nullptr) {
|
||||
return true;
|
||||
}
|
||||
if (e->flags & (EntityFlag_ForValue|EntityFlag_SwitchValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((e->scope->flags &~ ScopeFlag_ContextDefined) == 0) ||
|
||||
(e->scope->flags & ScopeFlag_Proc) != 0;
|
||||
}
|
||||
+8
-4
@@ -28,7 +28,7 @@ gb_global ErrorCollector global_error_collector;
|
||||
|
||||
|
||||
gb_internal void push_error_value(TokenPos const &pos, ErrorValueKind kind = ErrorValue_Error) {
|
||||
GB_ASSERT(global_error_collector.curr_error_value_set.load() == false);
|
||||
GB_ASSERT_MSG(global_error_collector.curr_error_value_set.load() == false, "Possible race condition in error handling system, please report this with an issue");
|
||||
ErrorValue ev = {kind, pos};
|
||||
ev.msgs.allocator = heap_allocator();
|
||||
|
||||
@@ -53,7 +53,7 @@ gb_internal void try_pop_error_value(void) {
|
||||
}
|
||||
|
||||
gb_internal ErrorValue *get_error_value(void) {
|
||||
GB_ASSERT(global_error_collector.curr_error_value_set.load() == true);
|
||||
GB_ASSERT_MSG(global_error_collector.curr_error_value_set.load() == true, "Possible race condition in error handling system, please report this with an issue");
|
||||
return &global_error_collector.curr_error_value;
|
||||
}
|
||||
|
||||
@@ -600,7 +600,9 @@ gb_internal void syntax_error_with_verbose(TokenPos pos, TokenPos end, char cons
|
||||
|
||||
|
||||
gb_internal void compiler_error(char const *fmt, ...) {
|
||||
print_all_errors();
|
||||
if (any_errors()) {
|
||||
print_all_errors();
|
||||
}
|
||||
|
||||
va_list va;
|
||||
|
||||
@@ -614,7 +616,9 @@ gb_internal void compiler_error(char const *fmt, ...) {
|
||||
|
||||
|
||||
gb_internal void exit_with_errors(void) {
|
||||
print_all_errors();
|
||||
if (any_errors()) {
|
||||
print_all_errors();
|
||||
}
|
||||
gb_exit(1);
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -502,7 +502,6 @@ gb_internal i32 linker_stage(LinkerData *gen) {
|
||||
platform_lib_str = gb_string_appendc(platform_lib_str, "-L/opt/local/lib ");
|
||||
}
|
||||
|
||||
// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
|
||||
if (build_context.minimum_os_version_string.len) {
|
||||
link_settings = gb_string_append_fmt(link_settings, "-mmacosx-version-min=%.*s ", LIT(build_context.minimum_os_version_string));
|
||||
}
|
||||
@@ -513,7 +512,9 @@ gb_internal i32 linker_stage(LinkerData *gen) {
|
||||
if (!build_context.no_crt) {
|
||||
platform_lib_str = gb_string_appendc(platform_lib_str, "-lm ");
|
||||
if (build_context.metrics.os == TargetOs_darwin) {
|
||||
platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem ");
|
||||
// NOTE: adding this causes a warning about duplicate libraries, I think it is
|
||||
// automatically assumed/added by clang when you don't do `-nostdlib`.
|
||||
// platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem ");
|
||||
} else {
|
||||
platform_lib_str = gb_string_appendc(platform_lib_str, "-lc ");
|
||||
}
|
||||
|
||||
+19
-2
@@ -1146,11 +1146,26 @@ namespace lbAbiArm64 {
|
||||
if (size <= 16) {
|
||||
LLVMTypeRef cast_type = nullptr;
|
||||
|
||||
GB_ASSERT(size > 0);
|
||||
if (size <= 8) {
|
||||
cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8));
|
||||
} else {
|
||||
unsigned count = cast(unsigned)((size+7)/8);
|
||||
cast_type = llvm_array_type(LLVMInt64TypeInContext(c), count);
|
||||
|
||||
LLVMTypeRef llvm_i64 = LLVMIntTypeInContext(c, 64);
|
||||
LLVMTypeRef *types = gb_alloc_array(temporary_allocator(), LLVMTypeRef, count);
|
||||
|
||||
i64 size_copy = size;
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
if (size_copy >= 8) {
|
||||
types[i] = llvm_i64;
|
||||
} else {
|
||||
types[i] = LLVMIntTypeInContext(c, 8*cast(unsigned)size_copy);
|
||||
}
|
||||
size_copy -= 8;
|
||||
}
|
||||
GB_ASSERT(size_copy <= 0);
|
||||
cast_type = LLVMStructTypeInContext(c, types, count, true);
|
||||
}
|
||||
return lb_arg_type_direct(return_type, cast_type, nullptr, nullptr);
|
||||
} else {
|
||||
@@ -1183,7 +1198,9 @@ namespace lbAbiArm64 {
|
||||
i64 size = lb_sizeof(type);
|
||||
if (size <= 16) {
|
||||
LLVMTypeRef cast_type = nullptr;
|
||||
if (size <= 8) {
|
||||
if (size == 0) {
|
||||
cast_type = LLVMStructTypeInContext(c, nullptr, 0, false);
|
||||
} else if (size <= 8) {
|
||||
cast_type = LLVMIntTypeInContext(c, cast(unsigned)(size*8));
|
||||
} else {
|
||||
unsigned count = cast(unsigned)((size+7)/8);
|
||||
|
||||
+1
-1
@@ -1925,7 +1925,7 @@ gb_internal void print_show_help(String const arg0, String const &command) {
|
||||
if (run_or_build) {
|
||||
print_usage_line(1, "-minimum-os-version:<string>");
|
||||
print_usage_line(2, "Sets the minimum OS version targeted by the application.");
|
||||
print_usage_line(2, "Example: -minimum-os-version:12.0.0");
|
||||
print_usage_line(2, "Default: -minimum-os-version:11.0.0");
|
||||
print_usage_line(2, "(Only used when target is Darwin.)");
|
||||
print_usage_line(0, "");
|
||||
|
||||
|
||||
+36
-15
@@ -2351,9 +2351,6 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
|
||||
return ast_bad_expr(f, token, name);
|
||||
}
|
||||
switch (expr->kind) {
|
||||
case Ast_ArrayType:
|
||||
syntax_error(expr, "#partial has been replaced with #sparse for non-contiguous enumerated array types");
|
||||
break;
|
||||
case Ast_CompoundLit:
|
||||
expr->CompoundLit.tag = tag;
|
||||
break;
|
||||
@@ -2541,6 +2538,9 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
|
||||
return ast_pointer_type(f, token, elem);
|
||||
} break;
|
||||
|
||||
case Token_Mul:
|
||||
return parse_unary_expr(f, true);
|
||||
|
||||
case Token_OpenBracket: {
|
||||
Token token = expect_token(f, Token_OpenBracket);
|
||||
Ast *count_expr = nullptr;
|
||||
@@ -3274,7 +3274,9 @@ gb_internal Ast *parse_unary_expr(AstFile *f, bool lhs) {
|
||||
case Token_Sub:
|
||||
case Token_Xor:
|
||||
case Token_And:
|
||||
case Token_Not: {
|
||||
case Token_Not:
|
||||
case Token_Mul: // Used for error handling when people do C-like things
|
||||
{
|
||||
Token token = advance_token(f);
|
||||
Ast *expr = parse_unary_expr(f, lhs);
|
||||
return ast_unary_expr(f, token, expr);
|
||||
@@ -3661,6 +3663,7 @@ gb_internal Ast *parse_simple_stmt(AstFile *f, u32 flags) {
|
||||
expect_token_after(f, Token_Colon, "identifier list");
|
||||
if ((flags&StmtAllowFlag_Label) && lhs.count == 1) {
|
||||
bool is_partial = false;
|
||||
bool is_reverse = false;
|
||||
Token partial_token = {};
|
||||
if (f->curr_token.kind == Token_Hash) {
|
||||
// NOTE(bill): This is purely for error messages
|
||||
@@ -3670,6 +3673,11 @@ gb_internal Ast *parse_simple_stmt(AstFile *f, u32 flags) {
|
||||
partial_token = expect_token(f, Token_Hash);
|
||||
expect_token(f, Token_Ident);
|
||||
is_partial = true;
|
||||
} else if (name.kind == Token_Ident && name.string == "reverse" &&
|
||||
peek_token_n(f, 1).kind == Token_for) {
|
||||
partial_token = expect_token(f, Token_Hash);
|
||||
expect_token(f, Token_Ident);
|
||||
is_reverse = true;
|
||||
}
|
||||
}
|
||||
switch (f->curr_token.kind) {
|
||||
@@ -3704,6 +3712,18 @@ gb_internal Ast *parse_simple_stmt(AstFile *f, u32 flags) {
|
||||
break;
|
||||
}
|
||||
syntax_error(partial_token, "Incorrect use of directive, use '#partial %.*s: switch'", LIT(ast_token(name).string));
|
||||
} else if (is_reverse) {
|
||||
switch (stmt->kind) {
|
||||
case Ast_RangeStmt:
|
||||
if (stmt->RangeStmt.reverse) {
|
||||
syntax_error(token, "#reverse already applied to a 'for in' statement");
|
||||
}
|
||||
stmt->RangeStmt.reverse = true;
|
||||
break;
|
||||
default:
|
||||
syntax_error(token, "#reverse can only be applied to a 'for in' statement");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return stmt;
|
||||
@@ -3898,14 +3918,15 @@ struct ParseFieldPrefixMapping {
|
||||
FieldFlag flag;
|
||||
};
|
||||
|
||||
gb_global ParseFieldPrefixMapping parse_field_prefix_mappings[] = {
|
||||
{str_lit("using"), Token_using, FieldFlag_using},
|
||||
{str_lit("no_alias"), Token_Hash, FieldFlag_no_alias},
|
||||
{str_lit("c_vararg"), Token_Hash, FieldFlag_c_vararg},
|
||||
{str_lit("const"), Token_Hash, FieldFlag_const},
|
||||
{str_lit("any_int"), Token_Hash, FieldFlag_any_int},
|
||||
{str_lit("subtype"), Token_Hash, FieldFlag_subtype},
|
||||
{str_lit("by_ptr"), Token_Hash, FieldFlag_by_ptr},
|
||||
gb_global ParseFieldPrefixMapping const parse_field_prefix_mappings[] = {
|
||||
{str_lit("using"), Token_using, FieldFlag_using},
|
||||
{str_lit("no_alias"), Token_Hash, FieldFlag_no_alias},
|
||||
{str_lit("c_vararg"), Token_Hash, FieldFlag_c_vararg},
|
||||
{str_lit("const"), Token_Hash, FieldFlag_const},
|
||||
{str_lit("any_int"), Token_Hash, FieldFlag_any_int},
|
||||
{str_lit("subtype"), Token_Hash, FieldFlag_subtype},
|
||||
{str_lit("by_ptr"), Token_Hash, FieldFlag_by_ptr},
|
||||
{str_lit("no_broadcast"), Token_Hash, FieldFlag_no_broadcast},
|
||||
};
|
||||
|
||||
|
||||
@@ -6289,7 +6310,7 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
|
||||
if (!path_is_directory(init_fullpath)) {
|
||||
String const ext = str_lit(".odin");
|
||||
if (!string_ends_with(init_fullpath, ext)) {
|
||||
error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
|
||||
error({}, "Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
|
||||
return ParseFile_WrongExtension;
|
||||
}
|
||||
} else if (init_fullpath.len != 0) {
|
||||
@@ -6302,7 +6323,7 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
|
||||
String short_path = filename_from_path(path);
|
||||
char *cpath = alloc_cstring(temporary_allocator(), short_path);
|
||||
if (gb_file_exists(cpath)) {
|
||||
error_line("Please specify the executable name with -out:<string> as a directory exists with the same name in the current working directory");
|
||||
error({}, "Please specify the executable name with -out:<string> as a directory exists with the same name in the current working directory");
|
||||
return ParseFile_DirectoryAlreadyExists;
|
||||
}
|
||||
}
|
||||
@@ -6338,7 +6359,7 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
|
||||
if (!path_is_directory(fullpath)) {
|
||||
String const ext = str_lit(".odin");
|
||||
if (!string_ends_with(fullpath, ext)) {
|
||||
error_line("Expected either a directory or a .odin file, got '%.*s'\n", LIT(fullpath));
|
||||
error({}, "Expected either a directory or a .odin file, got '%.*s'\n", LIT(fullpath));
|
||||
return ParseFile_WrongExtension;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -326,6 +326,7 @@ enum FieldFlag : u32 {
|
||||
FieldFlag_any_int = 1<<6,
|
||||
FieldFlag_subtype = 1<<7,
|
||||
FieldFlag_by_ptr = 1<<8,
|
||||
FieldFlag_no_broadcast = 1<<9, // disallow array programming
|
||||
|
||||
// Internal use by the parser only
|
||||
FieldFlag_Tags = 1<<10,
|
||||
@@ -336,7 +337,7 @@ enum FieldFlag : u32 {
|
||||
FieldFlag_Invalid = 1u<<31,
|
||||
|
||||
// Parameter List Restrictions
|
||||
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr,
|
||||
FieldFlag_Signature = FieldFlag_ellipsis|FieldFlag_using|FieldFlag_no_alias|FieldFlag_c_vararg|FieldFlag_const|FieldFlag_any_int|FieldFlag_by_ptr|FieldFlag_no_broadcast,
|
||||
FieldFlag_Struct = FieldFlag_using|FieldFlag_subtype|FieldFlag_Tags,
|
||||
};
|
||||
|
||||
|
||||
+1
-3
@@ -113,7 +113,7 @@ struct Wait_Signal {
|
||||
|
||||
gb_internal void wait_signal_until_available(Wait_Signal *ws) {
|
||||
if (ws->futex.load() == 0) {
|
||||
futex_wait(&ws->futex, 1);
|
||||
futex_wait(&ws->futex, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,8 +122,6 @@ gb_internal void wait_signal_set(Wait_Signal *ws) {
|
||||
futex_broadcast(&ws->futex);
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct MutexGuard {
|
||||
MutexGuard() = delete;
|
||||
MutexGuard(MutexGuard const &) = delete;
|
||||
|
||||
+5
-1
@@ -24,7 +24,8 @@ all: c_libc_test \
|
||||
slice_test \
|
||||
strings_test \
|
||||
thread_test \
|
||||
runtime_test
|
||||
runtime_test \
|
||||
time_test
|
||||
|
||||
download_test_assets:
|
||||
$(PYTHON) download_assets.py
|
||||
@@ -94,3 +95,6 @@ thread_test:
|
||||
|
||||
runtime_test:
|
||||
$(ODIN) run runtime $(COMMON) -out:test_core_runtime
|
||||
|
||||
time_test:
|
||||
$(ODIN) run time $(COMMON) -out:test_core_time
|
||||
|
||||
@@ -100,3 +100,8 @@ echo ---
|
||||
echo Running core:runtime tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% run runtime %COMMON% %COLLECTION% -out:test_core_runtime.exe || exit /b
|
||||
|
||||
echo ---
|
||||
echo Running core:time tests
|
||||
echo ---
|
||||
%PATH_TO_ODIN% run time %COMMON% %COLLECTION% -out:test_core_time.exe || exit /b
|
||||
@@ -0,0 +1,178 @@
|
||||
package test_core_time
|
||||
|
||||
import "core:fmt"
|
||||
import "core:mem"
|
||||
import "core:os"
|
||||
import "core:testing"
|
||||
import "core:time"
|
||||
import dt "core:time/datetime"
|
||||
|
||||
is_leap_year :: time.is_leap_year
|
||||
|
||||
TEST_count := 0
|
||||
TEST_fail := 0
|
||||
|
||||
when ODIN_TEST {
|
||||
expect :: testing.expect
|
||||
expect_value :: testing.expect_value
|
||||
log :: testing.log
|
||||
} else {
|
||||
expect :: proc(t: ^testing.T, condition: bool, message: string, loc := #caller_location) {
|
||||
TEST_count += 1
|
||||
if !condition {
|
||||
TEST_fail += 1
|
||||
fmt.printf("[%v] %v\n", loc, message)
|
||||
return
|
||||
}
|
||||
}
|
||||
log :: proc(t: ^testing.T, v: any, loc := #caller_location) {
|
||||
fmt.printf("[%v] ", loc)
|
||||
fmt.printf("log: %v\n", v)
|
||||
}
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
t := testing.T{}
|
||||
|
||||
track: mem.Tracking_Allocator
|
||||
mem.tracking_allocator_init(&track, context.allocator)
|
||||
defer mem.tracking_allocator_destroy(&track)
|
||||
context.allocator = mem.tracking_allocator(&track)
|
||||
|
||||
test_ordinal_date_roundtrip(&t)
|
||||
test_component_to_time_roundtrip(&t)
|
||||
test_parse_rfc3339_string(&t)
|
||||
|
||||
for _, leak in track.allocation_map {
|
||||
expect(&t, false, fmt.tprintf("%v leaked %m\n", leak.location, leak.size))
|
||||
}
|
||||
for bad_free in track.bad_free_array {
|
||||
expect(&t, false, fmt.tprintf("%v allocation %p was freed badly\n", bad_free.location, bad_free.memory))
|
||||
}
|
||||
|
||||
fmt.printf("%v/%v tests successful.\n", TEST_count - TEST_fail, TEST_count)
|
||||
if TEST_fail > 0 {
|
||||
os.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@test
|
||||
test_ordinal_date_roundtrip :: proc(t: ^testing.T) {
|
||||
expect(t, dt.unsafe_ordinal_to_date(dt.unsafe_date_to_ordinal(dt.MIN_DATE)) == dt.MIN_DATE, "Roundtripping MIN_DATE failed.")
|
||||
expect(t, dt.unsafe_date_to_ordinal(dt.unsafe_ordinal_to_date(dt.MIN_ORD)) == dt.MIN_ORD, "Roundtripping MIN_ORD failed.")
|
||||
expect(t, dt.unsafe_ordinal_to_date(dt.unsafe_date_to_ordinal(dt.MAX_DATE)) == dt.MAX_DATE, "Roundtripping MAX_DATE failed.")
|
||||
expect(t, dt.unsafe_date_to_ordinal(dt.unsafe_ordinal_to_date(dt.MAX_ORD)) == dt.MAX_ORD, "Roundtripping MAX_ORD failed.")
|
||||
}
|
||||
|
||||
/*
|
||||
1990-12-31T23:59:60Z
|
||||
|
||||
This represents the leap second inserted at the end of 1990.
|
||||
|
||||
1990-12-31T15:59:60-08:00
|
||||
|
||||
This represents the same leap second in Pacific Standard Time, 8 hours behind UTC.
|
||||
|
||||
1937-01-01T12:00:27.87+00:20
|
||||
|
||||
This represents the same instant of time as noon, January 1, 1937, Netherlands time.
|
||||
Standard time in the Netherlands was exactly 19 minutes and 32.13 seconds ahead of UTC by law from 1909-05-01 through 1937-06-30.
|
||||
This time zone cannot be represented exactly using the HH:MM format, and this timestamp uses the closest representable UTC offset.
|
||||
*/
|
||||
RFC3339_Test :: struct{
|
||||
rfc_3339: string,
|
||||
datetime: time.Time,
|
||||
apply_offset: bool,
|
||||
utc_offset: int,
|
||||
consumed: int,
|
||||
is_leap: bool,
|
||||
}
|
||||
|
||||
// These are based on RFC 3339's examples, see https://www.rfc-editor.org/rfc/rfc3339#page-10
|
||||
rfc3339_tests :: []RFC3339_Test{
|
||||
// This represents 20 minutes and 50.52 seconds after the 23rd hour of April 12th, 1985 in UTC.
|
||||
{"1985-04-12T23:20:50.52Z", {482196050520000000}, true, 0, 23, false},
|
||||
|
||||
// This represents 39 minutes and 57 seconds after the 16th hour of December 19th, 1996 with an offset of -08:00 from UTC (Pacific Standard Time).
|
||||
// Note that this is equivalent to 1996-12-20T00:39:57Z in UTC.
|
||||
{"1996-12-19T16:39:57-08:00", {851013597000000000}, false, -480, 25, false},
|
||||
{"1996-12-19T16:39:57-08:00", {851042397000000000}, true, 0, 25, false},
|
||||
{"1996-12-20T00:39:57Z", {851042397000000000}, false, 0, 20, false},
|
||||
|
||||
// This represents the leap second inserted at the end of 1990.
|
||||
// It'll be represented as 1990-12-31 23:59:59 UTC after parsing, and `is_leap` will be set to `true`.
|
||||
{"1990-12-31T23:59:60Z", {662687999000000000}, true, 0, 20, true},
|
||||
|
||||
// This represents the same leap second in Pacific Standard Time, 8 hours behind UTC.
|
||||
{"1990-12-31T15:59:60-08:00", {662687999000000000}, true, 0, 25, true},
|
||||
|
||||
// This represents the same instant of time as noon, January 1, 1937, Netherlands time.
|
||||
// Standard time in the Netherlands was exactly 19 minutes and 32.13 seconds ahead of UTC by law
|
||||
// from 1909-05-01 through 1937-06-30. This time zone cannot be represented exactly using the
|
||||
// HH:MM format, and this timestamp uses the closest representable UTC offset.
|
||||
{"1937-01-01T12:00:27.87+00:20", {-1041335972130000000}, false, 20, 28, false},
|
||||
{"1937-01-01T12:00:27.87+00:20", {-1041337172130000000}, true, 0, 28, false},
|
||||
}
|
||||
|
||||
@test
|
||||
test_parse_rfc3339_string :: proc(t: ^testing.T) {
|
||||
for test in rfc3339_tests {
|
||||
is_leap := false
|
||||
if test.apply_offset {
|
||||
res, consumed := time.rfc3339_to_time_utc(test.rfc_3339, &is_leap)
|
||||
msg := fmt.tprintf("[apply offet] Parsing failed: %v -> %v (nsec: %v). Expected %v consumed, got %v", test.rfc_3339, res, res._nsec, test.consumed, consumed)
|
||||
expect(t, test.consumed == consumed, msg)
|
||||
|
||||
if test.consumed == consumed {
|
||||
expect(t, test.datetime == res, fmt.tprintf("Time didn't match. Expected %v (%v), got %v (%v)", test.datetime, test.datetime._nsec, res, res._nsec))
|
||||
expect(t, test.is_leap == is_leap, "Expected a leap second, got none.")
|
||||
}
|
||||
} else {
|
||||
res, offset, consumed := time.rfc3339_to_time_and_offset(test.rfc_3339)
|
||||
msg := fmt.tprintf("Parsing failed: %v -> %v (nsec: %v), offset: %v. Expected %v consumed, got %v", test.rfc_3339, res, res._nsec, offset, test.consumed, consumed)
|
||||
expect(t, test.consumed == consumed, msg)
|
||||
|
||||
if test.consumed == consumed {
|
||||
expect(t, test.datetime == res, fmt.tprintf("Time didn't match. Expected %v (%v), got %v (%v)", test.datetime, test.datetime._nsec, res, res._nsec))
|
||||
expect(t, test.utc_offset == offset, fmt.tprintf("UTC offset didn't match. Expected %v, got %v", test.utc_offset, offset))
|
||||
expect(t, test.is_leap == is_leap, "Expected a leap second, got none.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MONTH_DAYS := []int{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
|
||||
YEAR_START :: 1900
|
||||
YEAR_END :: 2024
|
||||
|
||||
@test
|
||||
test_component_to_time_roundtrip :: proc(t: ^testing.T) {
|
||||
// Roundtrip a datetime through `datetime_to_time` to `Time` and back to its components.
|
||||
for year in YEAR_START..=YEAR_END {
|
||||
for month in 1..=12 {
|
||||
days := MONTH_DAYS[month - 1]
|
||||
if month == 2 && is_leap_year(year) {
|
||||
days += 1
|
||||
}
|
||||
for day in 1..=days {
|
||||
d, _ := dt.components_to_datetime(year, month, day, 0, 0, 0, 0)
|
||||
date_component_roundtrip_test(t, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
date_component_roundtrip_test :: proc(t: ^testing.T, moment: dt.DateTime) {
|
||||
res, ok := time.datetime_to_time(moment.year, moment.month, moment.day, moment.hour, moment.minute, moment.second)
|
||||
expect(t, ok, "Couldn't convert date components into date")
|
||||
|
||||
YYYY, MM, DD := time.date(res)
|
||||
hh, mm, ss := time.clock(res)
|
||||
|
||||
expected := fmt.tprintf("Expected %4d-%2d-%2d %2d:%2d:%2d, got %4d-%2d-%2d %2d:%2d:%2d",
|
||||
moment.year, moment.month, moment.day, moment.hour, moment.minute, moment.second, YYYY, MM, DD, hh, mm, ss)
|
||||
|
||||
ok = moment.year == i64(YYYY) && moment.month == i8(MM) && moment.day == i8(DD)
|
||||
ok &= moment.hour == i8(hh) && moment.minute == i8(mm) && moment.second == i8(ss)
|
||||
expect(t, ok, expected)
|
||||
}
|
||||
+3
-3
@@ -6,8 +6,8 @@ import "vendor:glfw"
|
||||
import "core:os"
|
||||
|
||||
GLFW_MAJOR :: 3
|
||||
GLFW_MINOR :: 3
|
||||
GLFW_PATCH :: 4
|
||||
GLFW_MINOR :: 4
|
||||
GLFW_PATCH :: 0
|
||||
|
||||
TEST_count := 0
|
||||
TEST_fail := 0
|
||||
@@ -46,4 +46,4 @@ main :: proc() {
|
||||
test_glfw :: proc(t: ^testing.T) {
|
||||
major, minor, patch := glfw.GetVersion()
|
||||
expect(t, major == GLFW_MAJOR && minor == GLFW_MINOR, fmt.tprintf("Expected GLFW.GetVersion: %v.%v.%v, got %v.%v.%v instead", GLFW_MAJOR, GLFW_MINOR, GLFW_PATCH, major, minor, patch))
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+22
-7
@@ -21,14 +21,21 @@ when ODIN_OS == .Windows {
|
||||
"system:shell32.lib",
|
||||
}
|
||||
}
|
||||
} else when ODIN_OS == .Linux {
|
||||
foreign import glfw "system:glfw"
|
||||
} else when ODIN_OS == .Darwin {
|
||||
foreign import glfw {
|
||||
"../lib/darwin/libglfw3.a",
|
||||
"system:Cocoa.framework",
|
||||
"system:IOKit.framework",
|
||||
"system:OpenGL.framework",
|
||||
when GLFW_SHARED {
|
||||
foreign import glfw {
|
||||
"system:glfw",
|
||||
"system:Cocoa.framework",
|
||||
"system:IOKit.framework",
|
||||
"system:OpenGL.framework",
|
||||
}
|
||||
} else {
|
||||
foreign import glfw {
|
||||
"../lib/darwin/libglfw3.a",
|
||||
"system:Cocoa.framework",
|
||||
"system:IOKit.framework",
|
||||
"system:OpenGL.framework",
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreign import glfw "system:glfw"
|
||||
@@ -44,6 +51,10 @@ foreign glfw {
|
||||
|
||||
InitHint :: proc(hint, value: c.int) ---
|
||||
|
||||
InitAllocator :: proc(#by_ptr allocator: Allocator) ---
|
||||
|
||||
InitVulkanLoader :: proc(loader: vk.ProcGetInstanceProcAddr) ---
|
||||
|
||||
GetVersion :: proc(major, minor, rev: ^c.int) ---
|
||||
GetError :: proc(description: ^cstring) -> c.int ---
|
||||
|
||||
@@ -94,6 +105,7 @@ foreign glfw {
|
||||
GetKey :: proc(window: WindowHandle, key: c.int) -> c.int ---
|
||||
GetKeyName :: proc(key, scancode: c.int) -> cstring ---
|
||||
SetWindowShouldClose :: proc(window: WindowHandle, value: b32) ---
|
||||
GetWindowTitle :: proc(window: WindowHandle) -> cstring ---
|
||||
JoystickPresent :: proc(joy: c.int) -> b32 ---
|
||||
GetJoystickName :: proc(joy: c.int) -> cstring ---
|
||||
GetKeyScancode :: proc(key: c.int) -> c.int ---
|
||||
@@ -184,5 +196,8 @@ foreign glfw {
|
||||
SetJoystickCallback :: proc(cbfun: JoystickProc) -> JoystickProc ---
|
||||
|
||||
SetErrorCallback :: proc(cbfun: ErrorProc) -> ErrorProc ---
|
||||
|
||||
GetPlatform :: proc() -> c.int ---
|
||||
PlatformSupported :: proc(platform: c.int) -> b32 ---
|
||||
}
|
||||
|
||||
|
||||
Vendored
+11
@@ -30,6 +30,13 @@ GamepadState :: struct {
|
||||
axes: [6]f32,
|
||||
}
|
||||
|
||||
Allocator :: struct {
|
||||
allocate: AllocateProc,
|
||||
reallocate: ReallocateProc,
|
||||
deallocate: DeallocateProc,
|
||||
user: rawptr,
|
||||
}
|
||||
|
||||
/*** Procedure type declarations ***/
|
||||
WindowIconifyProc :: #type proc "c" (window: WindowHandle, iconified: c.int)
|
||||
WindowRefreshProc :: #type proc "c" (window: WindowHandle)
|
||||
@@ -53,3 +60,7 @@ CursorEnterProc :: #type proc "c" (window: WindowHandle, entered: c.int)
|
||||
JoystickProc :: #type proc "c" (joy, event: c.int)
|
||||
|
||||
ErrorProc :: #type proc "c" (error: c.int, description: cstring)
|
||||
|
||||
AllocateProc :: #type proc "c" (size: c.size_t, user: rawptr) -> rawptr
|
||||
ReallocateProc :: #type proc "c" (block: rawptr, size: c.size_t, user: rawptr) -> rawptr
|
||||
DeallocateProc :: #type proc "c" (block: rawptr, user: rawptr)
|
||||
|
||||
Vendored
+71
-26
@@ -6,8 +6,8 @@ GLFW_SHARED :: #config(GLFW_SHARED, false)
|
||||
/*** Constants ***/
|
||||
/* Versions */
|
||||
VERSION_MAJOR :: 3
|
||||
VERSION_MINOR :: 3
|
||||
VERSION_REVISION :: 8
|
||||
VERSION_MINOR :: 4
|
||||
VERSION_REVISION :: 0
|
||||
|
||||
/* Booleans */
|
||||
TRUE :: true
|
||||
@@ -251,17 +251,21 @@ GAMEPAD_AXIS_RIGHT_TRIGGER :: 5
|
||||
GAMEPAD_AXIS_LAST :: GAMEPAD_AXIS_RIGHT_TRIGGER
|
||||
|
||||
/* Error constants */
|
||||
NO_ERROR :: 0x00000000
|
||||
NOT_INITIALIZED :: 0x00010001
|
||||
NO_CURRENT_CONTEXT :: 0x00010002
|
||||
INVALID_ENUM :: 0x00010003
|
||||
INVALID_VALUE :: 0x00010004
|
||||
OUT_OF_MEMORY :: 0x00010005
|
||||
API_UNAVAILABLE :: 0x00010006
|
||||
VERSION_UNAVAILABLE :: 0x00010007
|
||||
PLATFORM_ERROR :: 0x00010008
|
||||
FORMAT_UNAVAILABLE :: 0x00010009
|
||||
NO_WINDOW_CONTEXT :: 0x0001000A
|
||||
NO_ERROR :: 0x00000000
|
||||
NOT_INITIALIZED :: 0x00010001
|
||||
NO_CURRENT_CONTEXT :: 0x00010002
|
||||
INVALID_ENUM :: 0x00010003
|
||||
INVALID_VALUE :: 0x00010004
|
||||
OUT_OF_MEMORY :: 0x00010005
|
||||
API_UNAVAILABLE :: 0x00010006
|
||||
VERSION_UNAVAILABLE :: 0x00010007
|
||||
PLATFORM_ERROR :: 0x00010008
|
||||
FORMAT_UNAVAILABLE :: 0x00010009
|
||||
NO_WINDOW_CONTEXT :: 0x0001000A
|
||||
CURSOR_UNAVAILABLE :: 0x0001000B
|
||||
FEATURE_UNAVAILABLE :: 0x0001000C
|
||||
FEATURE_UNIMPLEMENTED :: 0x0001000D
|
||||
PLATFORM_UNAVAILABLE :: 0x0001000E
|
||||
|
||||
/* Window attributes */
|
||||
FOCUSED :: 0x00020001
|
||||
@@ -276,6 +280,9 @@ CENTER_CURSOR :: 0x00020009
|
||||
TRANSPARENT_FRAMEBUFFER :: 0x0002000A
|
||||
HOVERED :: 0x0002000B
|
||||
FOCUS_ON_SHOW :: 0x0002000C
|
||||
MOUSE_PASSTHROUGH :: 0x0002000D
|
||||
POSITION_X :: 0x0002000E
|
||||
POSITION_Y :: 0x0002000F
|
||||
|
||||
/* Pixel window attributes */
|
||||
RED_BITS :: 0x00021001
|
||||
@@ -302,12 +309,14 @@ CONTEXT_VERSION_MINOR :: 0x00022003
|
||||
CONTEXT_REVISION :: 0x00022004
|
||||
CONTEXT_ROBUSTNESS :: 0x00022005
|
||||
OPENGL_FORWARD_COMPAT :: 0x00022006
|
||||
OPENGL_DEBUG_CONTEXT :: 0x00022007
|
||||
CONTEXT_DEBUG :: 0x00022007
|
||||
OPENGL_DEBUG_CONTEXT :: CONTEXT_DEBUG // Backwards compatibility
|
||||
OPENGL_PROFILE :: 0x00022008
|
||||
CONTEXT_RELEASE_BEHAVIOR :: 0x00022009
|
||||
CONTEXT_NO_ERROR :: 0x0002200A
|
||||
CONTEXT_CREATION_API :: 0x0002200B
|
||||
SCALE_TO_MONITOR :: 0x0002200C
|
||||
SCALE_FRAMEBUFFER :: 0x0002200D
|
||||
|
||||
/* Cross platform attributes */
|
||||
COCOA_RETINA_FRAMEBUFFER :: 0x00023001
|
||||
@@ -315,6 +324,9 @@ COCOA_FRAME_NAME :: 0x00023002
|
||||
COCOA_GRAPHICS_SWITCHING :: 0x00023003
|
||||
X11_CLASS_NAME :: 0x00024001
|
||||
X11_INSTANCE_NAME :: 0x00024002
|
||||
WIN32_KEYBOARD_MENU :: 0x00025001
|
||||
WIN32_SHOWDEFAULT :: 0x00025002
|
||||
WAYLAND_APP_ID :: 0x00026001
|
||||
|
||||
/* APIs */
|
||||
NO_API :: 0
|
||||
@@ -341,6 +353,7 @@ LOCK_KEY_MODS :: 0x00033004
|
||||
CURSOR_NORMAL :: 0x00034001
|
||||
CURSOR_HIDDEN :: 0x00034002
|
||||
CURSOR_DISABLED :: 0x00034003
|
||||
CURSOR_CAPTURED :: 0x00034004
|
||||
|
||||
/* Mouse motion */
|
||||
RAW_MOUSE_MOTION :: 0x00033005
|
||||
@@ -355,24 +368,56 @@ NATIVE_CONTEXT_API :: 0x00036001
|
||||
EGL_CONTEXT_API :: 0x00036002
|
||||
OSMESA_CONTEXT_API :: 0x00036003
|
||||
|
||||
ANGLE_PLATFORM_TYPE_NONE :: 0x00037001
|
||||
ANGLE_PLATFORM_TYPE_OPENGL :: 0x00037002
|
||||
ANGLE_PLATFORM_TYPE_OPENGLES :: 0x00037003
|
||||
ANGLE_PLATFORM_TYPE_D3D9 :: 0x00037004
|
||||
ANGLE_PLATFORM_TYPE_D3D11 :: 0x00037005
|
||||
ANGLE_PLATFORM_TYPE_VULKAN :: 0x00037007
|
||||
ANGLE_PLATFORM_TYPE_METAL :: 0x00037008
|
||||
|
||||
WAYLAND_PREFER_LIBDECOR :: 0x00038001
|
||||
WAYLAND_DISABLE_LIBDECOR :: 0x00038002
|
||||
|
||||
ANY_POSITION :: 0x80000000
|
||||
|
||||
/* Types of cursors */
|
||||
ARROW_CURSOR :: 0x00036001
|
||||
IBEAM_CURSOR :: 0x00036002
|
||||
CROSSHAIR_CURSOR :: 0x00036003
|
||||
HAND_CURSOR :: 0x00036004
|
||||
HRESIZE_CURSOR :: 0x00036005
|
||||
VRESIZE_CURSOR :: 0x00036006
|
||||
RESIZE_NWSE_CURSOR :: 0x00036007
|
||||
RESIZE_NESW_CURSOR :: 0x00036008
|
||||
ARROW_CURSOR :: 0x00036001
|
||||
IBEAM_CURSOR :: 0x00036002
|
||||
CROSSHAIR_CURSOR :: 0x00036003
|
||||
POINTING_HAND_CURSOR :: 0x00036004
|
||||
RESIZE_EW_CURSOR :: 0x00036005
|
||||
RESIZE_NS_CURSOR :: 0x00036006
|
||||
RESIZE_NWSE_CURSOR :: 0x00036007
|
||||
RESIZE_NESW_CURSOR :: 0x00036008
|
||||
RESIZE_ALL_CURSOR :: 0x00036009
|
||||
NOT_ALLOWED_CURSOR :: 0x0003600A
|
||||
|
||||
/* Backwards compatibility cursors. */
|
||||
HRESIZE_CURSOR :: RESIZE_EW_CURSOR
|
||||
VRESIZE_CURSOR :: RESIZE_NS_CURSOR
|
||||
HAND_CURSOR :: POINTING_HAND_CURSOR
|
||||
|
||||
/* Joystick? */
|
||||
CONNECTED :: 0x00040001
|
||||
DISCONNECTED :: 0x00040002
|
||||
|
||||
/* macOS specific init hint. */
|
||||
JOYSTICK_HAT_BUTTONS :: 0x00050001
|
||||
COCOA_CHDIR_RESOURCES :: 0x00051001
|
||||
COCOA_MENUBAR :: 0x00051002
|
||||
JOYSTICK_HAT_BUTTONS :: 0x00050001
|
||||
ANGLE_PLATFORM_TYPE :: 0x00050002
|
||||
PLATFORM :: 0x00050003
|
||||
|
||||
/* Platform specific init hints. */
|
||||
COCOA_CHDIR_RESOURCES :: 0x00051001
|
||||
COCOA_MENUBAR :: 0x00051002
|
||||
X11_XCB_VULKAN_SURFACE :: 0x00052001
|
||||
WAYLAND_LIBDECOR :: 0x00053001
|
||||
|
||||
ANY_PLATFORM :: 0x00060000
|
||||
PLATFORM_WIN32 :: 0x00060001
|
||||
PLATFORM_COCOA :: 0x00060002
|
||||
PLATFORM_WAYLAND :: 0x00060003
|
||||
PLATFORM_X11 :: 0x00060004
|
||||
PLATFORM_NULL :: 0x00060005
|
||||
|
||||
/* */
|
||||
DONT_CARE :: -1
|
||||
|
||||
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
BIN
Binary file not shown.
Vendored
+3
-11
@@ -4,18 +4,10 @@ package glfw
|
||||
|
||||
import NS "vendor:darwin/Foundation"
|
||||
|
||||
when GLFW_SHARED {
|
||||
#panic("Dynamic linking for glfw is not supported for darwin yet")
|
||||
foreign import glfw {"_"}
|
||||
} else {
|
||||
foreign import glfw {
|
||||
"lib/darwin/libglfw3.a",
|
||||
}
|
||||
}
|
||||
|
||||
@(default_calling_convention="c", link_prefix="glfw")
|
||||
foreign glfw {
|
||||
GetCocoaWindow :: proc(window: WindowHandle) -> ^NS.Window ---
|
||||
foreign {
|
||||
GetCocoaWindow :: proc(window: WindowHandle) -> ^NS.Window ---
|
||||
GetCocoaView :: proc(window: WindowHandle) -> ^NS.View ---
|
||||
}
|
||||
|
||||
// TODO:
|
||||
|
||||
Vendored
+1
-17
@@ -4,24 +4,8 @@ package glfw
|
||||
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
when GLFW_SHARED {
|
||||
foreign import glfw {
|
||||
"lib/glfw3dll.lib",
|
||||
"system:user32.lib",
|
||||
"system:gdi32.lib",
|
||||
"system:shell32.lib",
|
||||
}
|
||||
} else {
|
||||
foreign import glfw {
|
||||
"lib/glfw3_mt.lib",
|
||||
"system:user32.lib",
|
||||
"system:gdi32.lib",
|
||||
"system:shell32.lib",
|
||||
}
|
||||
}
|
||||
|
||||
@(default_calling_convention="c", link_prefix="glfw")
|
||||
foreign glfw {
|
||||
foreign {
|
||||
GetWin32Adapter :: proc(monitor: MonitorHandle) -> cstring ---
|
||||
GetWin32Monitor :: proc(monitor: MonitorHandle) -> cstring ---
|
||||
GetWin32Window :: proc(window: WindowHandle) -> win32.HWND ---
|
||||
|
||||
Vendored
+6
@@ -11,6 +11,8 @@ GammaRamp :: glfw.GammaRamp
|
||||
Image :: glfw.Image
|
||||
GamepadState :: glfw.GamepadState
|
||||
|
||||
Allocator :: glfw.Allocator
|
||||
|
||||
/*** Procedure type declarations ***/
|
||||
WindowIconifyProc :: glfw.WindowIconifyProc
|
||||
WindowRefreshProc :: glfw.WindowRefreshProc
|
||||
@@ -34,3 +36,7 @@ CursorEnterProc :: glfw.CursorEnterProc
|
||||
JoystickProc :: glfw.JoystickProc
|
||||
|
||||
ErrorProc :: glfw.ErrorProc
|
||||
|
||||
AllocateProc :: glfw.AllocateProc
|
||||
ReallocateProc :: glfw.ReallocateProc
|
||||
DeallocateProc :: glfw.DeallocateProc
|
||||
|
||||
Vendored
+7
@@ -8,6 +8,10 @@ Terminate :: glfw.Terminate
|
||||
|
||||
InitHint :: glfw.InitHint
|
||||
|
||||
InitAllocator :: glfw.InitAllocator
|
||||
|
||||
InitVulkanLoader :: glfw.InitVulkanLoader
|
||||
|
||||
GetVersion :: proc "c" () -> (major, minor, rev: c.int) {
|
||||
glfw.GetVersion(&major, &minor, &rev)
|
||||
return
|
||||
@@ -121,6 +125,7 @@ GetKeyName :: proc "c" (key, scancode: c.int) -> string {
|
||||
return string(glfw.GetKeyName(key, scancode))
|
||||
}
|
||||
SetWindowShouldClose :: glfw.SetWindowShouldClose
|
||||
GetWindowTitle :: glfw.GetWindowTitle
|
||||
JoystickPresent :: glfw.JoystickPresent
|
||||
GetJoystickName :: proc "c" (joy: c.int) -> string {
|
||||
return string(glfw.GetJoystickName(joy))
|
||||
@@ -237,6 +242,8 @@ SetJoystickCallback :: glfw.SetJoystickCallback
|
||||
|
||||
SetErrorCallback :: glfw.SetErrorCallback
|
||||
|
||||
GetPlatform :: glfw.GetPlatform
|
||||
PlatformSupported :: glfw.PlatformSupported
|
||||
|
||||
// Used by vendor:OpenGL
|
||||
gl_set_proc_address :: proc(p: rawptr, name: cstring) {
|
||||
|
||||
Vendored
+2
-2
@@ -159,7 +159,7 @@ Vector2Transform :: proc "c" (v: Vector2, m: Matrix) -> Vector2 {
|
||||
// Calculate linear interpolation between two vectors
|
||||
@(require_results, deprecated="Prefer = linalg.lerp(v1, v2, amount)")
|
||||
Vector2Lerp :: proc "c" (v1, v2: Vector2, amount: f32) -> Vector2 {
|
||||
return linalg.lerp(v1, v2, amount)
|
||||
return linalg.lerp(v1, v2, Vector2(amount))
|
||||
}
|
||||
// Calculate reflected vector to normal
|
||||
@(require_results, deprecated="Prefer = linalg.reflect(v, normal)")
|
||||
@@ -405,7 +405,7 @@ Vector3Transform :: proc "c" (v: Vector3, m: Matrix) -> Vector3 {
|
||||
// Calculate linear interpolation between two vectors
|
||||
@(require_results, deprecated="Prefer = linalg.lerp(v1, v2, amount)")
|
||||
Vector3Lerp :: proc "c" (v1, v2: Vector3, amount: f32) -> Vector3 {
|
||||
return linalg.lerp(v1, v2, amount)
|
||||
return linalg.lerp(v1, v2, Vector3(amount))
|
||||
}
|
||||
// Calculate reflected vector to normal
|
||||
@(require_results, deprecated="Prefer = linalg.reflect(v, normal)")
|
||||
|
||||
Reference in New Issue
Block a user