Merge remote-tracking branch 'offical/master'

This commit is contained in:
2024-03-25 18:44:26 -04:00
55 changed files with 1576 additions and 457 deletions
+14 -14
View File
@@ -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
+9 -9
View File
@@ -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.
+9 -6
View File
@@ -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
}
+5 -3
View File
@@ -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)
}
}
+22 -8
View File
@@ -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)
}
-1
View File
@@ -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
View File
@@ -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:]
}
}
-2
View File
@@ -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})
+1 -1
View File
@@ -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 {
+3 -1
View File
@@ -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
View File
@@ -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,
}
+10 -8
View File
@@ -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,
+1 -1
View File
@@ -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}
}
+77
View File
@@ -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}
+272
View File
@@ -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}
}
+95
View File
@@ -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
}
+72
View File
@@ -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,
}
+122
View File
@@ -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
View File
@@ -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{
+2
View File
@@ -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
View File
@@ -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];
+5
View File
@@ -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");
+1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
+5
View File
@@ -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
+178
View File
@@ -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
View File
@@ -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))
}
}
+22 -7
View File
@@ -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 ---
}
+11
View File
@@ -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)
+71 -26
View File
@@ -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
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+3 -11
View File
@@ -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:
+1 -17
View File
@@ -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 ---
+6
View File
@@ -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
+7
View File
@@ -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) {
+2 -2
View File
@@ -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)")