core_builtin.odin 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127
  1. package runtime
  2. import "base:intrinsics"
  3. @builtin
  4. Maybe :: union($T: typeid) {T}
  5. /*
  6. Represents an Objective-C block with a given procedure signature T
  7. */
  8. @builtin
  9. Objc_Block :: struct($T: typeid) where intrinsics.type_is_proc(T) { using _: intrinsics.objc_object }
  10. /*
  11. Recovers the containing/parent struct from a pointer to one of its fields.
  12. Works by "walking back" to the struct's starting address using the offset between the field and the struct.
  13. Inputs:
  14. - ptr: Pointer to the field of a container struct
  15. - T: The type of the container struct
  16. - field_name: The name of the field in the `T` struct
  17. Returns:
  18. - A pointer to the container struct based on a pointer to a field in it
  19. Example:
  20. package container_of
  21. import "base:runtime"
  22. Node :: struct {
  23. value: int,
  24. prev: ^Node,
  25. next: ^Node,
  26. }
  27. main :: proc() {
  28. node: Node
  29. field_ptr := &node.next
  30. container_struct_ptr: ^Node = runtime.container_of(field_ptr, Node, "next")
  31. assert(container_struct_ptr == &node)
  32. assert(uintptr(field_ptr) - uintptr(container_struct_ptr) == size_of(node.value) + size_of(node.prev))
  33. }
  34. Output:
  35. ^Node
  36. */
  37. @(builtin, require_results)
  38. container_of :: #force_inline proc "contextless" (ptr: $P/^$Field_Type, $T: typeid, $field_name: string) -> ^T
  39. where intrinsics.type_has_field(T, field_name),
  40. intrinsics.type_field_type(T, field_name) == Field_Type {
  41. offset :: offset_of_by_string(T, field_name)
  42. return (^T)(uintptr(ptr) - offset) if ptr != nil else nil
  43. }
  44. when !NO_DEFAULT_TEMP_ALLOCATOR {
  45. when ODIN_ARCH == .i386 && ODIN_OS == .Windows {
  46. // Thread-local storage is problematic on Windows i386
  47. global_default_temp_allocator_data: Default_Temp_Allocator
  48. } else {
  49. @thread_local global_default_temp_allocator_data: Default_Temp_Allocator
  50. }
  51. }
  52. @(builtin, disabled=NO_DEFAULT_TEMP_ALLOCATOR)
  53. init_global_temporary_allocator :: proc(size: int, backup_allocator := context.allocator) {
  54. when !NO_DEFAULT_TEMP_ALLOCATOR {
  55. default_temp_allocator_init(&global_default_temp_allocator_data, size, backup_allocator)
  56. }
  57. }
  58. // `copy_slice` is a built-in procedure that copies elements from a source slice `src` to a destination slice `dst`.
  59. // The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
  60. // of len(src) and len(dst).
  61. //
  62. // Prefer the procedure group `copy`.
  63. @builtin
  64. copy_slice :: proc "contextless" (dst, src: $T/[]$E) -> int {
  65. n := min(len(dst), len(src))
  66. if n > 0 {
  67. intrinsics.mem_copy(raw_data(dst), raw_data(src), n*size_of(E))
  68. }
  69. return n
  70. }
  71. // `copy_from_string` is a built-in procedure that copies elements from a source string `src` to a destination slice `dst`.
  72. // The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
  73. // of len(src) and len(dst).
  74. //
  75. // Prefer the procedure group `copy`.
  76. @builtin
  77. copy_from_string :: proc "contextless" (dst: $T/[]$E/u8, src: $S/string) -> int {
  78. n := min(len(dst), len(src))
  79. if n > 0 {
  80. intrinsics.mem_copy(raw_data(dst), raw_data(src), n)
  81. }
  82. return n
  83. }
  84. // `copy_from_string16` is a built-in procedure that copies elements from a source string `src` to a destination slice `dst`.
  85. // The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
  86. // of len(src) and len(dst).
  87. //
  88. // Prefer the procedure group `copy`.
  89. @builtin
  90. copy_from_string16 :: proc "contextless" (dst: $T/[]$E/u16, src: $S/string16) -> int {
  91. n := min(len(dst), len(src))
  92. if n > 0 {
  93. intrinsics.mem_copy(raw_data(dst), raw_data(src), n*size_of(u16))
  94. }
  95. return n
  96. }
  97. // `copy` is a built-in procedure that copies elements from a source slice/string `src` to a destination slice `dst`.
  98. // The source and destination may overlap. Copy returns the number of elements copied, which will be the minimum
  99. // of len(src) and len(dst).
  100. @builtin
  101. copy :: proc{copy_slice, copy_from_string, copy_from_string16}
  102. // `unordered_remove` removed the element at the specified `index`. It does so by replacing the current end value
  103. // with the old value, and reducing the length of the dynamic array by 1.
  104. //
  105. // Note: This is an O(1) operation.
  106. // Note: If you want the elements to remain in their order, use `ordered_remove`.
  107. // Note: If the index is out of bounds, this procedure will panic.
  108. @builtin
  109. unordered_remove :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #caller_location) #no_bounds_check {
  110. bounds_check_error_loc(loc, index, len(array))
  111. n := len(array)-1
  112. if index != n {
  113. array[index] = array[n]
  114. }
  115. (^Raw_Dynamic_Array)(array).len -= 1
  116. }
  117. // `ordered_remove` removed the element at the specified `index` whilst keeping the order of the other elements.
  118. //
  119. // Note: This is an O(N) operation.
  120. // Note: If the elements do not have to remain in their order, prefer `unordered_remove`.
  121. // Note: If the index is out of bounds, this procedure will panic.
  122. @builtin
  123. ordered_remove :: proc(array: ^$D/[dynamic]$T, #any_int index: int, loc := #caller_location) #no_bounds_check {
  124. bounds_check_error_loc(loc, index, len(array))
  125. if index+1 < len(array) {
  126. copy(array[index:], array[index+1:])
  127. }
  128. (^Raw_Dynamic_Array)(array).len -= 1
  129. }
  130. // `remove_range` removes a range of elements specified by the range `lo` and `hi`, whilst keeping the order of the other elements.
  131. //
  132. // Note: This is an O(N) operation.
  133. // Note: If the range is out of bounds, this procedure will panic.
  134. @builtin
  135. remove_range :: proc(array: ^$D/[dynamic]$T, #any_int lo, hi: int, loc := #caller_location) #no_bounds_check {
  136. slice_expr_error_lo_hi_loc(loc, lo, hi, len(array))
  137. n := max(hi-lo, 0)
  138. if n > 0 {
  139. if hi != len(array) {
  140. copy(array[lo:], array[hi:])
  141. }
  142. (^Raw_Dynamic_Array)(array).len -= n
  143. }
  144. }
  145. // `pop` will remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
  146. //
  147. // Note: If the dynamic array has no elements (`len(array) == 0`), this procedure will panic.
  148. @builtin
  149. pop :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
  150. assert(len(array) > 0, loc=loc)
  151. _pop_type_erased(&res, (^Raw_Dynamic_Array)(array), size_of(E))
  152. return res
  153. }
  154. _pop_type_erased :: proc(res: rawptr, array: ^Raw_Dynamic_Array, elem_size: int, loc := #caller_location) {
  155. end := rawptr(uintptr(array.data) + uintptr(elem_size*(array.len-1)))
  156. intrinsics.mem_copy_non_overlapping(res, end, elem_size)
  157. array.len -= 1
  158. }
  159. // `pop_safe` trys to remove and return the end value of dynamic array `array` and reduces the length of `array` by 1.
  160. // If the operation is not possible, it will return false.
  161. @builtin
  162. pop_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
  163. if len(array) == 0 {
  164. return
  165. }
  166. res, ok = array[len(array)-1], true
  167. (^Raw_Dynamic_Array)(array).len -= 1
  168. return
  169. }
  170. // `pop_front` will remove and return the first value of dynamic array `array` and reduces the length of `array` by 1.
  171. //
  172. // Note: If the dynamic array as no elements (`len(array) == 0`), this procedure will panic.
  173. @builtin
  174. pop_front :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (res: E) #no_bounds_check {
  175. assert(len(array) > 0, loc=loc)
  176. res = array[0]
  177. if len(array) > 1 {
  178. copy(array[0:], array[1:])
  179. }
  180. (^Raw_Dynamic_Array)(array).len -= 1
  181. return res
  182. }
  183. // `pop_front_safe` trys to return and remove the first value of dynamic array `array` and reduces the length of `array` by 1.
  184. // If the operation is not possible, it will return false.
  185. @builtin
  186. pop_front_safe :: proc "contextless" (array: ^$T/[dynamic]$E) -> (res: E, ok: bool) #no_bounds_check {
  187. if len(array) == 0 {
  188. return
  189. }
  190. res, ok = array[0], true
  191. if len(array) > 1 {
  192. copy(array[0:], array[1:])
  193. }
  194. (^Raw_Dynamic_Array)(array).len -= 1
  195. return
  196. }
  197. // `clear` will set the length of a passed dynamic array or map to `0`
  198. @builtin
  199. clear :: proc{
  200. clear_dynamic_array,
  201. clear_map,
  202. clear_soa_dynamic_array,
  203. }
  204. // `reserve` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
  205. @builtin
  206. reserve :: proc{
  207. reserve_dynamic_array,
  208. reserve_map,
  209. reserve_soa,
  210. }
  211. @builtin
  212. non_zero_reserve :: proc{
  213. non_zero_reserve_dynamic_array,
  214. non_zero_reserve_soa,
  215. }
  216. // `resize` will try to resize memory of a passed dynamic array to the requested element count (setting the `len`, and possibly `cap`).
  217. @builtin
  218. resize :: proc{
  219. resize_dynamic_array,
  220. resize_soa,
  221. }
  222. @builtin
  223. non_zero_resize :: proc{
  224. non_zero_resize_dynamic_array,
  225. non_zero_resize_soa,
  226. }
  227. // Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity.
  228. @builtin
  229. shrink :: proc{shrink_dynamic_array, shrink_map}
  230. // `free` will try to free the passed pointer, with the given `allocator` if the allocator supports this operation.
  231. @builtin
  232. free :: proc{mem_free}
  233. // `free_all` will try to free/reset all of the memory of the given `allocator` if the allocator supports this operation.
  234. @builtin
  235. free_all :: proc{mem_free_all}
  236. // `delete_string` will try to free the underlying data of the passed string, with the given `allocator` if the allocator supports this operation.
  237. //
  238. // Note: Prefer the procedure group `delete`.
  239. @builtin
  240. delete_string :: proc(str: string, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
  241. return mem_free_with_size(raw_data(str), len(str), allocator, loc)
  242. }
  243. // `delete_cstring` will try to free the underlying data of the passed string, with the given `allocator` if the allocator supports this operation.
  244. //
  245. // Note: Prefer the procedure group `delete`.
  246. @builtin
  247. delete_cstring :: proc(str: cstring, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
  248. return mem_free((^byte)(str), allocator, loc)
  249. }
  250. // `delete_dynamic_array` will try to free the underlying data of the passed dynamic array, with the given `allocator` if the allocator supports this operation.
  251. //
  252. // Note: Prefer the procedure group `delete`.
  253. @builtin
  254. delete_dynamic_array :: proc(array: $T/[dynamic]$E, loc := #caller_location) -> Allocator_Error {
  255. return mem_free_with_size(raw_data(array), cap(array)*size_of(E), array.allocator, loc)
  256. }
  257. // `delete_slice` will try to free the underlying data of the passed sliced, with the given `allocator` if the allocator supports this operation.
  258. //
  259. // Note: Prefer the procedure group `delete`.
  260. @builtin
  261. delete_slice :: proc(array: $T/[]$E, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
  262. return mem_free_with_size(raw_data(array), len(array)*size_of(E), allocator, loc)
  263. }
  264. // `delete_map` will try to free the underlying data of the passed map, with the given `allocator` if the allocator supports this operation.
  265. //
  266. // Note: Prefer the procedure group `delete`.
  267. @builtin
  268. delete_map :: proc(m: $T/map[$K]$V, loc := #caller_location) -> Allocator_Error {
  269. return map_free_dynamic(transmute(Raw_Map)m, map_info(T), loc)
  270. }
  271. @builtin
  272. delete_string16 :: proc(str: string16, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
  273. return mem_free_with_size(raw_data(str), len(str)*size_of(u16), allocator, loc)
  274. }
  275. @builtin
  276. delete_cstring16 :: proc(str: cstring16, allocator := context.allocator, loc := #caller_location) -> Allocator_Error {
  277. return mem_free((^u16)(str), allocator, loc)
  278. }
  279. // `delete` will try to free the underlying data of the passed built-in data structure (string, cstring, dynamic array, slice, or map), with the given `allocator` if the allocator supports this operation.
  280. //
  281. // Note: Prefer `delete` over the specific `delete_*` procedures where possible.
  282. @builtin
  283. delete :: proc{
  284. delete_string,
  285. delete_cstring,
  286. delete_dynamic_array,
  287. delete_slice,
  288. delete_map,
  289. delete_soa_slice,
  290. delete_soa_dynamic_array,
  291. delete_string16,
  292. delete_cstring16,
  293. }
  294. // The new built-in procedure allocates memory. The first argument is a type, not a value, and the value
  295. // return is a pointer to a newly allocated value of that type using the specified allocator, default is context.allocator
  296. @(builtin, require_results)
  297. new :: proc($T: typeid, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) #optional_allocator_error {
  298. t = (^T)(raw_data(mem_alloc_bytes(size_of(T), align_of(T), allocator, loc) or_return))
  299. return
  300. }
  301. @(require_results)
  302. new_aligned :: proc($T: typeid, alignment: int, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) {
  303. t = (^T)(raw_data(mem_alloc_bytes(size_of(T), alignment, allocator, loc) or_return))
  304. return
  305. }
  306. @(builtin, require_results)
  307. new_clone :: proc(data: $T, allocator := context.allocator, loc := #caller_location) -> (t: ^T, err: Allocator_Error) #optional_allocator_error {
  308. t = (^T)(raw_data(mem_alloc_bytes(size_of(T), align_of(T), allocator, loc) or_return))
  309. if t != nil {
  310. t^ = data
  311. }
  312. return
  313. }
  314. DEFAULT_DYNAMIC_ARRAY_CAPACITY :: 8
  315. @(require_results)
  316. make_aligned :: proc($T: typeid/[]$E, #any_int len: int, alignment: int, allocator := context.allocator, loc := #caller_location) -> (res: T, err: Allocator_Error) #optional_allocator_error {
  317. err = _make_aligned_type_erased(&res, size_of(E), len, alignment, allocator, loc)
  318. return
  319. }
  320. @(require_results)
  321. _make_aligned_type_erased :: proc(slice: rawptr, elem_size: int, len: int, alignment: int, allocator: Allocator, loc := #caller_location) -> Allocator_Error {
  322. make_slice_error_loc(loc, len)
  323. data, err := mem_alloc_bytes(elem_size*len, alignment, allocator, loc)
  324. if data == nil && elem_size != 0 {
  325. return err
  326. }
  327. (^Raw_Slice)(slice).data = raw_data(data)
  328. (^Raw_Slice)(slice).len = len
  329. return err
  330. }
  331. // `make_slice` allocates and initializes a slice. Like `new`, the first argument is a type, not a value.
  332. // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
  333. //
  334. // Note: Prefer using the procedure group `make`.
  335. @(builtin, require_results)
  336. make_slice :: proc($T: typeid/[]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (res: T, err: Allocator_Error) #optional_allocator_error {
  337. err = _make_aligned_type_erased(&res, size_of(E), len, align_of(E), allocator, loc)
  338. return
  339. }
  340. // `make_dynamic_array` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
  341. // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
  342. //
  343. // Note: Prefer using the procedure group `make`.
  344. @(builtin, require_results)
  345. make_dynamic_array :: proc($T: typeid/[dynamic]$E, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
  346. err = _make_dynamic_array_len_cap((^Raw_Dynamic_Array)(&array), size_of(E), align_of(E), 0, 0, allocator, loc)
  347. return
  348. }
  349. // `make_dynamic_array_len` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
  350. // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
  351. //
  352. // Note: Prefer using the procedure group `make`.
  353. @(builtin, require_results)
  354. make_dynamic_array_len :: proc($T: typeid/[dynamic]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
  355. err = _make_dynamic_array_len_cap((^Raw_Dynamic_Array)(&array), size_of(E), align_of(E), len, len, allocator, loc)
  356. return
  357. }
  358. // `make_dynamic_array_len_cap` allocates and initializes a dynamic array. Like `new`, the first argument is a type, not a value.
  359. // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
  360. //
  361. // Note: Prefer using the procedure group `make`.
  362. @(builtin, require_results)
  363. make_dynamic_array_len_cap :: proc($T: typeid/[dynamic]$E, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (array: T, err: Allocator_Error) #optional_allocator_error {
  364. err = _make_dynamic_array_len_cap((^Raw_Dynamic_Array)(&array), size_of(E), align_of(E), len, cap, allocator, loc)
  365. return
  366. }
  367. @(require_results)
  368. _make_dynamic_array_len_cap :: proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, #any_int len: int, #any_int cap: int, allocator := context.allocator, loc := #caller_location) -> (err: Allocator_Error) {
  369. make_dynamic_array_error_loc(loc, len, cap)
  370. array.allocator = allocator // initialize allocator before just in case it fails to allocate any memory
  371. data := mem_alloc_bytes(size_of_elem*cap, align_of_elem, allocator, loc) or_return
  372. use_zero := data == nil && size_of_elem != 0
  373. array.data = raw_data(data)
  374. array.len = 0 if use_zero else len
  375. array.cap = 0 if use_zero else cap
  376. array.allocator = allocator
  377. return
  378. }
  379. // `make_map` initializes a map with an allocator. Like `new`, the first argument is a type, not a value.
  380. // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
  381. //
  382. // Note: Prefer using the procedure group `make`.
  383. @(builtin, require_results)
  384. make_map :: proc($T: typeid/map[$K]$E, allocator := context.allocator, loc := #caller_location) -> (m: T) {
  385. m.allocator = allocator
  386. return m
  387. }
  388. // `make_map_cap` initializes a map with an allocator and allocates space using `capacity`.
  389. // Like `new`, the first argument is a type, not a value.
  390. // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
  391. //
  392. // Note: Prefer using the procedure group `make`.
  393. @(builtin, require_results)
  394. make_map_cap :: proc($T: typeid/map[$K]$E, #any_int capacity: int, allocator := context.allocator, loc := #caller_location) -> (m: T, err: Allocator_Error) #optional_allocator_error {
  395. make_map_expr_error_loc(loc, capacity)
  396. context.allocator = allocator
  397. err = reserve_map(&m, capacity, loc)
  398. return
  399. }
  400. // `make_multi_pointer` allocates and initializes a multi-pointer. Like `new`, the first argument is a type, not a value.
  401. // Unlike `new`, `make`'s return value is the same as the type of its argument, not a pointer to it.
  402. //
  403. // This is "similar" to doing `raw_data(make([]E, len, allocator))`.
  404. //
  405. // Note: Prefer using the procedure group `make`.
  406. @(builtin, require_results)
  407. make_multi_pointer :: proc($T: typeid/[^]$E, #any_int len: int, allocator := context.allocator, loc := #caller_location) -> (mp: T, err: Allocator_Error) #optional_allocator_error {
  408. make_slice_error_loc(loc, len)
  409. data := mem_alloc_bytes(size_of(E)*len, align_of(E), allocator, loc) or_return
  410. if data == nil && size_of(E) != 0 {
  411. return
  412. }
  413. mp = cast(T)raw_data(data)
  414. return
  415. }
  416. // `make` built-in procedure allocates and initializes a value of type slice, dynamic array, map, or multi-pointer (only).
  417. //
  418. // Similar to `new`, the first argument is a type, not a value. Unlike new, make's return type is the same as the
  419. // type of its argument, not a pointer to it.
  420. // Make uses the specified allocator, default is context.allocator.
  421. @builtin
  422. make :: proc{
  423. make_slice,
  424. make_dynamic_array,
  425. make_dynamic_array_len,
  426. make_dynamic_array_len_cap,
  427. make_map,
  428. make_map_cap,
  429. make_multi_pointer,
  430. make_soa_slice,
  431. make_soa_dynamic_array,
  432. make_soa_dynamic_array_len,
  433. make_soa_dynamic_array_len_cap,
  434. }
  435. // `clear_map` will set the length of a passed map to `0`
  436. //
  437. // Note: Prefer the procedure group `clear`
  438. @builtin
  439. clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
  440. if m == nil {
  441. return
  442. }
  443. map_clear_dynamic((^Raw_Map)(m), map_info(T))
  444. }
  445. // `reserve_map` will try to reserve memory of a passed map to the requested element count (setting the `cap`).
  446. //
  447. // Note: Prefer the procedure group `reserve`
  448. @builtin
  449. reserve_map :: proc(m: ^$T/map[$K]$V, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
  450. return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc)
  451. }
  452. // Shrinks the capacity of a map down to the current length.
  453. //
  454. // Note: Prefer the procedure group `shrink`
  455. @builtin
  456. shrink_map :: proc(m: ^$T/map[$K]$V, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
  457. if m != nil {
  458. return map_shrink_dynamic((^Raw_Map)(m), map_info(T), loc)
  459. }
  460. return
  461. }
  462. // The delete_key built-in procedure deletes the element with the specified key (m[key]) from the map.
  463. // If m is nil, or there is no such element, this procedure is a no-op
  464. @builtin
  465. delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value: V) {
  466. if m != nil {
  467. key := key
  468. old_k, old_v, ok := map_erase_dynamic((^Raw_Map)(m), map_info(T), uintptr(&key))
  469. if ok {
  470. deleted_key = (^K)(old_k)^
  471. deleted_value = (^V)(old_v)^
  472. }
  473. }
  474. return
  475. }
  476. _append_elem :: #force_no_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, arg_ptr: rawptr, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
  477. if array == nil {
  478. return
  479. }
  480. if array.cap < array.len+1 {
  481. // Same behavior as _append_elems but there's only one arg, so we always just add DEFAULT_DYNAMIC_ARRAY_CAPACITY.
  482. cap := 2 * array.cap + DEFAULT_DYNAMIC_ARRAY_CAPACITY
  483. // do not 'or_return' here as it could be a partial success
  484. err = _reserve_dynamic_array(array, size_of_elem, align_of_elem, cap, should_zero, loc)
  485. }
  486. if array.cap-array.len > 0 {
  487. data := ([^]byte)(array.data)
  488. assert(data != nil, loc=loc)
  489. data = data[array.len*size_of_elem:]
  490. intrinsics.mem_copy_non_overlapping(data, arg_ptr, size_of_elem)
  491. array.len += 1
  492. n = 1
  493. }
  494. return
  495. }
  496. @builtin
  497. append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
  498. when size_of(E) == 0 {
  499. (^Raw_Dynamic_Array)(array).len += 1
  500. return 1, nil
  501. } else {
  502. arg := arg
  503. return _append_elem((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), &arg, true, loc=loc)
  504. }
  505. }
  506. @builtin
  507. non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, #no_broadcast arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
  508. when size_of(E) == 0 {
  509. (^Raw_Dynamic_Array)(array).len += 1
  510. return 1, nil
  511. } else {
  512. arg := arg
  513. return _append_elem((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), &arg, false, loc=loc)
  514. }
  515. }
  516. _append_elems :: #force_no_inline proc(array: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, should_zero: bool, loc := #caller_location, args: rawptr, arg_len: int) -> (n: int, err: Allocator_Error) #optional_allocator_error {
  517. if array == nil {
  518. return 0, nil
  519. }
  520. if arg_len <= 0 {
  521. return 0, nil
  522. }
  523. if array.cap < array.len+arg_len {
  524. cap := 2 * array.cap + max(DEFAULT_DYNAMIC_ARRAY_CAPACITY, arg_len)
  525. // do not 'or_return' here as it could be a partial success
  526. err = _reserve_dynamic_array(array, size_of_elem, align_of_elem, cap, should_zero, loc)
  527. }
  528. arg_len := arg_len
  529. arg_len = min(array.cap-array.len, arg_len)
  530. if arg_len > 0 {
  531. data := ([^]byte)(array.data)
  532. assert(data != nil, loc=loc)
  533. data = data[array.len*size_of_elem:]
  534. intrinsics.mem_copy(data, args, size_of_elem * arg_len) // must be mem_copy (overlapping)
  535. array.len += arg_len
  536. }
  537. return arg_len, err
  538. }
  539. @builtin
  540. append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
  541. when size_of(E) == 0 {
  542. a := (^Raw_Dynamic_Array)(array)
  543. a.len += len(args)
  544. return len(args), nil
  545. } else {
  546. return _append_elems((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), true, loc, raw_data(args), len(args))
  547. }
  548. }
  549. @builtin
  550. non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, #no_broadcast args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
  551. when size_of(E) == 0 {
  552. a := (^Raw_Dynamic_Array)(array)
  553. a.len += len(args)
  554. return len(args), nil
  555. } else {
  556. return _append_elems((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), false, loc, raw_data(args), len(args))
  557. }
  558. }
  559. // The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
  560. _append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
  561. return _append_elems((^Raw_Dynamic_Array)(array), 1, 1, should_zero, loc, raw_data(arg), len(arg))
  562. }
  563. @builtin
  564. append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
  565. return _append_elem_string(array, arg, true, loc)
  566. }
  567. @builtin
  568. non_zero_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
  569. return _append_elem_string(array, arg, false, loc)
  570. }
  571. // The append_string built-in procedure appends multiple strings to the end of a [dynamic]u8 like type
  572. @builtin
  573. append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
  574. n_arg: int
  575. for arg in args {
  576. n_arg, err = append(array, ..transmute([]E)(arg), loc=loc)
  577. n += n_arg
  578. if err != nil {
  579. return
  580. }
  581. }
  582. return
  583. }
  584. // The append built-in procedure appends elements to the end of a dynamic array
  585. @builtin append :: proc{
  586. append_elem,
  587. append_elems,
  588. append_elem_string,
  589. append_soa_elem,
  590. append_soa_elems,
  591. }
  592. @builtin non_zero_append :: proc{
  593. non_zero_append_elem,
  594. non_zero_append_elems,
  595. non_zero_append_elem_string,
  596. non_zero_append_soa_elem,
  597. non_zero_append_soa_elems,
  598. }
  599. @builtin
  600. append_nothing :: proc(array: ^$T/[dynamic]$E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
  601. if array == nil {
  602. return 0, nil
  603. }
  604. prev_len := len(array)
  605. resize(array, len(array)+1, loc) or_return
  606. return len(array)-prev_len, nil
  607. }
  608. @builtin
  609. inject_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
  610. when !ODIN_NO_BOUNDS_CHECK {
  611. ensure(index >= 0, "Index must be positive.", loc)
  612. }
  613. if array == nil {
  614. return
  615. }
  616. n := max(len(array), index)
  617. m :: 1
  618. new_size := n + m
  619. resize(array, new_size, loc) or_return
  620. when size_of(E) != 0 {
  621. copy(array[index + m:], array[index:])
  622. array[index] = arg
  623. }
  624. ok = true
  625. return
  626. }
  627. @builtin
  628. inject_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
  629. when !ODIN_NO_BOUNDS_CHECK {
  630. ensure(index >= 0, "Index must be positive.", loc)
  631. }
  632. if array == nil {
  633. return
  634. }
  635. if len(args) == 0 {
  636. ok = true
  637. return
  638. }
  639. n := max(len(array), index)
  640. m := len(args)
  641. new_size := n + m
  642. resize(array, new_size, loc) or_return
  643. when size_of(E) != 0 {
  644. copy(array[index + m:], array[index:])
  645. copy(array[index:], args)
  646. }
  647. ok = true
  648. return
  649. }
  650. @builtin
  651. inject_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
  652. when !ODIN_NO_BOUNDS_CHECK {
  653. ensure(index >= 0, "Index must be positive.", loc)
  654. }
  655. if array == nil {
  656. return
  657. }
  658. if len(arg) == 0 {
  659. ok = true
  660. return
  661. }
  662. n := max(len(array), index)
  663. m := len(arg)
  664. new_size := n + m
  665. resize(array, new_size, loc) or_return
  666. copy(array[index+m:], array[index:])
  667. copy(array[index:], arg)
  668. ok = true
  669. return
  670. }
  671. @builtin inject_at :: proc{inject_at_elem, inject_at_elems, inject_at_elem_string}
  672. @builtin
  673. assign_at_elem :: proc(array: ^$T/[dynamic]$E, #any_int index: int, arg: E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
  674. if index < len(array) {
  675. array[index] = arg
  676. ok = true
  677. } else {
  678. resize(array, index+1, loc) or_return
  679. array[index] = arg
  680. ok = true
  681. }
  682. return
  683. }
  684. @builtin
  685. assign_at_elems :: proc(array: ^$T/[dynamic]$E, #any_int index: int, #no_broadcast args: ..E, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
  686. new_size := index + len(args)
  687. if len(args) == 0 {
  688. ok = true
  689. } else if new_size < len(array) {
  690. copy(array[index:], args)
  691. ok = true
  692. } else {
  693. resize(array, new_size, loc) or_return
  694. copy(array[index:], args)
  695. ok = true
  696. }
  697. return
  698. }
  699. @builtin
  700. assign_at_elem_string :: proc(array: ^$T/[dynamic]$E/u8, #any_int index: int, arg: string, loc := #caller_location) -> (ok: bool, err: Allocator_Error) #no_bounds_check #optional_allocator_error {
  701. new_size := index + len(arg)
  702. if len(arg) == 0 {
  703. ok = true
  704. } else if new_size < len(array) {
  705. copy(array[index:], arg)
  706. ok = true
  707. } else {
  708. resize(array, new_size, loc) or_return
  709. copy(array[index:], arg)
  710. ok = true
  711. }
  712. return
  713. }
  714. @builtin assign_at :: proc{assign_at_elem, assign_at_elems, assign_at_elem_string}
  715. // `clear_dynamic_array` will set the length of a passed dynamic array to `0`
  716. //
  717. // Note: Prefer the procedure group `clear`.
  718. @builtin
  719. clear_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) {
  720. if array != nil {
  721. (^Raw_Dynamic_Array)(array).len = 0
  722. }
  723. }
  724. // `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
  725. //
  726. // Note: Prefer the procedure group `reserve`.
  727. _reserve_dynamic_array :: #force_no_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
  728. if a == nil {
  729. return nil
  730. }
  731. if capacity <= a.cap {
  732. return nil
  733. }
  734. if a.allocator.procedure == nil {
  735. a.allocator = context.allocator
  736. }
  737. assert(a.allocator.procedure != nil)
  738. old_size := a.cap * size_of_elem
  739. new_size := capacity * size_of_elem
  740. allocator := a.allocator
  741. new_data: []byte
  742. if should_zero {
  743. new_data = mem_resize(a.data, old_size, new_size, align_of_elem, allocator, loc) or_return
  744. } else {
  745. new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of_elem, allocator, loc) or_return
  746. }
  747. if new_data == nil && new_size > 0 {
  748. return .Out_Of_Memory
  749. }
  750. a.data = raw_data(new_data)
  751. a.cap = capacity
  752. return nil
  753. }
  754. @builtin
  755. reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
  756. return _reserve_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), capacity, true, loc)
  757. }
  758. @builtin
  759. non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
  760. return _reserve_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), capacity, false, loc)
  761. }
  762. _resize_dynamic_array :: #force_no_inline proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, length: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
  763. if a == nil {
  764. return nil
  765. }
  766. if should_zero && a.len < length {
  767. num_reused := min(a.cap, length) - a.len
  768. intrinsics.mem_zero(([^]byte)(a.data)[a.len*size_of_elem:], num_reused*size_of_elem)
  769. }
  770. if length <= a.cap {
  771. a.len = max(length, 0)
  772. return nil
  773. }
  774. if a.allocator.procedure == nil {
  775. a.allocator = context.allocator
  776. }
  777. assert(a.allocator.procedure != nil)
  778. old_size := a.cap * size_of_elem
  779. new_size := length * size_of_elem
  780. allocator := a.allocator
  781. new_data : []byte
  782. if should_zero {
  783. new_data = mem_resize(a.data, old_size, new_size, align_of_elem, allocator, loc) or_return
  784. } else {
  785. new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of_elem, allocator, loc) or_return
  786. }
  787. if new_data == nil && new_size > 0 {
  788. return .Out_Of_Memory
  789. }
  790. a.data = raw_data(new_data)
  791. a.len = length
  792. a.cap = length
  793. return nil
  794. }
  795. // `resize_dynamic_array` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
  796. //
  797. // Note: Prefer the procedure group `resize`
  798. @builtin
  799. resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
  800. return _resize_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), length, true, loc=loc)
  801. }
  802. @builtin
  803. non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
  804. return _resize_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), length, false, loc=loc)
  805. }
  806. /*
  807. Shrinks the capacity of a dynamic array down to the current length, or the given capacity.
  808. If `new_cap` is negative, then `len(array)` is used.
  809. Returns false if `cap(array) < new_cap`, or the allocator report failure.
  810. If `len(array) < new_cap`, then `len(array)` will be left unchanged.
  811. Note: Prefer the procedure group `shrink`
  812. */
  813. shrink_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
  814. return _shrink_dynamic_array((^Raw_Dynamic_Array)(array), size_of(E), align_of(E), new_cap, loc)
  815. }
  816. _shrink_dynamic_array :: proc(a: ^Raw_Dynamic_Array, size_of_elem, align_of_elem: int, new_cap := -1, loc := #caller_location) -> (did_shrink: bool, err: Allocator_Error) {
  817. if a == nil {
  818. return
  819. }
  820. new_cap := new_cap if new_cap >= 0 else a.len
  821. if new_cap > a.cap {
  822. return
  823. }
  824. if a.allocator.procedure == nil {
  825. a.allocator = context.allocator
  826. }
  827. assert(a.allocator.procedure != nil)
  828. old_size := a.cap * size_of_elem
  829. new_size := new_cap * size_of_elem
  830. new_data := mem_resize(a.data, old_size, new_size, align_of_elem, a.allocator, loc) or_return
  831. a.data = raw_data(new_data)
  832. a.len = min(new_cap, a.len)
  833. a.cap = new_cap
  834. return true, nil
  835. }
  836. @builtin
  837. map_insert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (ptr: ^V) {
  838. key, value := key, value
  839. return (^V)(__dynamic_map_set_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc))
  840. }
  841. // Explicitly inserts a key and value into a map `m`, the same as `map_insert`, but the return values differ.
  842. // - `prev_key` will return the previous pointer of a key if it exists, check `found_previous` if was previously found
  843. // - `value_ptr` will return the pointer of the memory where the insertion happens, and `nil` if the map failed to resize
  844. // - `found_previous` will be true a previous key was found
  845. @(builtin, require_results)
  846. map_upsert :: proc(m: ^$T/map[$K]$V, key: K, value: V, loc := #caller_location) -> (prev_key: K, value_ptr: ^V, found_previous: bool) {
  847. key, value := key, value
  848. kp, vp := __dynamic_map_set_extra_without_hash((^Raw_Map)(m), map_info(T), rawptr(&key), rawptr(&value), loc)
  849. if kp != nil {
  850. prev_key = (^K)(kp)^
  851. found_previous = true
  852. }
  853. value_ptr = (^V)(vp)
  854. return
  855. }
  856. /*
  857. Retrieves a pointer to the key and value for a possibly just inserted entry into the map.
  858. If the `key` was not in the map `m`, an entry is inserted with the zero value and `just_inserted` will be `true`.
  859. Otherwise the existing entry is left untouched and pointers to its key and value are returned.
  860. If the map has to grow in order to insert the entry and the allocation fails, `err` is set and returned.
  861. If `err` is `nil`, `key_ptr` and `value_ptr` are valid pointers and will not be `nil`.
  862. WARN: User modification of the key pointed at by `key_ptr` should only be done if the new key is equal to (in hash) the old key.
  863. If that is not the case you will corrupt the map.
  864. */
  865. @(builtin, require_results)
  866. map_entry :: proc(m: ^$T/map[$K]$V, key: K, loc := #caller_location) -> (key_ptr: ^K, value_ptr: ^V, just_inserted: bool, err: Allocator_Error) {
  867. key := key
  868. zero: V
  869. _key_ptr, _value_ptr: rawptr
  870. _key_ptr, _value_ptr, just_inserted, err = __dynamic_map_entry((^Raw_Map)(m), map_info(T), &key, &zero, loc)
  871. key_ptr = (^K)(_key_ptr)
  872. value_ptr = (^V)(_value_ptr)
  873. return
  874. }
  875. @builtin
  876. card :: proc "contextless" (s: $S/bit_set[$E; $U]) -> int {
  877. return int(intrinsics.count_ones(transmute(intrinsics.type_bit_set_underlying_type(S))s))
  878. }
  879. @builtin
  880. @(disabled=ODIN_DISABLE_ASSERT)
  881. assert :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
  882. if !condition {
  883. // NOTE(bill): This is wrapped in a procedure call
  884. // to improve performance to make the CPU not
  885. // execute speculatively, making it about an order of
  886. // magnitude faster
  887. @(cold)
  888. internal :: proc(message: string, loc: Source_Code_Location) {
  889. p := context.assertion_failure_proc
  890. if p == nil {
  891. p = default_assertion_failure_proc
  892. }
  893. p("runtime assertion", message, loc)
  894. }
  895. internal(message, loc)
  896. }
  897. }
  898. // Evaluates the condition and aborts the program iff the condition is
  899. // false. This routine ignores `ODIN_DISABLE_ASSERT`, and will always
  900. // execute.
  901. @builtin
  902. ensure :: proc(condition: bool, message := #caller_expression(condition), loc := #caller_location) {
  903. if !condition {
  904. @(cold)
  905. internal :: proc(message: string, loc: Source_Code_Location) {
  906. p := context.assertion_failure_proc
  907. if p == nil {
  908. p = default_assertion_failure_proc
  909. }
  910. p("unsatisfied ensure", message, loc)
  911. }
  912. internal(message, loc)
  913. }
  914. }
  915. @builtin
  916. panic :: proc(message: string, loc := #caller_location) -> ! {
  917. p := context.assertion_failure_proc
  918. if p == nil {
  919. p = default_assertion_failure_proc
  920. }
  921. p("panic", message, loc)
  922. }
  923. @builtin
  924. unimplemented :: proc(message := "", loc := #caller_location) -> ! {
  925. p := context.assertion_failure_proc
  926. if p == nil {
  927. p = default_assertion_failure_proc
  928. }
  929. p("not yet implemented", message, loc)
  930. }
  931. @builtin
  932. @(disabled=ODIN_DISABLE_ASSERT)
  933. assert_contextless :: proc "contextless" (condition: bool, message := #caller_expression(condition), loc := #caller_location) {
  934. if !condition {
  935. // NOTE(bill): This is wrapped in a procedure call
  936. // to improve performance to make the CPU not
  937. // execute speculatively, making it about an order of
  938. // magnitude faster
  939. @(cold)
  940. internal :: proc "contextless" (message: string, loc: Source_Code_Location) {
  941. default_assertion_contextless_failure_proc("runtime assertion", message, loc)
  942. }
  943. internal(message, loc)
  944. }
  945. }
  946. @builtin
  947. ensure_contextless :: proc "contextless" (condition: bool, message := #caller_expression(condition), loc := #caller_location) {
  948. if !condition {
  949. @(cold)
  950. internal :: proc "contextless" (message: string, loc: Source_Code_Location) {
  951. default_assertion_contextless_failure_proc("unsatisfied ensure", message, loc)
  952. }
  953. internal(message, loc)
  954. }
  955. }
  956. @builtin
  957. panic_contextless :: proc "contextless" (message: string, loc := #caller_location) -> ! {
  958. default_assertion_contextless_failure_proc("panic", message, loc)
  959. }
  960. @builtin
  961. unimplemented_contextless :: proc "contextless" (message := "", loc := #caller_location) -> ! {
  962. default_assertion_contextless_failure_proc("not yet implemented", message, loc)
  963. }