Compare commits

...

27 Commits

Author SHA1 Message Date
Christoffer Lerno
808ce6d605 Fix deprecation warnings not silenced. 2026-02-26 00:28:36 +01:00
Christoffer Lerno
09606a2998 Update hint for deprecation 2026-02-26 00:15:58 +01:00
Christoffer Lerno
0a21323652 Fixes 2026-02-26 00:11:55 +01:00
Christoffer Lerno
c990b5aaf1 Typekind enums are changed CONST_ENUM -> CONSTDEF, DISTINCT -> TYPEDEF. 2026-02-25 23:52:13 +01:00
Christoffer Lerno
98e0985a10 Fix error message on deprecated enum = 2026-02-25 23:27:22 +01:00
Christoffer Lerno
5974bf6cd0 Make first RC 0.7.10 2026-02-25 18:11:42 +01:00
Manu Linares
992890065a vendor-fetch lists available libraries (#2976)
* vendor-fetch lists available libraries

from `github.com/c3lang/vendor` when no library is specified.

* add release notes
2026-02-24 22:55:18 +01:00
Christoffer Lerno
d4dbe27072 Add mention of LLVM libs 2026-02-24 20:00:50 +01:00
Christoffer Lerno
56a11ae3ca Fix text in error message. 2026-02-24 19:07:38 +01:00
konimarti
f1d03c194f distributions: fix tests (#2975)
Remove stochastic elements in the distributions tests and ensure they
are deterministic.

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-24 16:18:53 +01:00
Christoffer Lerno
0085fe73ad Fix test 2026-02-24 15:18:57 +01:00
Christoffer Lerno
a238b62483 Typo 2026-02-24 14:58:28 +01:00
Book-reader
58022e7cca only enable emulated TLS on android compiling natively in termux (#2974) 2026-02-24 00:10:22 +01:00
Christoffer Lerno
d2f046780d - Compiler crash using ?? with a void? macro #2973 2026-02-23 23:28:32 +01:00
Christoffer Lerno
ec65c5761e Fixed test 2026-02-23 19:21:56 +01:00
Christoffer Lerno
0ed917cdc2 Improve error when trying to use an extern const as a compile time constant. #2969 2026-02-23 19:15:30 +01:00
Christoffer Lerno
8373ab0509 Typo in releasenotes. 2026-02-23 19:15:30 +01:00
srkkov
8f7610345d More x86 instructions (#2964)
* Added most x86 cryptographic instructions

* Fixed popcnt test

* Fixed asm_ops_x64_2.c3t test

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-23 15:27:36 +01:00
Zack Puhl
8bd963ecaf Allow Async w/ Subprocess for POSIX Systems (#2953)
* add native_fflush libc passthru

* add `pipe2` which combines `pipe` and `fcntl` - see docs

* POSIX subprocess: incorporate async reader capability and and fflush for writer

* use the real-name O_NONBLOCK since it's available

* typo

* ok... remove `pipe2` and do this with an existing method......

* Update releasenotes.md

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-22 01:59:44 +01:00
Christoffer Lerno
dc52478c09 - Warn on use of visibility modifiers on methods. #2962 2026-02-21 21:10:08 +01:00
Gantsev Denis
e1ec4b1235 add more x86 instructions 2026-02-20 21:23:35 +01:00
Lucas Alves
7c68396c0d Enable default NDK detection for Android x86_64 (#2910)
* android: detect NDK for x86_64 targets

* Treat Android as POSIX in is_posix

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-20 21:07:44 +01:00
Manu Linares
eae7d0c4a1 stdlib: std::compression::zip and std::compression::deflate (#2930)
* stdlib: implement `std::compression::zip` and `std::compression::deflate`

- C3 implementation of DEFLATE (RFC 1951) and ZIP archive handling.
- Support for reading and writing archives using STORE and DEFLATE
methods.
- Decompression supports both fixed and dynamic Huffman blocks.
- Compression using greedy LZ77 matching.
- Zero dependencies on libc.
- Stream-based entry reading and writing.
- Full unit test coverage.

NOTE: This is an initial implementation. Future improvements could be:

- Optimization of the LZ77 matching (lazy matching).
- Support for dynamic Huffman blocks in compression.
- ZIP64 support for large files/archives.
- Support for encryption and additional compression methods.

* optimizations+refactoring

deflate:
- replace linear search with hash-based match finding.
- implement support for dynamic Huffman blocks using the Package-Merge
algorithm.
- add streaming decompression.
- add buffered StreamBitReader.

zip:
- add ZIP64 support.
- add CP437 and UTF-8 filename encoding detection.
- add DOS date/time conversion and timestamp preservation.
- add ZipEntryReader for streaming entry reads.
- implement ZipArchive.extract and ZipArchive.recover helpers.

other:
- Add `set_modified_time` to std::io;
- Add benchmarks and a few more unit tests.

* zip: add archive comment support

add tests

* forgot to rename the benchmark :(

* detect utf8 names on weird zips

fix method not passed to open_writer

* another edge case where directory doesn't end with /

* testing utilities

- detect encrypted zip
- `ZipArchive.open_writer` default to DEFLATE

* fix zip64 creation, add tests

* fix ZIP header endianness for big-endian compatibility

Update ZipLFH, ZipCDH, ZipEOCD, Zip64EOCD, and Zip64Locator structs to
use little-endian bitstruct types from std::core::bitorder

* fix ZipEntryReader position tracking and seek logic ZIP_METHOD_STORE

added a test to track this

* add package-merge algorithm attribution

Thanks @konimarti

* standalone deflate_benchmark.c3 against `miniz`

* fix integer overflows, leaks and improve safety

* a few safety for 32-bit systems and tests

* deflate compress optimization

* improve match finding, hash updates, and buffer usage

* use ulong for zip offsets

* style changes (#18)

* style changes

* update tests

* style changes in `deflate.c3`

* fix typo

* Allocator first. Some changes to deflate to use `copy_to`

* Fix missing conversion on 32 bits.

* Fix deflate stream. Formatting. Prefer switch over if-elseif

* - Stream functions now use long/ulong rather than isz/usz for seek/available.
- `instream.seek` is replaced by `set_cursor` and `cursor`.
- `instream.available`, `cursor` etc are long/ulong rather than isz/usz to be correct on 32-bit.

* Update to constdef

* Fix test

---------

Co-authored-by: Book-reader <thevoid@outlook.co.nz>
Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-20 20:41:34 +01:00
mmoustafa8108
5055e86518 test: added comprehensive tests for in-string & complex nested comments (#2956)
* test: added comprehensive tests for in-string & complex nested comments

* Properly handle error

---------

Co-authored-by: Christoffer Lerno <christoffer.lerno@gmail.com>
2026-02-20 13:24:08 +01:00
Manu Linares
4b13ad692a fixes for RISC-V ABI Implementation #1567 (#2937)
* fixes for RISC-V ABI Implementation #1567

Fixed RISC-V floating-point ABI by correcting the target triple to
`riscv64-unknown-linux-gnu`, adding the `target-abi` module flag, and
ensuring ABI-required CPU features are enabled.

I tested this with:
```bash
build/c3c compile-only --target linux-riscv64 rv_hello.c3
readelf -h obj/linux-riscv64/rv_hello.o | grep Flags
# Output: Flags: 0x5, RVC, double-float ABI
```

```bash
# and qemu because I don't have a riscv machine :/
qemu-riscv64-static -L /usr/riscv64-linux-gnu ./rv_hello
```

---

@lerno I purposedly left these two failing tests to clearly see the
difference.

`test/test_suite/abi/riscv64-lp64-lp64f-abi-1.c3t`
`test/test_suite/abi/riscv64-lp64-abi.c3t`

* improve Linux cross-compilation, specifically for RISC-V

- Implement automatic sysroot and CRT object discovery for RISC-V.
- Fix dynamic linker paths and emulation flags for LLD.
- Link against libgcc to resolve required arithmetic symbols.

* Update tests.

* fix linker CRT detection by centralizing host arch check

use target_host_arch() in linker.c
move target_host_arch() from hostinfo.c to target.c

* missing debug info

---------

Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
2026-02-20 01:53:29 +01:00
Christoffer Lerno
7c81bb35ca Refactored contracts (#2960)
* - Properly support `@deprecated` as contract.
- Support deprecating enum values.
- Refactor contracts

* Always copy enum constants.

* Bugfix.
2026-02-20 01:51:28 +01:00
Christoffer Lerno
5a82f672b5 Update to constdef 2026-02-20 01:13:20 +01:00
165 changed files with 8704 additions and 1931 deletions

View File

@@ -142,7 +142,7 @@ fn void main()
### Current status
The current stable version of the compiler is **version 0.7.9**.
The current stable version of the compiler is **version 0.7.10**.
The upcoming 0.7.x releases will focus on expanding the standard library,
fixing bugs and improving compile time analysis.

View File

@@ -0,0 +1,51 @@
module deflate_benchmarks;
import std::compression::deflate;
const uint SMALL_ITERATIONS = 50000;
const uint LARGE_ITERATIONS = 100;
// Data to compress
const char[] SMALL_DATA = { [0..1023] = 'A' };
const char[] LARGE_DATA = { [0..1048575] = 'B' };
char[] small_compressed;
char[] large_compressed;
fn void initialize_bench() @init
{
small_compressed = deflate::compress(mem, SMALL_DATA)!!;
large_compressed = deflate::compress(mem, LARGE_DATA)!!;
set_benchmark_warmup_iterations(2);
set_benchmark_max_iterations(10);
set_benchmark_func_iterations($qnameof(deflate_compress_small), SMALL_ITERATIONS);
set_benchmark_func_iterations($qnameof(deflate_decompress_small), SMALL_ITERATIONS);
set_benchmark_func_iterations($qnameof(deflate_compress_large), LARGE_ITERATIONS);
set_benchmark_func_iterations($qnameof(deflate_decompress_large), LARGE_ITERATIONS);
}
// =======================================================================================
module deflate_benchmarks @benchmark;
import std::compression::deflate;
import std::core::mem;
fn void deflate_compress_small() => @pool()
{
char[]? compressed = deflate::compress(tmem, SMALL_DATA);
}
fn void deflate_decompress_small() => @pool()
{
char[]? decompressed = deflate::decompress(tmem, small_compressed);
}
fn void deflate_compress_large() => @pool()
{
char[]? compressed = deflate::compress(tmem, LARGE_DATA);
}
fn void deflate_decompress_large() => @pool()
{
char[]? decompressed = deflate::decompress(tmem, large_compressed);
}

View File

@@ -32,8 +32,8 @@ fn void initialize_bench() @init
$qnameof(sha1_16)[..^4],
$qnameof(sha2_256_16)[..^4],
$qnameof(sha2_512_16)[..^4],
$qnameof(blake2s_256_16)[..^4],
$qnameof(blake2b_256_16)[..^4],
//$qnameof(blake2s_256_16)[..^4],
//$qnameof(blake2b_256_16)[..^4],
$qnameof(blake3_16)[..^4],
$qnameof(ripemd_160_16)[..^4],
$qnameof(whirlpool_16)[..^4],
@@ -68,8 +68,8 @@ fn void md5_16() => md5::hash(common_16);
fn void sha1_16() => sha1::hash(common_16);
fn void sha2_256_16() => sha256::hash(common_16);
fn void sha2_512_16() => sha512::hash(common_16);
fn void blake2s_256_16() => blake2::s(256, common_16);
fn void blake2b_256_16() => blake2::b(256, common_16);
//fn void blake2s_256_16() => blake2::s(256, common_16);
//fn void blake2b_256_16() => blake2::b(256, common_16);
fn void blake3_16() => blake3::hash(common_16);
fn void ripemd_160_16() => ripemd::hash{160}(common_16);
fn void whirlpool_16() => whirlpool::hash(common_16);
@@ -80,8 +80,8 @@ fn void md5_256() => md5::hash(common_256);
fn void sha1_256() => sha1::hash(common_256);
fn void sha2_256_256() => sha256::hash(common_256);
fn void sha2_512_256() => sha512::hash(common_256);
fn void blake2s_256_256() => blake2::s(256, common_256);
fn void blake2b_256_256() => blake2::b(256, common_256);
//fn void blake2s_256_256() => blake2::s(256, common_256);
//fn void blake2b_256_256() => blake2::b(256, common_256);
fn void blake3_256() => blake3::hash(common_256);
fn void ripemd_160_256() => ripemd::hash{160}(common_256);
fn void whirlpool_256() => whirlpool::hash(common_256);
@@ -92,8 +92,8 @@ fn void md5_4kib() => md5::hash(common_4kib);
fn void sha1_4kib() => sha1::hash(common_4kib);
fn void sha2_256_4kib() => sha256::hash(common_4kib);
fn void sha2_512_4kib() => sha512::hash(common_4kib);
fn void blake2s_256_4kib() => blake2::s(256, common_4kib);
fn void blake2b_256_4kib() => blake2::b(256, common_4kib);
//fn void blake2s_256_4kib() => blake2::s(256, common_4kib);
//fn void blake2b_256_4kib() => blake2::b(256, common_4kib);
fn void blake3_4kib() => blake3::hash(common_4kib);
fn void ripemd_160_4kib() => ripemd::hash{160}(common_4kib);
fn void whirlpool_4kib() => whirlpool::hash(common_4kib);
@@ -104,8 +104,8 @@ fn void md5_1mib() => md5::hash(common_1mib);
fn void sha1_1mib() => sha1::hash(common_1mib);
fn void sha2_256_1mib() => sha256::hash(common_1mib);
fn void sha2_512_1mib() => sha512::hash(common_1mib);
fn void blake2s_256_1mib() => blake2::s(256, common_1mib);
fn void blake2b_256_1mib() => blake2::b(256, common_1mib);
//fn void blake2s_256_1mib() => blake2::s(256, common_1mib);
//fn void blake2b_256_1mib() => blake2::b(256, common_1mib);
fn void blake3_1mib() => blake3::hash(common_1mib);
fn void ripemd_160_1mib() => ripemd::hash{160}(common_1mib);
fn void whirlpool_1mib() => whirlpool::hash(common_1mib);

View File

@@ -115,8 +115,8 @@ macro bool is_native_atomic_type($Type)
$case FLOAT:
$case BOOL:
return true;
$case DISTINCT:
$case CONST_ENUM:
$case TYPEDEF:
$case CONSTDEF:
return is_native_atomic_type($Type.inner);
$default:
return false;

View File

@@ -150,7 +150,7 @@ fn bool HashMap.is_initialized(&map)
fn HashMap* HashMap.init_from_map(&self, Allocator allocator, HashMap* other_map)
{
self.init(allocator, other_map.table.len, other_map.load_factor);
self.put_all_for_create(other_map);
hashmap_put_all_for_create(self, other_map);
return self;
}
@@ -233,7 +233,7 @@ macro Value HashMap.@get_or_set(&map, Key key, Value #expr)
if (e.hash == hash && equals(key, e.key)) return e.value;
}
Value val = #expr;
map.add_entry(hash, key, val, index);
hashmap_add_entry(map, hash, key, val, index);
return val;
}
@@ -269,13 +269,13 @@ fn bool HashMap.set(&map, Key key, Value value) @operator([]=)
return true;
}
}
map.add_entry(hash, key, value, index);
hashmap_add_entry(map, hash, key, value, index);
return false;
}
fn void? HashMap.remove(&map, Key key) @maydiscard
{
if (!map.remove_entry_for_key(key)) return NOT_FOUND~;
if (!hashmap_remove_entry_for_key(map, key)) return NOT_FOUND~;
}
fn void HashMap.clear(&map)
@@ -290,9 +290,9 @@ fn void HashMap.clear(&map)
{
Entry *to_delete = next;
next = next.next;
map.free_entry(to_delete);
hashmap_free_entry(map, to_delete);
}
map.free_entry(entry);
hashmap_free_entry(map, entry);
*entry_ref = null;
}
map.count = 0;
@@ -302,7 +302,7 @@ fn void HashMap.free(&map)
{
if (!map.is_initialized()) return;
map.clear();
map.free_internal(map.table.ptr);
hashmap_free_internal(map, map.table.ptr);
map.table = {};
}
@@ -402,7 +402,7 @@ fn HashMapKeyIterator HashMap.key_iter(&self)
// --- private methods
fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
fn void hashmap_add_entry(HashMap* map, uint hash, Key key, Value value, uint bucket_index) @private
{
$if COPY_KEYS:
key = key.copy(map.allocator);
@@ -411,11 +411,11 @@ fn void HashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_ind
map.table[bucket_index] = entry;
if (map.count++ >= map.threshold)
{
map.resize(map.table.len * 2);
hashmap_resize(map, map.table.len * 2);
}
}
fn void HashMap.resize(&map, uint new_capacity) @private
fn void hashmap_resize(HashMap* map, uint new_capacity) @private
{
Entry*[] old_table = map.table;
uint old_capacity = old_table.len;
@@ -425,9 +425,9 @@ fn void HashMap.resize(&map, uint new_capacity) @private
return;
}
Entry*[] new_table = allocator::new_array(map.allocator, Entry*, new_capacity);
map.transfer(new_table);
hashmap_transfer(map, new_table);
map.table = new_table;
map.free_internal(old_table.ptr);
hashmap_free_internal(map, old_table.ptr);
map.threshold = (uint)(new_capacity * map.load_factor);
}
@@ -443,7 +443,7 @@ fn usz? HashMap.to_format(&self, Formatter* f) @dynamic
return len + f.print(" }");
}
fn void HashMap.transfer(&map, Entry*[] new_table) @private
fn void hashmap_transfer(HashMap* map, Entry*[] new_table) @private
{
Entry*[] src = map.table;
uint new_capacity = new_table.len;
@@ -462,20 +462,20 @@ fn void HashMap.transfer(&map, Entry*[] new_table) @private
}
}
fn void HashMap.put_all_for_create(&map, HashMap* other_map) @private
fn void hashmap_put_all_for_create(HashMap* map, HashMap* other_map) @private
{
if (!other_map.count) return;
foreach (Entry *e : other_map.table)
{
while (e)
{
map.put_for_create(e.key, e.value);
hashmap_put_for_create(map, e.key, e.value);
e = e.next;
}
}
}
fn void HashMap.put_for_create(&map, Key key, Value value) @private
fn void hashmap_put_for_create(HashMap* map, Key key, Value value) @private
{
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
@@ -487,15 +487,15 @@ fn void HashMap.put_for_create(&map, Key key, Value value) @private
return;
}
}
map.create_entry(hash, key, value, i);
hashmap_create_entry(map, hash, key, value, i);
}
fn void HashMap.free_internal(&map, void* ptr) @inline @private
fn void hashmap_free_internal(HashMap* map, void* ptr) @inline @private
{
allocator::free(map.allocator, ptr);
}
fn bool HashMap.remove_entry_for_key(&map, Key key) @private
fn bool hashmap_remove_entry_for_key(HashMap* map, Key key) @private
{
if (!map.count) return false;
uint hash = rehash(key.hash());
@@ -516,7 +516,7 @@ fn bool HashMap.remove_entry_for_key(&map, Key key) @private
{
prev.next = next;
}
map.free_entry(e);
hashmap_free_entry(map, e);
return true;
}
prev = e;
@@ -525,7 +525,7 @@ fn bool HashMap.remove_entry_for_key(&map, Key key) @private
return false;
}
fn void HashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
fn void hashmap_create_entry(HashMap* map, uint hash, Key key, Value value, int bucket_index) @private
{
Entry *e = map.table[bucket_index];
$if COPY_KEYS:
@@ -536,12 +536,12 @@ fn void HashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_i
map.count++;
}
fn void HashMap.free_entry(&self, Entry *entry) @local
fn void hashmap_free_entry(HashMap* map, Entry *entry) @local
{
$if COPY_KEYS:
allocator::free(self.allocator, entry.key);
allocator::free(map.allocator, entry.key);
$endif
self.free_internal(entry);
hashmap_free_internal(map, entry);
}

View File

@@ -135,7 +135,7 @@ fn bool HashSet.is_initialized(&set)
fn HashSet* HashSet.init_from_set(&self, Allocator allocator, HashSet* other_set)
{
self.init(allocator, other_set.table.len, other_set.load_factor);
self.put_all_for_create(other_set);
hashset_put_all_for_create(self, other_set);
return self;
}
@@ -213,7 +213,7 @@ fn bool HashSet.add(&set, Value value)
{
if (e.hash == hash && equals(value, e.value)) return false;
}
set.add_entry(hash, value, index);
hashset_add_entry(set, hash, value, index);
return true;
}
@@ -258,7 +258,7 @@ fn bool HashSet.contains(&set, Value value)
*>
fn void? HashSet.remove(&set, Value value) @maydiscard
{
if (!set.remove_entry_for_value(value)) return NOT_FOUND~;
if (!hashset_remove_entry_for_value(set, value)) return NOT_FOUND~;
}
fn usz HashSet.remove_all(&set, Value[] values)
@@ -266,7 +266,7 @@ fn usz HashSet.remove_all(&set, Value[] values)
usz total;
foreach (v : values)
{
if (set.remove_entry_for_value(v)) total++;
if (hashset_remove_entry_for_value(set, v)) total++;
}
return total;
}
@@ -279,7 +279,7 @@ fn usz HashSet.remove_all_from(&set, HashSet* other)
usz total;
other.@each(;Value val)
{
if (set.remove_entry_for_value(val)) total++;
if (hashset_remove_entry_for_value(set, val)) total++;
};
return total;
}
@@ -291,7 +291,7 @@ fn void HashSet.free(&set)
{
if (!set.is_initialized()) return;
set.clear();
set.free_internal(set.table.ptr);
hashset_free_internal(set, set.table.ptr);
*set = {};
}
@@ -314,10 +314,10 @@ fn void HashSet.clear(&set)
{
Entry *to_delete = next;
next = next.next;
set.free_entry(to_delete);
hashset_free_entry(set, to_delete);
}
set.free_entry(entry);
hashset_free_entry(set, entry);
*entry_ref = null;
}
set.count = 0;
@@ -327,7 +327,7 @@ fn void HashSet.reserve(&set, usz capacity)
{
if (capacity > set.threshold)
{
set.resize(math::next_power_of_2(capacity));
hashset_resize(set, math::next_power_of_2(capacity));
}
}
@@ -464,17 +464,17 @@ fn bool HashSet.is_subset(&self, HashSet* other)
// --- private methods
fn void HashSet.add_entry(&set, uint hash, Value value, uint bucket_index) @private
fn void hashset_add_entry(HashSet* set, uint hash, Value value, uint bucket_index) @private
{
Entry* entry = allocator::new(set.allocator, Entry, { .hash = hash, .value = value, .next = set.table[bucket_index] });
set.table[bucket_index] = entry;
if (set.count++ >= set.threshold)
{
set.resize(set.table.len * 2);
hashset_resize(set, set.table.len * 2);
}
}
fn void HashSet.resize(&self, usz new_capacity) @private
fn void hashset_resize(HashSet* self, usz new_capacity) @private
{
Entry*[] old_table = self.table;
usz old_capacity = old_table.len;
@@ -484,9 +484,9 @@ fn void HashSet.resize(&self, usz new_capacity) @private
return;
}
Entry*[] new_table = allocator::new_array(self.allocator, Entry*, new_capacity);
self.transfer(new_table);
hashset_transfer(self, new_table);
self.table = new_table;
self.free_internal(old_table.ptr);
hashset_free_internal(self, old_table.ptr);
self.threshold = (uint)(new_capacity * self.load_factor);
}
@@ -502,7 +502,7 @@ fn usz? HashSet.to_format(&self, Formatter* f) @dynamic
return len + f.print(" }");
}
fn void HashSet.transfer(&self, Entry*[] new_table) @private
fn void hashset_transfer(HashSet* self, Entry*[] new_table) @private
{
Entry*[] src = self.table;
uint new_capacity = new_table.len;
@@ -521,20 +521,20 @@ fn void HashSet.transfer(&self, Entry*[] new_table) @private
}
}
fn void HashSet.put_all_for_create(&set, HashSet* other_set) @private
fn void hashset_put_all_for_create(HashSet* set, HashSet* other_set) @private
{
if (!other_set.count) return;
foreach (Entry *e : other_set.table)
{
while (e)
{
set.put_for_create(e.value);
hashset_put_for_create(set, e.value);
e = e.next;
}
}
}
fn void HashSet.put_for_create(&set, Value value) @private
fn void hashset_put_for_create(HashSet* set, Value value) @private
{
uint hash = rehash(value.hash());
uint i = index_for(hash, set.table.len);
@@ -546,15 +546,15 @@ fn void HashSet.put_for_create(&set, Value value) @private
return;
}
}
set.create_entry(hash, value, i);
hashset_create_entry(set, hash, value, i);
}
fn void HashSet.free_internal(&self, void* ptr) @inline @private
fn void hashset_free_internal(HashSet* self, void* ptr) @inline @private
{
allocator::free(self.allocator, ptr);
}
fn void HashSet.create_entry(&set, uint hash, Value value, int bucket_index) @private
fn void hashset_create_entry(HashSet* set, uint hash, Value value, int bucket_index) @private
{
Entry* entry = allocator::new(set.allocator, Entry, {
.hash = hash,
@@ -569,7 +569,7 @@ fn void HashSet.create_entry(&set, uint hash, Value value, int bucket_index) @pr
Removes the entry for the specified value if present
@return "true if found and removed, false otherwise"
*>
fn bool HashSet.remove_entry_for_value(&set, Value value) @private
fn bool hashset_remove_entry_for_value(HashSet* set, Value value) @private
{
if (!set.count) return false;
uint hash = rehash(value.hash());
@@ -590,7 +590,7 @@ fn bool HashSet.remove_entry_for_value(&set, Value value) @private
{
prev.next = next;
}
set.free_entry(e);
hashset_free_entry(set, e);
return true;
}
prev = e;
@@ -600,7 +600,7 @@ fn bool HashSet.remove_entry_for_value(&set, Value value) @private
return false;
}
fn void HashSet.free_entry(&set, Entry *entry) @private
fn void hashset_free_entry(HashSet* set, Entry *entry) @private
{
allocator::free(set.allocator, entry);
}

View File

@@ -73,7 +73,7 @@ fn bool InterfaceList.is_initialized(&self) @inline => self.allocator != null;
macro void InterfaceList.push(&self, element)
{
if (!self.allocator) self.allocator = tmem;
self._append(allocator::clone(self.allocator, element));
interfacelist_append(self, allocator::clone(self.allocator, element));
}
<*
@@ -381,7 +381,7 @@ fn usz? InterfaceList.to_format(&self, Formatter* formatter) @dynamic
*>
fn usz InterfaceList.remove_if(&self, InterfacePredicate filter)
{
return self._remove_if(filter, false);
return interfacelist_remove_if(self, filter, false);
}
<*
@@ -392,7 +392,7 @@ fn usz InterfaceList.remove_if(&self, InterfacePredicate filter)
*>
fn usz InterfaceList.retain_if(&self, InterfacePredicate selection)
{
return self._remove_if(selection, true);
return interfacelist_remove_if(self, selection, true);
}
<*
@@ -404,7 +404,7 @@ fn usz InterfaceList.retain_if(&self, InterfacePredicate selection)
*>
fn usz InterfaceList.remove_using_test(&self, InterfaceTest filter, Type context)
{
return self._remove_using_test(filter, false, context);
return interfacelist_remove_using_test(self, filter, false, context);
}
<*
@@ -416,7 +416,7 @@ fn usz InterfaceList.remove_using_test(&self, InterfaceTest filter, Type context
*>
fn usz InterfaceList.retain_using_test(&self, InterfaceTest selection, Type context)
{
return self._remove_using_test(selection, true, context);
return interfacelist_remove_using_test(self, selection, true, context);
}
<*
@@ -455,7 +455,7 @@ macro void InterfaceList.set(&self, usz index, value)
// -- private
fn void InterfaceList.ensure_capacity(&self, usz added = 1) @inline @private
fn void interfacelist_ensure_capacity(InterfaceList* self, usz added = 1) @inline @private
{
usz new_size = self.size + added;
if (self.capacity >= new_size) return;
@@ -466,18 +466,18 @@ fn void InterfaceList.ensure_capacity(&self, usz added = 1) @inline @private
self.reserve(new_capacity);
}
fn void InterfaceList._append(&self, Type element) @local
fn void interfacelist_append(InterfaceList* self, Type element) @local
{
self.ensure_capacity();
interfacelist_ensure_capacity(self);
self.entries[self.size++] = element;
}
<*
@require index < self.size
*>
fn void InterfaceList._insert_at(&self, usz index, Type value) @local
fn void interfacelist_insert_at(InterfaceList* self, usz index, Type value) @local
{
self.ensure_capacity();
interfacelist_ensure_capacity(self);
for (usz i = self.size; i > index; i--)
{
self.entries[i] = self.entries[i - 1];
@@ -486,7 +486,7 @@ fn void InterfaceList._insert_at(&self, usz index, Type value) @local
self.entries[index] = value;
}
macro usz InterfaceList._remove_using_test(&self, InterfaceTest filter, bool $invert, ctx) @local
macro usz interfacelist_remove_using_test(InterfaceList* self, InterfaceTest filter, bool $invert, ctx) @local
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)
@@ -512,7 +512,7 @@ macro usz InterfaceList._remove_using_test(&self, InterfaceTest filter, bool $in
return size - self.size;
}
macro usz InterfaceList._remove_if(&self, InterfacePredicate filter, bool $invert) @local
macro usz interfacelist_remove_if(InterfaceList* self, InterfacePredicate filter, bool $invert) @local
{
usz size = self.size;
for (usz i = size, usz k = size; k > 0; k = i)

View File

@@ -75,7 +75,7 @@ fn void LinkedBlockingQueue.free(&self)
self.not_full.destroy();
}
fn void LinkedBlockingQueue.link_entry(&self, QueueEntry* entry) @private
fn void linkedblockingqueue_link_entry(LinkedBlockingQueue* self, QueueEntry* entry) @private
{
entry.next = null;
entry.prev = self.tail;
@@ -95,7 +95,7 @@ fn void LinkedBlockingQueue.link_entry(&self, QueueEntry* entry) @private
}
fn QueueEntry* LinkedBlockingQueue.unlink_head(&self) @private
fn QueueEntry* linkedblockingqueue_unlink_head(LinkedBlockingQueue* self) @private
{
if (self.head == null) return null;
@@ -134,7 +134,7 @@ fn void LinkedBlockingQueue.push(&self, Value value)
.next = null,
.prev = null
});
self.link_entry(entry);
linkedblockingqueue_link_entry(self, entry);
// Signal that queue is no longer empty
self.not_empty.signal();
@@ -156,7 +156,7 @@ fn Value LinkedBlockingQueue.poll(&self)
self.not_empty.wait(&self.lock);
}
QueueEntry* entry = self.unlink_head();
QueueEntry* entry = linkedblockingqueue_unlink_head(self);
Value value = entry.value;
allocator::free(self.allocator, entry);
if (self.capacity > 0)
@@ -180,7 +180,7 @@ fn Value? LinkedBlockingQueue.pop(&self)
{
if (self.count == 0) return NO_MORE_ELEMENT~;
QueueEntry* entry = self.unlink_head();
QueueEntry* entry = linkedblockingqueue_unlink_head(self);
Value value = entry.value;
allocator::free(self.allocator, entry);
@@ -217,7 +217,7 @@ fn Value? LinkedBlockingQueue.poll_timeout(&self, Duration timeout)
if (!self.count) return NO_MORE_ELEMENT~;
}
QueueEntry* entry = self.unlink_head();
QueueEntry* entry = linkedblockingqueue_unlink_head(self);
Value value = entry.value;
allocator::free(self.allocator, entry);
@@ -273,7 +273,7 @@ fn void? LinkedBlockingQueue.try_push(&self, Value value)
.next = null,
.prev = null
});
self.link_entry(entry);
linkedblockingqueue_link_entry(self, entry);
self.not_empty.signal();
};
}
@@ -307,7 +307,7 @@ fn void? LinkedBlockingQueue.push_timeout(&self, Value value, Duration timeout)
.next = null,
.prev = null
});
self.link_entry(entry);
linkedblockingqueue_link_entry(self, entry);
self.not_empty.signal();
};
}

View File

@@ -150,7 +150,7 @@ fn bool LinkedHashMap.is_initialized(&map)
fn LinkedHashMap* LinkedHashMap.init_from_map(&self, Allocator allocator, LinkedHashMap* other_map)
{
self.init(allocator, other_map.table.len, other_map.load_factor);
self.put_all_for_create(other_map);
linkedhashmap_put_all_for_create(self, other_map);
return self;
}
@@ -242,13 +242,13 @@ fn bool LinkedHashMap.set(&map, Key key, Value value) @operator([]=)
return true;
}
}
map.add_entry(hash, key, value, index);
linkedhashmap_add_entry(map, hash, key, value, index);
return false;
}
fn void? LinkedHashMap.remove(&map, Key key) @maydiscard
{
if (!map.remove_entry_for_key(key)) return NOT_FOUND~;
if (!linkedhashmap_remove_entry_for_key(map, key)) return NOT_FOUND~;
}
fn void LinkedHashMap.clear(&map)
@@ -259,7 +259,7 @@ fn void LinkedHashMap.clear(&map)
while (entry)
{
LinkedEntry* next = entry.after;
map.free_entry(entry);
linkedhashmap_free_entry(map, entry);
entry = next;
}
@@ -277,7 +277,7 @@ fn void LinkedHashMap.free(&map)
{
if (!map.is_initialized()) return;
map.clear();
map.free_internal(map.table.ptr);
linkedhashmap_free_internal(map, map.table.ptr);
map.table = {};
}
@@ -396,7 +396,7 @@ fn bool LinkedHashMapIterator.has_next(&self)
// --- private methods
fn void LinkedHashMap.add_entry(&map, uint hash, Key key, Value value, uint bucket_index) @private
fn void linkedhashmap_add_entry(LinkedHashMap* map, uint hash, Key key, Value value, uint bucket_index) @private
{
$if COPY_KEYS:
key = key.copy(map.allocator);
@@ -428,11 +428,11 @@ fn void LinkedHashMap.add_entry(&map, uint hash, Key key, Value value, uint buck
if (map.count++ >= map.threshold)
{
map.resize(map.table.len * 2);
linkedhashmap_resize(map, map.table.len * 2);
}
}
fn void LinkedHashMap.resize(&map, uint new_capacity) @private
fn void linkedhashmap_resize(LinkedHashMap* map, uint new_capacity) @private
{
LinkedEntry*[] old_table = map.table;
uint old_capacity = old_table.len;
@@ -502,7 +502,7 @@ fn void LinkedHashMap.resize(&map, uint new_capacity) @private
}
}
map.free_internal(old_table.ptr);
linkedhashmap_free_internal(map, old_table.ptr);
}
fn usz? LinkedHashMap.to_format(&self, Formatter* f) @dynamic
@@ -517,7 +517,7 @@ fn usz? LinkedHashMap.to_format(&self, Formatter* f) @dynamic
return len + f.print(" }");
}
fn void LinkedHashMap.transfer(&map, LinkedEntry*[] new_table) @private
fn void linkedhashmap_transfer(LinkedHashMap* map, LinkedEntry*[] new_table) @private
{
LinkedEntry*[] src = map.table;
uint new_capacity = new_table.len;
@@ -536,7 +536,7 @@ fn void LinkedHashMap.transfer(&map, LinkedEntry*[] new_table) @private
}
}
fn void LinkedHashMap.put_all_for_create(&map, LinkedHashMap* other_map) @private
fn void linkedhashmap_put_all_for_create(LinkedHashMap* map, LinkedHashMap* other_map) @private
{
if (!other_map.count) return;
other_map.@each(; Key key, Value value) {
@@ -544,7 +544,7 @@ fn void LinkedHashMap.put_all_for_create(&map, LinkedHashMap* other_map) @privat
};
}
fn void LinkedHashMap.put_for_create(&map, Key key, Value value) @private
fn void linkedhashmap_put_for_create(LinkedHashMap* map, Key key, Value value) @private
{
uint hash = rehash(key.hash());
uint i = index_for(hash, map.table.len);
@@ -556,15 +556,15 @@ fn void LinkedHashMap.put_for_create(&map, Key key, Value value) @private
return;
}
}
map.create_entry(hash, key, value, i);
linkedhashmap_create_entry(map, hash, key, value, i);
}
fn void LinkedHashMap.free_internal(&map, void* ptr) @inline @private
fn void linkedhashmap_free_internal(LinkedHashMap* map, void* ptr) @inline @private
{
allocator::free(map.allocator, ptr);
}
fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
fn bool linkedhashmap_remove_entry_for_key(LinkedHashMap* map, Key key) @private
{
if (!map.count) return false;
@@ -605,7 +605,7 @@ fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
}
map.count--;
map.free_entry(e);
linkedhashmap_free_entry(map, e);
return true;
}
prev = e;
@@ -614,23 +614,23 @@ fn bool LinkedHashMap.remove_entry_for_key(&map, Key key) @private
return false;
}
fn void LinkedHashMap.create_entry(&map, uint hash, Key key, Value value, int bucket_index) @private
fn void linkedhashmap_create_entry(LinkedHashMap* map, uint hash, Key key, Value value, int bucket_index) @private
{
LinkedEntry *e = map.table[bucket_index];
$if COPY_KEYS:
key = key.copy(map.allocator);
key = key.copy(map.allocator);
$endif
LinkedEntry* entry = allocator::new(map.allocator, LinkedEntry, { .hash = hash, .key = key, .value = value, .next = map.table[bucket_index] });
map.table[bucket_index] = entry;
map.count++;
}
fn void LinkedHashMap.free_entry(&self, LinkedEntry *entry) @local
fn void linkedhashmap_free_entry(LinkedHashMap* self, LinkedEntry *entry) @local
{
$if COPY_KEYS:
allocator::free(self.allocator, entry.key);
allocator::free(self.allocator, entry.key);
$endif
self.free_internal(entry);
linkedhashmap_free_internal(self, entry);
}

View File

@@ -143,7 +143,7 @@ fn LinkedHashSet* LinkedHashSet.init_from_set(&self, Allocator allocator, Linked
LinkedEntry* entry = other_set.head;
while (entry) // Save insertion order
{
self.put_for_create(entry.value);
linkedhashset_put_for_create(self, entry.value);
entry = entry.after;
}
return self;
@@ -223,7 +223,7 @@ fn bool LinkedHashSet.add(&set, Value value)
{
if (e.hash == hash && equals(value, e.value)) return false;
}
set.add_entry(hash, value, index);
linkedhashset_add_entry(set, hash, value, index);
return true;
}
@@ -266,7 +266,7 @@ fn bool LinkedHashSet.contains(&set, Value value)
*>
fn void? LinkedHashSet.remove(&set, Value value) @maydiscard
{
if (!set.remove_entry_for_value(value)) return NOT_FOUND~;
if (!linkedhashset_remove_entry_for_value(set, value)) return NOT_FOUND~;
}
fn usz LinkedHashSet.remove_all(&set, Value[] values)
@@ -274,7 +274,7 @@ fn usz LinkedHashSet.remove_all(&set, Value[] values)
usz total;
foreach (v : values)
{
if (set.remove_entry_for_value(v)) total++;
if (linkedhashset_remove_entry_for_value(set, v)) total++;
}
return total;
}
@@ -287,7 +287,7 @@ fn usz LinkedHashSet.remove_all_from(&set, LinkedHashSet* other)
usz total;
other.@each(;Value val)
{
if (set.remove_entry_for_value(val)) total++;
if (linkedhashset_remove_entry_for_value(set, val)) total++;
};
return total;
}
@@ -299,7 +299,7 @@ fn void LinkedHashSet.free(&set)
{
if (!set.is_initialized()) return;
set.clear();
set.free_internal(set.table.ptr);
linkedhashset_free_internal(set, set.table.ptr);
set.table = {};
}
@@ -316,7 +316,7 @@ fn void LinkedHashSet.clear(&set)
while (entry)
{
LinkedEntry* next = entry.after;
set.free_entry(entry);
linkedhashset_free_entry(set, entry);
entry = next;
}
@@ -334,7 +334,7 @@ fn void LinkedHashSet.reserve(&set, usz capacity)
{
if (capacity > set.threshold)
{
set.resize(math::next_power_of_2(capacity));
linkedhashset_resize(set, math::next_power_of_2(capacity));
}
}
@@ -451,7 +451,7 @@ fn bool LinkedHashSet.is_subset(&self, LinkedHashSet* other)
// --- private methods
fn void LinkedHashSet.add_entry(&set, uint hash, Value value, uint bucket_index) @private
fn void linkedhashset_add_entry(LinkedHashSet* set, uint hash, Value value, uint bucket_index) @private
{
LinkedEntry* entry = allocator::new(set.allocator, LinkedEntry, {
.hash = hash,
@@ -478,11 +478,11 @@ fn void LinkedHashSet.add_entry(&set, uint hash, Value value, uint bucket_index)
if (set.count++ >= set.threshold)
{
set.resize(set.table.len * 2);
linkedhashset_resize(set, set.table.len * 2);
}
}
fn void LinkedHashSet.resize(&set, usz new_capacity) @private
fn void linkedhashset_resize(LinkedHashSet* set, usz new_capacity) @private
{
LinkedEntry*[] old_table = set.table;
usz old_capacity = old_table.len;
@@ -552,7 +552,7 @@ fn void LinkedHashSet.resize(&set, usz new_capacity) @private
}
}
set.free_internal(old_table.ptr);
linkedhashset_free_internal(set, old_table.ptr);
}
fn usz? LinkedHashSet.to_format(&self, Formatter* f) @dynamic
@@ -567,7 +567,7 @@ fn usz? LinkedHashSet.to_format(&self, Formatter* f) @dynamic
return len + f.print(" }");
}
fn void LinkedHashSet.transfer(&set, LinkedEntry*[] new_table) @private
fn void linked_hashset_transfer(LinkedHashSet* set, LinkedEntry*[] new_table) @private
{
LinkedEntry*[] src = set.table;
uint new_capacity = new_table.len;
@@ -586,7 +586,7 @@ fn void LinkedHashSet.transfer(&set, LinkedEntry*[] new_table) @private
}
}
fn void LinkedHashSet.put_for_create(&set, Value value) @private
fn void linkedhashset_put_for_create(LinkedHashSet* set, Value value) @private
{
uint hash = rehash(value.hash());
uint i = index_for(hash, set.table.len);
@@ -598,15 +598,15 @@ fn void LinkedHashSet.put_for_create(&set, Value value) @private
return;
}
}
set.create_entry(hash, value, i);
linkedhashset_create_entry(set, hash, value, i);
}
fn void LinkedHashSet.free_internal(&set, void* ptr) @inline @private
fn void linkedhashset_free_internal(LinkedHashSet* set, void* ptr) @inline @private
{
allocator::free(set.allocator, ptr);
}
fn void LinkedHashSet.create_entry(&set, uint hash, Value value, int bucket_index) @private
fn void linkedhashset_create_entry(LinkedHashSet* set, uint hash, Value value, int bucket_index) @private
{
LinkedEntry* entry = allocator::new(set.allocator, LinkedEntry, {
.hash = hash,
@@ -633,7 +633,7 @@ fn void LinkedHashSet.create_entry(&set, uint hash, Value value, int bucket_inde
set.count++;
}
fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
fn bool linkedhashset_remove_entry_for_value(LinkedHashSet* set, Value value) @private
{
if (!set.count) return false;
@@ -674,7 +674,7 @@ fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
}
set.count--;
set.free_entry(e);
linkedhashset_free_entry(set, e);
return true;
}
prev = e;
@@ -683,7 +683,7 @@ fn bool LinkedHashSet.remove_entry_for_value(&set, Value value) @private
return false;
}
fn void LinkedHashSet.free_entry(&set, LinkedEntry *entry) @private
fn void linkedhashset_free_entry(LinkedHashSet* set, LinkedEntry *entry) @private
{
allocator::free(set.allocator, entry);
}

View File

@@ -217,7 +217,7 @@ fn usz? LinkedList.rindex_of(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
*>
fn void LinkedList.remove_at(&self, usz index)
{
self.unlink(self.node_at_index(index));
linked_list_unlink(self, self.node_at_index(index));
}
<*
@@ -232,47 +232,47 @@ fn void LinkedList.insert_at(&self, usz index, Type element)
case self.size:
self.push(element);
default:
self.link_before(self.node_at_index(index), element);
linked_list_link_before(self, self.node_at_index(index), element);
}
}
<*
@require succ != null
*>
fn void LinkedList.link_before(&self, Node *succ, Type value) @private
fn void linked_list_link_before(LinkedList* l, Node *succ, Type value) @private
{
Node* pred = succ.prev;
Node* new_node = self.alloc_node();
Node* new_node = l.alloc_node();
*new_node = { .prev = pred, .next = succ, .value = value };
succ.prev = new_node;
if (!pred)
{
self._first = new_node;
l._first = new_node;
}
else
{
pred.next = new_node;
}
self.size++;
l.size++;
}
<*
@require self._first != null
@require l._first != null
*>
fn void LinkedList.unlink_first(&self) @private
fn void linked_list_unlink_first(LinkedList* l) @private
{
Node* f = self._first;
Node* f = l._first;
Node* next = f.next;
self.free_node(f);
self._first = next;
l.free_node(f);
l._first = next;
if (!next)
{
self._last = null;
l._last = null;
}
else
{
next.prev = null;
}
self.size--;
l.size--;
}
fn usz LinkedList.remove(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
@@ -285,7 +285,7 @@ fn usz LinkedList.remove(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
{
case equals(node.value, t):
Node* next = node.next;
self.unlink(node);
linked_list_unlink(self, node);
node = next;
default:
node = node.next;
@@ -297,7 +297,7 @@ fn usz LinkedList.remove(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
fn Type? LinkedList.pop(&self)
{
if (!self._last) return NO_MORE_ELEMENT~;
defer self.unlink_last();
defer linked_list_unlink_last(self);
return self._last.value;
}
@@ -309,20 +309,20 @@ fn bool LinkedList.is_empty(&self)
fn Type? LinkedList.pop_front(&self)
{
if (!self._first) return NO_MORE_ELEMENT~;
defer self.unlink_first();
defer linked_list_unlink_first(self);
return self._first.value;
}
fn void? LinkedList.remove_last(&self) @maydiscard
{
if (!self._first) return NO_MORE_ELEMENT~;
self.unlink_last();
linked_list_unlink_last(self);
}
fn void? LinkedList.remove_first(&self) @maydiscard
{
if (!self._first) return NO_MORE_ELEMENT~;
self.unlink_first();
linked_list_unlink_first(self);
}
@@ -332,7 +332,7 @@ fn bool LinkedList.remove_first_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
{
if (node.value == t)
{
self.unlink(node);
linked_list_unlink(self, node);
return true;
}
}
@@ -345,7 +345,7 @@ fn bool LinkedList.remove_last_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
{
if (node.value == t)
{
self.unlink(node);
linked_list_unlink(self, node);
return true;
}
}
@@ -354,7 +354,7 @@ fn bool LinkedList.remove_last_match(&self, Type t) @if(ELEMENT_IS_EQUATABLE)
<*
@require self._last != null
*>
fn void LinkedList.unlink_last(&self) @inline @private
fn void linked_list_unlink_last(LinkedList* self) @inline @private
{
Node* l = self._last;
Node* prev = l.prev;
@@ -374,7 +374,7 @@ fn void LinkedList.unlink_last(&self) @inline @private
<*
@require x != null
*>
fn void LinkedList.unlink(&self, Node* x) @private
fn void linked_list_unlink(LinkedList* self, Node* x) @private
{
Node* next = x.next;
Node* prev = x.prev;

View File

@@ -82,7 +82,7 @@ fn void List.init_wrapping_array(&self, Allocator allocator, Type[] types)
self.allocator = allocator;
self.capacity = types.len;
self.entries = types.ptr;
self.set_size(types.len);
list_set_size(self, types.len);
}
fn bool List.is_initialized(&self) @inline => self.allocator != null && self.allocator != (Allocator)&dummy;
@@ -110,19 +110,19 @@ fn usz? List.to_format(&self, Formatter* formatter) @dynamic
fn void List.push(&self, Type element) @inline
{
self.reserve(1);
self.entries[self.set_size(self.size + 1)] = element;
self.entries[list_set_size(self, self.size + 1)] = element;
}
fn Type? List.pop(&self)
{
if (!self.size) return NO_MORE_ELEMENT~;
defer self.set_size(self.size - 1);
defer list_set_size(self, self.size - 1);
return self.entries[self.size - 1];
}
fn void List.clear(&self)
{
self.set_size(0);
list_set_size(self, 0);
}
fn Type? List.pop_first(&self)
@@ -138,7 +138,7 @@ fn Type? List.pop_first(&self)
fn void List.remove_at(&self, usz index)
{
usz new_size = self.size - 1;
defer self.set_size(new_size);
defer list_set_size(self, new_size);
if (!new_size || index == new_size) return;
self.entries[index .. new_size - 1] = self.entries[index + 1 .. new_size];
}
@@ -147,7 +147,7 @@ fn void List.add_all(&self, List* other_list)
{
if (!other_list.size) return;
self.reserve(other_list.size);
usz index = self.set_size(self.size + other_list.size);
usz index = list_set_size(self, self.size + other_list.size);
foreach (&value : other_list)
{
self.entries[index++] = *value;
@@ -203,7 +203,7 @@ fn void List.add_array(&self, Type[] array) @deprecated("Use push_all")
{
if (!array.len) return;
self.reserve(array.len);
usz index = self.set_size(self.size + array.len);
usz index = list_set_size(self, self.size + array.len);
self.entries[index : array.len] = array[..];
}
@@ -217,7 +217,7 @@ fn void List.push_all(&self, Type[] array)
{
if (!array.len) return;
self.reserve(array.len);
usz index = self.set_size(self.size + array.len);
usz index = list_set_size(self, self.size + array.len);
self.entries[index : array.len] = array[..];
}
@@ -232,7 +232,7 @@ fn void List.push_front(&self, Type type) @inline
fn void List.insert_at(&self, usz index, Type type)
{
self.reserve(1);
self.set_size(self.size + 1);
list_set_size(self, self.size + 1);
for (isz i = self.size - 1; i > index; i--)
{
self.entries[i] = self.entries[i - 1];
@@ -251,7 +251,7 @@ fn void List.set_at(&self, usz index, Type type)
fn void? List.remove_last(&self) @maydiscard
{
if (!self.size) return NO_MORE_ELEMENT~;
self.set_size(self.size - 1);
list_set_size(self, self.size - 1);
}
fn void? List.remove_first(&self) @maydiscard
@@ -299,7 +299,7 @@ fn void List.free(&self)
{
if (!self.allocator || self.allocator.ptr == &dummy || !self.capacity) return;
self.pre_free(); // Remove sanitizer annotation
list_pre_free(self); // Remove sanitizer annotation
$if type_is_overaligned():
allocator::free_aligned(self.allocator, self.entries);
@@ -358,7 +358,7 @@ fn usz List.retain_using_test(&self, ElementTest filter, any context)
return list_common::list_remove_using_test(self, filter, true, context);
}
fn void List.ensure_capacity(&self, usz min_capacity) @local
fn void list_ensure_capacity(List* self, usz min_capacity) @local
{
if (!min_capacity) return;
if (self.capacity >= min_capacity) return;
@@ -374,7 +374,7 @@ fn void List.ensure_capacity(&self, usz min_capacity) @local
break;
}
self.pre_free(); // Remove sanitizer annotation
list_pre_free(self); // Remove sanitizer annotation
min_capacity = math::next_power_of_2(min_capacity);
$if type_is_overaligned():
@@ -384,7 +384,7 @@ fn void List.ensure_capacity(&self, usz min_capacity) @local
$endif;
self.capacity = min_capacity;
self.post_alloc(); // Add sanitizer annotation
list_post_alloc(self); // Add sanitizer annotation
}
<*
@@ -419,7 +419,7 @@ fn void List.reserve(&self, usz added)
assert(new_size < usz.max / 2U);
usz new_capacity = self.capacity ? 2U * self.capacity : 16U;
while (new_capacity < new_size) new_capacity *= 2U;
self.ensure_capacity(new_capacity);
list_ensure_capacity(self, new_capacity);
}
fn void List._update_size_change(&self,usz old_size, usz new_size)
@@ -436,7 +436,7 @@ fn void List._update_size_change(&self,usz old_size, usz new_size)
<*
@require new_size == 0 || self.capacity != 0
*>
fn usz List.set_size(&self, usz new_size) @inline @private
fn usz list_set_size(List* self, usz new_size) @inline @private
{
usz old_size = self.size;
self._update_size_change(old_size, new_size);
@@ -444,7 +444,7 @@ fn usz List.set_size(&self, usz new_size) @inline @private
return old_size;
}
macro void List.pre_free(&self) @private
macro void list_pre_free(List* self) @private
{
if (!self.capacity) return;
self._update_size_change(self.size, self.capacity);
@@ -453,7 +453,7 @@ macro void List.pre_free(&self) @private
<*
@require self.capacity > 0
*>
macro void List.post_alloc(&self) @private
macro void list_post_alloc(List* self) @private
{
self._update_size_change(self.capacity, self.size);
}

View File

@@ -151,7 +151,7 @@ fn bool Object.is_indexable(&self) => self.is_empty() || self.is_array();
<*
@require self.is_keyable()
*>
fn void Object.init_map_if_needed(&self) @private
fn void object_init_map_if_needed(Object* self) @private
{
if (self.is_empty())
{
@@ -163,7 +163,7 @@ fn void Object.init_map_if_needed(&self) @private
<*
@require self.is_indexable()
*>
fn void Object.init_array_if_needed(&self) @private
fn void object_init_array_if_needed(Object* self) @private
{
if (self.is_empty())
{
@@ -175,9 +175,9 @@ fn void Object.init_array_if_needed(&self) @private
<*
@require self.is_keyable()
*>
fn void Object.set_object(&self, String key, Object* new_object) @private
fn void object_set_object(Object* self, String key, Object* new_object) @private
{
self.init_map_if_needed();
object_init_map_if_needed(self);
Object*? val = self.map.get_entry(key).value;
defer (void)val.free();
self.map.set(key, new_object);
@@ -214,7 +214,7 @@ macro Object* Object.object_from_value(&self, value) @private
macro Object* Object.set(&self, String key, value)
{
Object* val = self.object_from_value(value);
self.set_object(key, val);
object_set_object(self, key, val);
return val;
}
@@ -267,7 +267,7 @@ fn usz Object.get_len(&self)
*>
fn void Object.push_object(&self, Object* to_append)
{
self.init_array_if_needed();
object_init_array_if_needed(self);
self.array.push(to_append);
}
@@ -276,7 +276,7 @@ fn void Object.push_object(&self, Object* to_append)
*>
fn void Object.set_object_at(&self, usz index, Object* to_set)
{
self.init_array_if_needed();
object_init_array_if_needed(self);
while (self.array.len() < index)
{
self.array.push(&NULL_OBJECT);

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@ const uint PIXELS_MAX = 400000000;
Purely informative. It will be saved to the file header,
but does not affect how chunks are en-/decoded.
*>
const enum QOIColorspace : char
constdef QOIColorspace : char
{
<* sRGB with linear alpha *>
SRGB = 0,
@@ -21,7 +21,7 @@ const enum QOIColorspace : char
AUTO can be used when decoding to automatically determine
the channels from the file's header.
*>
const enum QOIChannels : inline char
constdef QOIChannels : inline char
{
AUTO = 0,
RGB = 3,

1215
lib/std/compression/zip.c3 Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -56,7 +56,7 @@ fn BackedArenaAllocator*? new_backed_allocator(usz size, Allocator allocator)
fn void BackedArenaAllocator.destroy(&self)
{
self.reset(0);
if (self.last_page) (void)self._free_page(self.last_page);
if (self.last_page) (void)_free_page(self, self.last_page);
allocator::free(self.backing_allocator, self);
}
@@ -79,7 +79,7 @@ fn void BackedArenaAllocator.reset(&self, usz mark)
self.used = last_page.mark;
ExtraPage *to_free = last_page;
last_page = last_page.prev_page;
self._free_page(to_free)!!;
_free_page(self, to_free)!!;
}
self.last_page = last_page;
$if env::COMPILER_SAFE_MODE || env::ADDRESS_SANITIZER:
@@ -98,13 +98,13 @@ fn void BackedArenaAllocator.reset(&self, usz mark)
self.used = mark;
}
fn void? BackedArenaAllocator._free_page(&self, ExtraPage* page) @inline @local
fn void? _free_page(BackedArenaAllocator* self, ExtraPage* page) @inline @local
{
void* mem = page.start;
return self.backing_allocator.release(mem, page.is_aligned());
}
fn void*? BackedArenaAllocator._realloc_page(&self, ExtraPage* page, usz size, usz alignment) @inline @local
fn void*? _realloc_page(BackedArenaAllocator* self, ExtraPage* page, usz size, usz alignment) @inline @local
{
// Then the actual start pointer:
void* real_pointer = page.start;
@@ -133,7 +133,7 @@ fn void*? BackedArenaAllocator.resize(&self, void* pointer, usz size, usz alignm
assert(self.last_page, "Realloc of unrelated pointer");
// First grab the page
ExtraPage *page = pointer - ExtraPage.sizeof;
return self._realloc_page(page, size, alignment);
return _realloc_page(self, page, size, alignment);
}
AllocChunk* data = self.acquire(size, NO_ZERO, alignment)!;

View File

@@ -137,12 +137,67 @@ fn void DynamicArenaAllocator.reset(&self)
self.page = page;
}
<*
@require size > 0 : `acquire expects size > 0`
@require !alignment || math::is_power_of_2(alignment)
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
*>
fn void*? DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
alignment = alignment_for_allocation(alignment);
DynamicArenaPage* page = self.page;
void* ptr @noinit;
do SET_DONE:
{
if (!page && self.unused_page)
{
self.page = page = self.unused_page;
self.unused_page = page.prev_arena;
page.prev_arena = null;
}
if (!page)
{
ptr = _alloc_new(self, size, alignment)!;
break SET_DONE;
}
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
usz new_used = start - page.memory + size;
if ALLOCATE_NEW: (new_used > page.total)
{
if ((page = self.unused_page))
{
start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
new_used = start + size - page.memory;
if (page.total >= new_used)
{
self.unused_page = page.prev_arena;
page.prev_arena = self.page;
self.page = page;
break ALLOCATE_NEW;
}
}
ptr = _alloc_new(self, size, alignment)!;
break SET_DONE;
}
page.used = new_used;
assert(start + size == page.memory + page.used);
ptr = start;
DynamicArenaChunk* chunk = (DynamicArenaChunk*)ptr - 1;
chunk.size = size;
};
if (init_type == ZERO) mem::clear(ptr, size, mem::DEFAULT_MEM_ALIGNMENT);
return ptr;
}
<*
@require math::is_power_of_2(alignment)
@require size > 0
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
*>
fn void*? DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @local
fn void*? _alloc_new(DynamicArenaAllocator* self, usz size, usz alignment) @local
{
// First, make sure that we can align it, extending the page size if needed.
usz page_size = max(self.page_size, mem::aligned_offset(size + DynamicArenaChunk.sizeof + alignment, alignment));
@@ -166,57 +221,4 @@ fn void*? DynamicArenaAllocator._alloc_new(&self, usz size, usz alignment) @loca
self.page = page;
page.current_stack_ptr = mem_start;
return mem_start;
}
<*
@require size > 0 : `acquire expects size > 0`
@require !alignment || math::is_power_of_2(alignment)
@return? mem::INVALID_ALLOC_SIZE, mem::OUT_OF_MEMORY
*>
fn void*? DynamicArenaAllocator.acquire(&self, usz size, AllocInitType init_type, usz alignment) @dynamic
{
alignment = alignment_for_allocation(alignment);
DynamicArenaPage* page = self.page;
void* ptr @noinit;
do SET_DONE:
{
if (!page && self.unused_page)
{
self.page = page = self.unused_page;
self.unused_page = page.prev_arena;
page.prev_arena = null;
}
if (!page)
{
ptr = self._alloc_new(size, alignment)!;
break SET_DONE;
}
void* start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
usz new_used = start - page.memory + size;
if ALLOCATE_NEW: (new_used > page.total)
{
if ((page = self.unused_page))
{
start = mem::aligned_pointer(page.memory + page.used + DynamicArenaChunk.sizeof, alignment);
new_used = start + size - page.memory;
if (page.total >= new_used)
{
self.unused_page = page.prev_arena;
page.prev_arena = self.page;
self.page = page;
break ALLOCATE_NEW;
}
}
ptr = self._alloc_new(size, alignment)!;
break SET_DONE;
}
page.used = new_used;
assert(start + size == page.memory + page.used);
ptr = start;
DynamicArenaChunk* chunk = (DynamicArenaChunk*)ptr - 1;
chunk.size = size;
};
if (init_type == ZERO) mem::clear(ptr, size, mem::DEFAULT_MEM_ALIGNMENT);
return ptr;
}
}

View File

@@ -32,58 +32,58 @@ fn void*? SimpleHeapAllocator.acquire(&self, usz size, AllocInitType init_type,
{
if (init_type == ZERO)
{
return alignment > 0 ? @aligned_alloc(self._calloc, size, alignment) : self._calloc(size);
return alignment > 0 ? @aligned_alloc_fn(self, simple_alloc_calloc, size, alignment) : simple_alloc_calloc(self, size);
}
return alignment > 0 ? @aligned_alloc(self._alloc, size, alignment) : self._alloc(size);
return alignment > 0 ? @aligned_alloc_fn(self, simple_alloc_alloc, size, alignment) : simple_alloc_alloc(self, size);
}
fn void*? SimpleHeapAllocator.resize(&self, void* old_pointer, usz size, usz alignment) @dynamic
{
return alignment > 0
? @aligned_realloc(self._calloc, self._free, old_pointer, size, alignment)
: self._realloc(old_pointer, size);
? @aligned_realloc_fn(self, simple_alloc_calloc, simple_alloc_free, old_pointer, size, alignment)
: simple_alloc_realloc(self, old_pointer, size);
}
fn void SimpleHeapAllocator.release(&self, void* old_pointer, bool aligned) @dynamic
{
if (aligned)
{
@aligned_free(self._free, old_pointer)!!;
@aligned_free_fn(self, simple_alloc_free, old_pointer)!!;
}
else
{
self._free(old_pointer);
simple_alloc_free(self, old_pointer);
}
}
<*
@require old_pointer && bytes > 0
*>
fn void*? SimpleHeapAllocator._realloc(&self, void* old_pointer, usz bytes) @local
fn void*? simple_alloc_realloc(SimpleHeapAllocator* self, void* old_pointer, usz bytes) @local
{
// Find the block header.
Header* block = (Header*)old_pointer - 1;
if (block.size >= bytes) return old_pointer;
void* new = self._alloc(bytes)!;
void* new = simple_alloc_alloc(self, bytes)!;
usz max_to_copy = math::min(block.size, bytes);
mem::copy(new, old_pointer, max_to_copy);
self._free(old_pointer);
simple_alloc_free(self, old_pointer);
return new;
}
fn void*? SimpleHeapAllocator._calloc(&self, usz bytes) @local
fn void*? simple_alloc_calloc(SimpleHeapAllocator* self, usz bytes) @local
{
void* data = self._alloc(bytes)!;
void* data = simple_alloc_alloc(self, bytes)!;
mem::clear(data, bytes, mem::DEFAULT_MEM_ALIGNMENT);
return data;
}
fn void*? SimpleHeapAllocator._alloc(&self, usz bytes) @local
fn void*? simple_alloc_alloc(SimpleHeapAllocator* self, usz bytes) @local
{
usz aligned_bytes = mem::aligned_offset(bytes, mem::DEFAULT_MEM_ALIGNMENT);
if (!self.free_list)
{
self.add_block(aligned_bytes)!;
simple_alloc_add_block(self, aligned_bytes)!;
}
Header* current = self.free_list;
@@ -123,22 +123,22 @@ fn void*? SimpleHeapAllocator._alloc(&self, usz bytes) @local
current = current.next;
}
}
self.add_block(aligned_bytes)!;
return self._alloc(aligned_bytes);
simple_alloc_add_block(self, aligned_bytes)!;
return simple_alloc_alloc(self, aligned_bytes);
}
fn void? SimpleHeapAllocator.add_block(&self, usz aligned_bytes) @local
fn void? simple_alloc_add_block(SimpleHeapAllocator* self, usz aligned_bytes) @local
{
assert(mem::aligned_offset(aligned_bytes, mem::DEFAULT_MEM_ALIGNMENT) == aligned_bytes);
char[] result = self.alloc_fn(aligned_bytes + Header.sizeof)!;
Header* new_block = (Header*)result.ptr;
new_block.size = result.len - Header.sizeof;
new_block.next = null;
self._free(new_block + 1);
simple_alloc_free(self, new_block + 1);
}
fn void SimpleHeapAllocator._free(&self, void* ptr) @local
fn void simple_alloc_free(SimpleHeapAllocator* self, void* ptr) @local
{
// Empty ptr -> do nothing.
if (!ptr) return;

View File

@@ -127,7 +127,7 @@ fn void TempAllocator.reset(&self)
{
TempAllocator* old = child;
child = old.derived;
old.destroy();
temp_allocator_destroy(old);
}
self.capacity = self.original_capacity;
$if env::ADDRESS_SANITIZER:
@@ -142,17 +142,17 @@ fn void TempAllocator.reset(&self)
fn void TempAllocator.free(&self)
{
self.reset();
self.destroy();
temp_allocator_destroy(self);
}
fn void TempAllocator.destroy(&self) @local
fn void temp_allocator_destroy(TempAllocator* self)
{
TempAllocatorPage *last_page = self.last_page;
while (last_page)
{
TempAllocatorPage *to_free = last_page;
last_page = last_page.prev_page;
self._free_page(to_free)!!;
_free_page(self, to_free)!!;
}
if (self.allocated)
{
@@ -179,33 +179,6 @@ fn void TempAllocator.release(&self, void* old_pointer, bool) @dynamic
}
fn void? TempAllocator._free_page(&self, TempAllocatorPage* page) @inline @local
{
void* mem = page.start;
return self.backing_allocator.release(mem, page.is_aligned());
}
fn void*? TempAllocator._realloc_page(&self, TempAllocatorPage* page, usz size, usz alignment) @inline @local
{
// Then the actual start pointer:
void* real_pointer = page.start;
// Walk backwards to find the pointer to this page.
TempAllocatorPage **pointer_to_prev = &self.last_page;
// Remove the page from the list
while (*pointer_to_prev != page)
{
pointer_to_prev = &((*pointer_to_prev).prev_page);
}
*pointer_to_prev = page.prev_page;
usz page_size = page.pagesize();
// Clear on size > original size.
void* data = self.acquire(size, NO_ZERO, alignment)!;
if (page_size > size) page_size = size;
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
self.backing_allocator.release(real_pointer, page.is_aligned());
return data;
}
fn void*? TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @dynamic
{
@@ -215,7 +188,7 @@ fn void*? TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @d
assert(self.last_page, "Realloc of non temp pointer");
// First grab the page
TempAllocatorPage *page = pointer - TempAllocatorPage.sizeof;
return self._realloc_page(page, size, alignment);
return _realloc_page(self, page, size, alignment);
}
bool is_realloc_of_last = chunk.size + pointer == &self.data[self.used];
if (is_realloc_of_last)
@@ -326,9 +299,39 @@ fn void*? TempAllocator.acquire(&self, usz size, AllocInitType init_type, usz al
return &page.data[0];
}
fn void? _free_page(TempAllocator* self, TempAllocatorPage* page) @inline @local
{
void* mem = page.start;
return self.backing_allocator.release(mem, page.is_aligned());
}
fn void*? _realloc_page(TempAllocator* self, TempAllocatorPage* page, usz size, usz alignment) @inline @local
{
// Then the actual start pointer:
void* real_pointer = page.start;
// Walk backwards to find the pointer to this page.
TempAllocatorPage **pointer_to_prev = &self.last_page;
// Remove the page from the list
while (*pointer_to_prev != page)
{
pointer_to_prev = &((*pointer_to_prev).prev_page);
}
*pointer_to_prev = page.prev_page;
usz page_size = page.pagesize();
// Clear on size > original size.
void* data = self.acquire(size, NO_ZERO, alignment)!;
if (page_size > size) page_size = size;
mem::copy(data, &page.data[0], page_size, mem::DEFAULT_MEM_ALIGNMENT, mem::DEFAULT_MEM_ALIGNMENT);
self.backing_allocator.release(real_pointer, page.is_aligned());
return data;
}
module std::core::mem::allocator @if((env::POSIX || env::WIN32) && $feature(VMEM_TEMP));
import std::math;
tlocal VmemOptions temp_allocator_default_options = {
.shrink_on_reset = env::MEMORY_ENV != NORMAL,
.protect_unused_pages = env::COMPILER_OPT_LEVEL <= O1 || env::COMPILER_SAFE_MODE,
@@ -383,10 +386,10 @@ fn void TempAllocator.reset(&self)
}
fn void TempAllocator.free(&self)
{
self.destroy();
_destroy(self);
}
fn void TempAllocator.destroy(&self) @local
fn void _destroy(TempAllocator* self) @local
{
TempAllocator* child = self.derived;
if (!child) return;
@@ -403,4 +406,4 @@ fn void*? TempAllocator.resize(&self, void* pointer, usz size, usz alignment) @d
fn void TempAllocator.release(&self, void* old_pointer, bool b) @dynamic
{
self.vmem.release(old_pointer, b) @inline;
}
}

View File

@@ -1,7 +1,7 @@
module std::core::string::ansi;
import std::io;
const enum Ansi : inline String
constdef Ansi : inline String
{
RESET = "\e[0m",
BOLD = "\e[1m",

View File

@@ -631,7 +631,7 @@ fn void DString.reverse(self)
}
}
fn StringData* DString.data(self) @inline @private
fn StringData* DString.data(self) @inline
{
return (StringData*)self;
}
@@ -658,9 +658,10 @@ fn usz? DString.read_from_stream(&self, InStream reader)
if (&reader.available)
{
usz total_read = 0;
while (usz available = reader.available()!)
while (ulong available = reader.available()!)
{
self.reserve(available);
if (available > isz.max) available = (ulong)isz.max;
self.reserve((usz)available);
StringData* data = self.data();
usz len = reader.read(data.chars[data.len..(data.capacity - 1)])!;
total_read += len;

View File

@@ -126,6 +126,7 @@ const bool ARCH_64_BIT = $$REGISTER_SIZE == 64;
const bool LIBC = $$COMPILER_LIBC_AVAILABLE;
const bool NO_LIBC = !LIBC && !CUSTOM_LIBC;
const bool CUSTOM_LIBC = $$CUSTOM_LIBC;
const bool OLD_IO = $feature(OLD_IO);
const CompilerOptLevel COMPILER_OPT_LEVEL = CompilerOptLevel.from_ordinal($$COMPILER_OPT_LEVEL);
const bool BIG_ENDIAN = $$PLATFORM_BIG_ENDIAN;
const bool I128_NATIVE_SUPPORT = $$PLATFORM_I128_SUPPORTED;

View File

@@ -404,6 +404,28 @@ macro void*? @aligned_alloc(#alloc_fn, usz bytes, usz alignment)
return mem;
}
<*
@require bytes > 0
@require alignment > 0
@require bytes <= isz.max
*>
macro void*? @aligned_alloc_fn(context, #alloc_fn, usz bytes, usz alignment)
{
if (alignment < void*.alignof) alignment = void*.alignof;
usz header = AlignedBlock.sizeof + alignment;
usz alignsize = bytes + header;
$if $kindof(#alloc_fn(context, bytes)) == OPTIONAL:
void* data = #alloc_fn(context, alignsize)!;
$else
void* data = #alloc_fn(context, alignsize);
$endif
void* mem = mem::aligned_pointer(data + AlignedBlock.sizeof, alignment);
AlignedBlock* desc = (AlignedBlock*)mem - 1;
assert(mem > data);
*desc = { bytes, data };
return mem;
}
struct AlignedBlock
{
usz len;
@@ -420,6 +442,16 @@ macro void? @aligned_free(#free_fn, void* old_pointer)
$endif
}
macro void? @aligned_free_fn(context, #free_fn, void* old_pointer)
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
$if $kindof(#free_fn(context, desc.start)) == OPTIONAL:
#free_fn(context, desc.start)!;
$else
#free_fn(context, desc.start);
$endif
}
<*
@require bytes > 0
@require alignment > 0
@@ -438,6 +470,23 @@ macro void*? @aligned_realloc(#calloc_fn, #free_fn, void* old_pointer, usz bytes
return new_data;
}
<*
@require bytes > 0
@require alignment > 0
*>
macro void*? @aligned_realloc_fn(context, #calloc_fn, #free_fn, void* old_pointer, usz bytes, usz alignment)
{
AlignedBlock* desc = (AlignedBlock*)old_pointer - 1;
void* data_start = desc.start;
void* new_data = @aligned_alloc_fn(context, #calloc_fn, bytes, alignment)!;
mem::copy(new_data, old_pointer, desc.len < bytes ? desc.len : bytes, 1, 1);
$if $kindof(#free_fn(context, data_start)) == OPTIONAL:
#free_fn(context, data_start)!;
$else
#free_fn(context, data_start);
$endif
return new_data;
}
// All allocators
alias mem @builtin = thread_allocator ;

View File

@@ -54,7 +54,7 @@ struct FixedBlockPool
@require calculate_actual_capacity(capacity, block_size) * block_size >= block_size
: "Total memory would overflow"
*>
macro FixedBlockPool* FixedBlockPool.init(&self, Allocator allocator, usz block_size, usz capacity = INITIAL_CAPACITY, usz alignment = 0)
fn FixedBlockPool* FixedBlockPool.init(&self, Allocator allocator, usz block_size, usz capacity = INITIAL_CAPACITY, usz alignment = 0)
{
self.allocator = allocator;
self.tail = &self.head;
@@ -64,7 +64,7 @@ macro FixedBlockPool* FixedBlockPool.init(&self, Allocator allocator, usz block_
self.alignment = allocator::alignment_for_allocation(alignment);
self.page_size = capacity * self.block_size;
assert(self.page_size >= self.block_size, "Total memory would overflow %d %d", block_size, capacity);
self.head.buffer = self.allocate_page();
self.head.buffer = fixedblockpool_allocate_page(self);
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
asan::poison_memory_region(self.head.buffer, self.page_size);
$endif
@@ -119,7 +119,7 @@ fn void FixedBlockPool.free(&self)
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
asan::unpoison_memory_region(self.head.buffer, self.page_size);
$endif
self.free_page(self.head.buffer);
fixedblockpool_free_page(self, self.head.buffer);
FixedBlockPoolNode* iter = self.head.next;
while (iter)
@@ -127,7 +127,7 @@ fn void FixedBlockPool.free(&self)
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
asan::unpoison_memory_region(iter.buffer, self.page_size);
$endif
self.free_page(iter.buffer);
fixedblockpool_free_page(self, iter.buffer);
FixedBlockPoolNode* current = iter;
iter = iter.next;
allocator::free(self.allocator, current);
@@ -158,7 +158,7 @@ fn void* FixedBlockPool.alloc(&self)
}
void* end = self.tail.buffer + (self.tail.capacity * self.block_size);
if (self.next_free >= end) self.new_node();
if (self.next_free >= end) fixedblockpool_new_node(self);
void* ptr = self.next_free;
self.next_free += self.block_size;
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
@@ -172,7 +172,7 @@ fn void* FixedBlockPool.alloc(&self)
Deallocate a block from the block pool
@require self.initialized : "The block pool must be initialized"
@require self.check_ptr(ptr) : "The pointer should be part of the pool"
@require fixedblockpool_check_ptr(self, ptr) : "The pointer should be part of the pool"
*>
fn void FixedBlockPool.dealloc(&self, void* ptr)
{
@@ -193,7 +193,7 @@ fn void FixedBlockPool.dealloc(&self, void* ptr)
<*
@require self.initialized : "The block pool must be initialized"
*>
fn bool FixedBlockPool.check_ptr(&self, void *ptr) @local
fn bool fixedblockpool_check_ptr(FixedBlockPool* self, void *ptr) @local
{
FixedBlockPoolNode* iter = &self.head;
@@ -210,10 +210,10 @@ fn bool FixedBlockPool.check_ptr(&self, void *ptr) @local
<*
@require self.grow_capacity > 0 : "How many blocks will it store"
*>
fn void FixedBlockPool.new_node(&self) @local
fn void fixedblockpool_new_node(FixedBlockPool* self) @local
{
FixedBlockPoolNode* node = allocator::new(self.allocator, FixedBlockPoolNode);
node.buffer = self.allocate_page();
node.buffer = fixedblockpool_allocate_page(self);
$if env::COMPILER_SAFE_MODE && env::ADDRESS_SANITIZER:
asan::poison_memory_region(node.buffer, self.page_size);
$endif
@@ -224,14 +224,14 @@ fn void FixedBlockPool.new_node(&self) @local
self.allocated += node.capacity;
}
macro void* FixedBlockPool.allocate_page(&self) @private
macro void* fixedblockpool_allocate_page(FixedBlockPool* self) @private
{
return self.alignment > mem::DEFAULT_MEM_ALIGNMENT
? allocator::calloc_aligned(self.allocator, self.page_size, self.alignment)!!
: allocator::calloc(self.allocator, self.page_size);
}
macro void FixedBlockPool.free_page(&self, void* page) @private
macro void fixedblockpool_free_page(FixedBlockPool* self, void* page) @private
{
if (self.alignment > mem::DEFAULT_MEM_ALIGNMENT)
{

View File

@@ -321,7 +321,7 @@ fn void? VirtualMemory.destroy(&self)
return release(self.ptr, self.size);
}
fn CInt VirtualMemoryAccess.to_posix(self) @if(env::POSIX) @private
fn CInt VirtualMemoryAccess.to_posix(self) @if(env::POSIX)
{
switch (self)
{
@@ -336,7 +336,7 @@ fn CInt VirtualMemoryAccess.to_posix(self) @if(env::POSIX) @private
}
}
fn Win32_Protect VirtualMemoryAccess.to_win32(self) @if(env::WIN32) @private
fn Win32_Protect VirtualMemoryAccess.to_win32(self) @if(env::WIN32)
{
switch (self)
{

View File

@@ -125,10 +125,11 @@ fn bool run_benchmarks(BenchmarkUnit[] benchmarks)
char[] perc_str = { [0..19] = ' ', [20] = 0 };
int perc = 0;
uint print_step = current_benchmark_iterations / 100;
if (print_step == 0) print_step = 1;
for (this_iteration = 0; this_iteration < current_benchmark_iterations; ++this_iteration, benchmark_nano_seconds = {})
{
if (0 == this_iteration % print_step) // only print right about when the % will update
if (this_iteration % print_step == 0) // only print right about when the % will update
{
perc_str[0..(uint)math::floor((this_iteration / (float)current_benchmark_iterations) * 20)] = '#';
perc = (uint)math::ceil(100 * (this_iteration / (float)current_benchmark_iterations));

View File

@@ -142,7 +142,7 @@ fn void mute_output() @local
File* stderr = io::stderr();
*stderr = test_context.fake_stdout;
*stdout = test_context.fake_stdout;
(void)test_context.fake_stdout.seek(0, Seek.SET)!!;
(void)test_context.fake_stdout.set_cursor(0)!!;
}
fn void unmute_output(bool has_error) @local
@@ -155,7 +155,7 @@ fn void unmute_output(bool has_error) @local
*stderr = test_context.stored.stderr;
*stdout = test_context.stored.stdout;
usz log_size = test_context.fake_stdout.seek(0, Seek.CURSOR)!!;
ulong log_size = test_context.fake_stdout.cursor()!!;
if (has_error)
{
io::printn(test_context.has_ansi_codes ? "[\e[0;31mFAIL\e[0m]" : "[FAIL]");
@@ -165,7 +165,7 @@ fn void unmute_output(bool has_error) @local
{
test_context.fake_stdout.write_byte('\n')!!;
test_context.fake_stdout.write_byte('\0')!!;
(void)test_context.fake_stdout.seek(0, Seek.SET)!!;
test_context.fake_stdout.set_cursor(0)!!;
io::printfn("\n========== TEST LOG ============");
io::printfn("%s\n", test_context.current_test_name);

View File

@@ -97,8 +97,8 @@ macro bool is_subtype_of($Type, $OtherType)
macro bool is_numerical($Type)
{
$switch $Type.kindof:
$case DISTINCT:
$case CONST_ENUM:
$case TYPEDEF:
$case CONSTDEF:
return is_numerical($Type.inner);
$case SIGNED_INT:
$case UNSIGNED_INT:
@@ -170,7 +170,7 @@ macro bool is_unsigned($Type) @const
macro typeid flat_type($Type) @const
{
$if $Type.kindof == DISTINCT || $Type.kindof == CONST_ENUM:
$if $Type.kindof == TYPEDEF || $Type.kindof == CONSTDEF:
return flat_type($Type.inner);
$else
return $Type.typeid;
@@ -179,7 +179,7 @@ macro typeid flat_type($Type) @const
macro TypeKind flat_kind($Type) @const
{
$if $Type.kindof == DISTINCT || $Type.kindof == CONST_ENUM:
$if $Type.kindof == TYPEDEF || $Type.kindof == CONSTDEF:
return flat_type($Type.inner);
$else
return $Type.kindof;
@@ -203,8 +203,8 @@ macro bool is_flat_intlike($Type) @const
$case UNSIGNED_INT:
return true;
$case VECTOR:
$case DISTINCT:
$case CONST_ENUM:
$case TYPEDEF:
$case CONSTDEF:
return is_flat_intlike($Type.inner);
$default:
return false;
@@ -230,7 +230,7 @@ macro bool is_underlying_int($Type) @const
$case SIGNED_INT:
$case UNSIGNED_INT:
return true;
$case DISTINCT:
$case TYPEDEF:
return is_underlying_int($Type.inner);
$default:
return false;
@@ -258,7 +258,7 @@ macro bool is_vector($Type) @const
macro typeid inner_type($Type) @const
{
$if $Type.kindof == DISTINCT || $Type.kindof == CONST_ENUM:
$if $Type.kindof == TYPEDEF || $Type.kindof == CONSTDEF:
return inner_type($Type.inner);
$else
return $Type.typeid;
@@ -298,7 +298,7 @@ macro bool may_load_atomic($Type) @const
$case POINTER:
$case FLOAT:
return true;
$case DISTINCT:
$case TYPEDEF:
$case ENUM:
return may_load_atomic($Type.inner);
$default:
@@ -313,8 +313,8 @@ macro lower_to_atomic_compatible_type($Type) @const
$case SIGNED_INT:
$case UNSIGNED_INT:
return $Type.typeid;
$case DISTINCT:
$case CONST_ENUM:
$case TYPEDEF:
$case CONSTDEF:
return lower_to_atomic_compatible_type($Type.inner);
$case FLOAT:
$switch $Type:
@@ -379,6 +379,9 @@ macro bool @comparable_value(#value) @const
$endif
}
const CONST_ENUM @builtin @deprecated("Use TypeKind.CONSTDEF instead") = TypeKind.CONSTDEF;
const DISTINCT @builtin @deprecated("Use TypeKind.TYPEDEF instead") = TypeKind.TYPEDEF;
enum TypeKind : char
{
VOID,
@@ -390,7 +393,7 @@ enum TypeKind : char
FAULT,
ANY,
ENUM,
CONST_ENUM,
CONSTDEF,
STRUCT,
UNION,
BITSTRUCT,
@@ -399,7 +402,7 @@ enum TypeKind : char
ARRAY,
SLICE,
VECTOR,
DISTINCT,
TYPEDEF,
POINTER,
INTERFACE,
}

View File

@@ -63,7 +63,7 @@ macro quarter_round(uint* x, int a, int b, int c, int d) @local
}
<* Process the next (or final) chunk of ingested data. *>
fn void ChaCha20.mutate_keystream(&self) @local @inline
fn void chacha20_mutate_keystream(ChaCha20* self) @local @inline
{
self.key_stream[..] = self.state[..];
@@ -137,7 +137,7 @@ fn void ChaCha20.transform(&self, char[] data)
for (usz x = offset; data.len >= BLOCK_SIZE; data = data[BLOCK_SIZE..], x = offset)
{
self.mutate_keystream();
chacha20_mutate_keystream(self);
if (offset) foreach (i, &b : data[:offset]) *b ^= key_stream[i];
char[] aligned_data = data[offset..];
for (; x <= (BLOCK_SIZE - usz.sizeof); x += usz.sizeof)
@@ -150,7 +150,7 @@ fn void ChaCha20.transform(&self, char[] data)
// 3. Process any remaining bytes.
if (data.len > 0)
{
self.mutate_keystream();
chacha20_mutate_keystream(self);
for (usz i = 0; i < data.len; i++) data[i] ^= key_stream[i];
self.position = data.len;
}

View File

@@ -567,7 +567,7 @@ fn F25519Int F25519Int.inv(&s)
@param [&in] s
*>
fn F25519Int F25519Int.pow_2523(&s) @local
fn F25519Int pow_2523(F25519Int* s) @local
{
F25519Int r = *s;
@@ -587,7 +587,7 @@ fn F25519Int F25519Int.pow_2523(&s) @local
fn F25519Int F25519Int.sqrt(&s)
{
F25519Int twice = s.mul_s(2);
F25519Int pow = twice.pow_2523();
F25519Int pow = pow_2523(&twice);
return (twice * pow * pow - ONE) * s * pow;
}

View File

@@ -7,7 +7,7 @@ struct DelayedSchedulerEvent @local
Clock execution_time;
}
fn int DelayedSchedulerEvent.compare_to(self, DelayedSchedulerEvent other) @local
fn int DelayedSchedulerEvent.compare_to(self, DelayedSchedulerEvent other)
{
switch
{

View File

@@ -49,43 +49,43 @@ macro @round(r, m, $s, #v) @local
@g(r, m, $s, 7, #v[ 3], #v[ 4], #v[ 9], #v[14]);
}
macro @common_compress(#instance, $rounds, $iv, $sigma, block) @local
macro common_compress(instance, $rounds, $iv, $sigma, block) @local
{
$typeof(#instance.h[0])[16] m, v;
$typeof(instance.h[0])[16] m, v;
((char*)&m)[:$sizeof(block)] = block[..];
v[:8] = #instance.h[..];
v[:8] = instance.h[..];
v[ 8] = $iv[0];
v[ 9] = $iv[1];
v[10] = $iv[2];
v[11] = $iv[3];
v[12] = $iv[4] ^ #instance.t[0];
v[13] = $iv[5] ^ #instance.t[1];
v[14] = $iv[6] ^ #instance.f[0];
v[15] = $iv[7] ^ #instance.f[1];
v[12] = $iv[4] ^ instance.t[0];
v[13] = $iv[5] ^ instance.t[1];
v[14] = $iv[6] ^ instance.f[0];
v[15] = $iv[7] ^ instance.f[1];
$for usz $i = 0; $i < $rounds; $i++:
@round($i, m, $sigma, v);
$endfor
$for usz $i = 0; $i < 8; $i++:
#instance.h[$i] ^= v[$i] ^ v[$i + 8];
instance.h[$i] ^= v[$i] ^ v[$i + 8];
$endfor
}
macro @add_ctr(#instance, usz amount) @local
macro _add_ctr(instance, usz amount) @local
{
#instance.t[0] += ($typeof(#instance.t[0]))amount;
#instance.t[1] += ($typeof(#instance.t[0]))(#instance.t[0] < amount); // adds 1 on overflow of [0]
instance.t[0] += ($typeof(instance.t[0]))amount;
instance.t[1] += ($typeof(instance.t[0]))(instance.t[0] < amount); // adds 1 on overflow of [0]
}
macro @common_init(#instance, $ParamType, $iv, usz out_len, char[] key = {}, char[] salt = {}, char[] personal = {}) @local
macro common_init(instance, $ParamType, $iv, usz out_len, char[] key = {}, char[] salt = {}, char[] personal = {}) @local
{
mem::zero_volatile(@as_char_view(*#instance)); // explicitly because habits around hash init usually involve @noinit
mem::zero_volatile(@as_char_view(*instance)); // explicitly because habits around hash init usually involve @noinit
#instance.h[..] = $iv[..];
#instance.outlen = out_len;
instance.h[..] = $iv[..];
instance.outlen = out_len;
$ParamType p = {
.digest_length = (char)out_len,
@@ -96,59 +96,60 @@ macro @common_init(#instance, $ParamType, $iv, usz out_len, char[] key = {}, cha
if (salt.len) p.salt[:salt.len] = salt[..];
if (personal.len) p.personal[:personal.len] = personal[..];
array::@zip_into(((char*)&#instance.h)[:$sizeof(p)], ((char*)&p)[:$sizeof(p)], fn (a, b) => a ^ b); // bytes(self.h) ^= bytes(p)
array::@zip_into(((char*)&instance.h)[:$sizeof(p)], ((char*)&p)[:$sizeof(p)], fn (a, b) => a ^ b); // bytes(self.h) ^= bytes(p)
if (key.len)
{
char[$sizeof($iv[0])*16] dummy = {};
dummy[:key.len] = key[..];
#instance.update(dummy[..]); // consume a FULL block
instance.update(dummy[..]); // consume a FULL block
mem::zero_volatile(dummy[..]); // do not optimize clearing this from the stack
}
}
macro @common_update(#instance, $block_size, char[] data) @local
macro common_update(instance, $block_size, char[] data) @local
{
if (@unlikely(!data.len)) return;
usz fill = $block_size - #instance.buflen;
usz fill = $block_size - instance.buflen;
if (data.len > fill)
{
#instance.buf[#instance.buflen:fill] = data[:fill];
#instance.buflen = 0;
instance.buf[instance.buflen:fill] = data[:fill];
instance.buflen = 0;
@add_ctr(#instance, $block_size);
#instance.compress(#instance.buf);
_add_ctr(instance, $block_size);
instance._compress(instance.buf);
data = data[fill..];
for (; data.len > $block_size; data = data[$block_size..])
{
@add_ctr(#instance, $block_size);
#instance.compress(data[:$block_size]);
_add_ctr(instance, $block_size);
instance._compress(data[:$block_size]);
}
}
#instance.buf[#instance.buflen:data.len] = data[..];
#instance.buflen += data.len;
instance.buf[instance.buflen:data.len] = data[..];
instance.buflen += data.len;
}
macro @common_final(#instance, $output_length) @local
macro common_final(instance, $output_length) @local
{
char[$output_length] result = {};
if ($output_length != #instance.outlen) return result;
if ($output_length != instance.outlen) return result;
@add_ctr(#instance, #instance.buflen);
if (#instance.f[0]) return result; // technically an error return
_add_ctr(instance, instance.buflen);
if (instance.f[0]) return result; // technically an error return
if (#instance.last_node) #instance.f[1] = $typeof(#instance.h[0]).max;
#instance.f[0] = $typeof(#instance.h[0]).max;
var $max = $typeof(instance.h[0]).max;
if (instance.last_node) instance.f[1] = $max;
instance.f[0] = $max;
mem::zero_volatile(#instance.buf[#instance.buflen..]); // pad buffer with zeroes
#instance.compress(#instance.buf);
mem::zero_volatile(instance.buf[instance.buflen..]); // pad buffer with zeroes
instance._compress(instance.buf);
defer mem::zero_volatile(@as_char_view(*#instance)); // destroy the current context implicitly
defer mem::zero_volatile(@as_char_view(*instance)); // destroy the current context implicitly
result[:#instance.outlen] = @as_char_view(#instance.h)[:#instance.outlen];
result[:instance.outlen] = @as_char_view(instance.h)[:instance.outlen];
return result;
}
@@ -244,13 +245,13 @@ alias b_512 = blake2b_512;
@require !personal.ptr || (personal.len > 0 && personal.len <= BLAKE2B_PERSONALBYTES) : "A specified personalization's length must be within the proper range."
*>
fn void Blake2b.init(&self, usz out_len, char[] key = {}, char[] salt = {}, char[] personal = {})
=> @common_init(self, Blake2bParam, BLAKE2B_IV, out_len, key, salt, personal);
=> common_init(self, Blake2bParam, BLAKE2B_IV, out_len, key, salt, personal);
<*
Core compression inline function for Blake2b.
*>
fn void Blake2b.compress(&self, char[BLAKE2B_BLOCKBYTES] block) @local @inline
=> @common_compress(self, 12, BLAKE2B_IV, BLAKE2B_SIGMA, block);
fn void Blake2b._compress(&self, char[BLAKE2B_BLOCKBYTES] block) @inline
=> common_compress(self, 12, BLAKE2B_IV, BLAKE2B_SIGMA, block);
<*
Add more data to the hash context or stream.
@@ -258,7 +259,7 @@ fn void Blake2b.compress(&self, char[BLAKE2B_BLOCKBYTES] block) @local @inline
@param[in] data : "The data to ingest into the hash context."
*>
fn void Blake2b.update(&self, char[] data)
=> @common_update(self, BLAKE2B_BLOCKBYTES, data);
=> common_update(self, BLAKE2B_BLOCKBYTES, data);
<*
Finalize the hash context and return the hash result at the given size.
@@ -267,9 +268,12 @@ fn void Blake2b.update(&self, char[] data)
@require $output_length == self.outlen : "The specified compile-time output size MUST be equal to the initialized output size."
*>
macro char[*] Blake2b.final(&self, $output_length)
=> @common_final(self, $output_length);
macro char[*] Blake2b.final(&self, $output_length) => _blake2b_final{$output_length}(self);
fn char[OUTPUT_LENGTH] _blake2b_final(Blake2b* self) <OUTPUT_LENGTH> @local
{
return common_final(self, OUTPUT_LENGTH);
}
// ======================================================================================
// BEGIN Blake2s contents. Do not separate this from Blake2b: there's not really a point.
@@ -356,13 +360,13 @@ alias s_256 = blake2s_256;
@require !personal.ptr || (personal.len > 0 && personal.len <= BLAKE2B_PERSONALBYTES) : "A specified personalization's length must be within the proper range."
*>
fn void Blake2s.init(&self, usz out_len, char[] key = {}, char[] salt = {}, char[] personal = {})
=> @common_init(self, Blake2sParam, BLAKE2S_IV, out_len, key, salt, personal);
=> common_init(self, Blake2sParam, BLAKE2S_IV, out_len, key, salt, personal);
<*
Core compression inline function for Blake2s.
*>
fn void Blake2s.compress(&self, char[BLAKE2S_BLOCKBYTES] block) @local @inline
=> @common_compress(self, 10, BLAKE2S_IV, BLAKE2S_SIGMA, block);
fn void Blake2s._compress(&self, char[BLAKE2S_BLOCKBYTES] block) @inline
=> common_compress(self, 10, BLAKE2S_IV, BLAKE2S_SIGMA, block);
<*
Add more data to the hash context or stream.
@@ -370,7 +374,7 @@ fn void Blake2s.compress(&self, char[BLAKE2S_BLOCKBYTES] block) @local @inline
@param[in] data : "The data to ingest into the hash context."
*>
fn void Blake2s.update(&self, char[] data)
=> @common_update(self, BLAKE2S_BLOCKBYTES, data);
=> common_update(self, BLAKE2S_BLOCKBYTES, data);
<*
Finalize the hash context and return the hash result at the given size.
@@ -380,4 +384,4 @@ fn void Blake2s.update(&self, char[] data)
@require $output_length == self.outlen : "The specified compile-time output size MUST be equal to the initialized output size."
*>
macro char[*] Blake2s.final(&self, $output_length)
=> @common_final(self, $output_length);
=> common_final(self, $output_length);

View File

@@ -91,7 +91,7 @@ macro @simd_degree() @local
}
<* Flags used during hash computation based on its state. *>
const enum Blake3Flags : inline char
constdef Blake3Flags : inline char
{
CHUNK_START = 1 << 0,
CHUNK_END = 1 << 1,
@@ -246,7 +246,7 @@ fn void Blake3.init(&self, char[] key = {}, char explicit_flags = 0)
<*
Reset the state of the hashing context, in case it should be reused without reloading the key value.
*>
fn void Blake3.reset(&self) @local @inline
fn void _reset(Blake3* self) @local @inline
{
self.chunk.reset(self.key[..], 0);
self.cv_stack_len = 0;
@@ -255,7 +255,7 @@ fn void Blake3.reset(&self) @local @inline
<*
Private function to merge tree results.
*>
fn void Blake3.merge_cv_stack(&self, ulong total_len) @local @inline
fn void _merge_cv_stack(Blake3* self, ulong total_len) @local @inline
{
usz post_merge_stack_len = (usz)@popcnt(total_len);
for (; self.cv_stack_len > post_merge_stack_len; self.cv_stack_len--)
@@ -269,9 +269,9 @@ fn void Blake3.merge_cv_stack(&self, ulong total_len) @local @inline
<*
Private function to add a new tree onto the stack.
*>
fn void Blake3.push_cv(&self, char* new_cv, ulong chunk_counter) @local @inline
fn void Blake3.push_cv(&self, char* new_cv, ulong chunk_counter) @inline
{
self.merge_cv_stack(chunk_counter);
_merge_cv_stack(self, chunk_counter);
self.cv_stack[self.cv_stack_len * OUT_SIZE : OUT_SIZE] = new_cv[:OUT_SIZE];
self.cv_stack_len++;
}
@@ -334,7 +334,7 @@ fn void Blake3.update(&self, char[] input, bool use_tbb = false)
if (input.len > 0)
{
self.chunk.update(input);
self.merge_cv_stack(self.chunk.chunk_counter);
_merge_cv_stack(self, self.chunk.chunk_counter);
}
}
@@ -400,7 +400,7 @@ fn void Blake3.destroy(&self) @inline
@param [in] key
@param flags
*>
fn void Blake3ChunkState.init(&self, uint[] key, char flags) @local @inline
fn void Blake3ChunkState.init(&self, uint[] key, char flags) @inline
{
mem::zero_volatile(@as_char_view(*self));
self.cv[..] = key[..];
@@ -413,7 +413,7 @@ fn void Blake3ChunkState.init(&self, uint[] key, char flags) @local @inline
@param [in] key
@param chunk_counter
*>
fn void Blake3ChunkState.reset(&self, uint[] key, ulong chunk_counter) @local @inline
fn void Blake3ChunkState.reset(&self, uint[] key, ulong chunk_counter) @inline
{
self.init(key, self.flags); // maintain its own flags
self.chunk_counter = chunk_counter; // update chunk counter
@@ -422,7 +422,7 @@ fn void Blake3ChunkState.reset(&self, uint[] key, ulong chunk_counter) @local @i
<*
Get bytes length of consumed data.
*>
fn usz Blake3ChunkState.len(&self) @operator(len) @local @inline
fn usz Blake3ChunkState.len(&self) @operator(len) @inline
=> (BLOCK_SIZE * (usz)self.blocks_compressed) + (usz)self.buf_len;
<*
@@ -430,7 +430,7 @@ fn usz Blake3ChunkState.len(&self) @operator(len) @local @inline
@param [in] data : "Data to ingest."
*>
fn usz Blake3ChunkState.fill_buf(&self, char[] data) @local @inline
fn usz Blake3ChunkState.fill_buf(&self, char[] data) @inline
{
usz take = min(BLOCK_SIZE - (usz)self.buf_len, data.len);
self.buf[self.buf_len:take] = data[:take];
@@ -441,7 +441,7 @@ fn usz Blake3ChunkState.fill_buf(&self, char[] data) @local @inline
<*
Determine whether to set the CHUNK_START flag.
*>
fn char Blake3ChunkState.maybe_start_flag(&self) @local @inline
fn char Blake3ChunkState.maybe_start_flag(&self) @inline
=> !self.blocks_compressed ? Blake3Flags.CHUNK_START : 0;
<*
@@ -449,7 +449,7 @@ fn char Blake3ChunkState.maybe_start_flag(&self) @local @inline
@param [in] input : "Incoming bytes to update with."
*>
fn void Blake3ChunkState.update(&self, char[] input) @local
fn void Blake3ChunkState.update(&self, char[] input)
{
if (self.buf_len)
{
@@ -473,7 +473,7 @@ fn void Blake3ChunkState.update(&self, char[] input) @local
<*
Convert the chunk state to an "output" type with the right flags.
*>
fn Blake3Output Blake3ChunkState.output(&self) @local @inline
fn Blake3Output Blake3ChunkState.output(&self) @inline
=> make_output(self.cv[..], &self.buf, self.buf_len, self.chunk_counter, self.flags | self.maybe_start_flag() | Blake3Flags.CHUNK_END);
<*
@@ -511,7 +511,7 @@ macro Blake3Output parent_output(char* block, uint[] key, char flags) @local
@param [&inout] cv
*>
macro void Blake3Output.chaining_value(&self, char* cv) @local
macro void Blake3Output.chaining_value(&self, char* cv)
{
uint[KEY_SIZE_WORDS] cv_words;
cv_words[..] = self.input_cv[..];
@@ -525,7 +525,7 @@ macro void Blake3Output.chaining_value(&self, char* cv) @local
@param seek
@param [inout] into
*>
fn void Blake3Output.root_bytes(&self, usz seek, char[] into) @local
fn void Blake3Output.root_bytes(&self, usz seek, char[] into)
{
if (!into.len) return;

View File

@@ -8,7 +8,7 @@
module std::hash::streebog;
const enum StreebogLength : inline uint
constdef StreebogLength : inline uint
{
SIZE_256 = 32,
SIZE_512 = 64,
@@ -147,13 +147,13 @@ fn void Streebog.update(&self, char[] data)
macro char[*] Streebog.final(&self, StreebogLength $hash_size)
{
char[$hash_size] result;
self._final_private();
streebog_final_private(self);
defer mem::zero_volatile(@as_char_view(*self)); // implicitly clear the structure when finalized
result[..] = @as_char_view(self.h)[(BLOCK_SIZE - $hash_size)..];
return result;
}
fn void Streebog._final_private(&self) @local
fn void streebog_final_private(Streebog* self) @local
{
ulong[8] unprocessed_bits_count;
usz index = self.index >> 3;

View File

@@ -86,14 +86,14 @@ fn void Poly1305.update(&self, char[] input)
}
// ingest up to a block size to finish the partial, then advance the slice ptr
self.temp[self.num:rem] = input[:rem];
self.blocks(self.temp[..]);
_blocks(self, self.temp[..]);
input = input[rem..];
}
usz even_length = input.len - (input.len % BLOCK_SIZE);
if (even_length >= BLOCK_SIZE)
{
self.blocks(input[:even_length]); // consume blocks
_blocks(self, input[:even_length]); // consume blocks
input = input[even_length..]; // scroll to end (remainder)
}
@@ -109,7 +109,7 @@ fn char[TAG_SIZE] Poly1305.final(&self)
{
self.temp[self.num++] = 1; // partial blocks must end with 0x01
self.temp[self.num..] = {}; // explicit zeros on the rest
self.blocks(self.temp[..], 0); // chomp
_blocks(self, self.temp[..], 0); // chomp
}
uint128 t = (uint128)self.h[0] + 5;
@@ -135,7 +135,7 @@ fn char[TAG_SIZE] Poly1305.final(&self)
}
fn void Poly1305.blocks(&self, char[] input, ulong pad_bit = 1) @local
fn void _blocks(Poly1305* self, char[] input, ulong pad_bit = 1) @local
{
for (; input.len >= BLOCK_SIZE; input = input[BLOCK_SIZE..])
{

View File

@@ -68,18 +68,18 @@ fn void Sha256.update(&self, char[] data)
usz len = min(BLOCK_SIZE - buffer_pos, data.len);
self.buffer[buffer_pos:len] = data[:len];
data = data[len..];
if (buffer_pos + len == BLOCK_SIZE) self.transform();
if (buffer_pos + len == BLOCK_SIZE) _transform(self);
}
// When the data pointer is aligned, we can disregard unaligned loading in the `transform` macro.
// We do this here from the outer call to reduce the expense of checking alignment on every single block.
if (0 == (usz)data.ptr % usz.sizeof)
{
for (; data.len >= BLOCK_SIZE; data = data[BLOCK_SIZE..]) self.transform((uint*)data.ptr);
for (; data.len >= BLOCK_SIZE; data = data[BLOCK_SIZE..]) _transform(self, (uint*)data.ptr);
}
else
{
for (; data.len >= BLOCK_SIZE; data = data[BLOCK_SIZE..]) self.transform_unaligned((uint*)data.ptr);
for (; data.len >= BLOCK_SIZE; data = data[BLOCK_SIZE..]) _transform_unaligned(self, (uint*)data.ptr);
}
// Leftover data just gets stored away for the next update or final.
@@ -102,7 +102,7 @@ fn char[HASH_SIZE] Sha256.final(&self)
if (i > BLOCK_SIZE - 8)
{
self.buffer[i..] = 0x00;
self.transform();
_transform(self);
i = 0; // Reset buffer index after transformation
}
@@ -111,7 +111,7 @@ fn char[HASH_SIZE] Sha256.final(&self)
// Append the bitcount in big-endian format
*(ulong*)(&self.buffer[BLOCK_SIZE - 8]) = env::BIG_ENDIAN ??? self.bitcount : bswap(self.bitcount);
self.transform();
_transform(self);
// Convert state to the final hash
foreach (x, s : self.state) *(uint*)(&hash[x * uint.sizeof]) = env::BIG_ENDIAN ??? s : bswap(s);
@@ -121,10 +121,10 @@ fn char[HASH_SIZE] Sha256.final(&self)
// These wrappers are necessary to significantly reduce code generation from macro expansions.
// Note that transformations on `self.buffer` (when incoming == null) should always be aligned.
fn void Sha256.transform(&self, uint* incoming = null) @local @noinline => self.do_transform(incoming, true);
fn void Sha256.transform_unaligned(&self, uint* incoming = null) @local @noinline => self.do_transform(incoming, false);
fn void _transform(Sha256* self, uint* incoming = null) @local @noinline => _do_transform(self, incoming, true);
fn void _transform_unaligned(Sha256* self, uint* incoming = null) @local @noinline => _do_transform(self, incoming, false);
macro Sha256.do_transform(&self, uint* incoming = null, bool $aligned = true) @local
macro _do_transform(Sha256* self, uint* incoming = null, bool $aligned = true) @local
{
uint a, b, c, d, e, f, g, h, t1, t2 @noinit;
uint[64] m @noinit;

View File

@@ -100,7 +100,7 @@ fn void SipHash.update(&self, char[] data)
self.v[3] ^= self.m;
$for var $i = 0; $i < BLOCK_ROUNDS; ++$i : // unrolled loop
self.round();
siphash_round(self);
$endfor
self.v[0] ^= self.m;
@@ -123,7 +123,7 @@ fn OutType SipHash.final(&self)
$endif
$for var $i = 0; $i < FINALIZE_ROUNDS; ++$i : // unrolled loop
self.round();
siphash_round(self);
$endfor
$if OutType.typeid == ulong.typeid :
@@ -134,7 +134,7 @@ fn OutType SipHash.final(&self)
self.v[1] ^= 0xDD;
$for var $i = 0; $i < FINALIZE_ROUNDS; ++$i : // unrolled loop
self.round();
siphash_round(self);
$endfor
return lo | ((uint128)(self.v[0] ^ self.v[1] ^ self.v[2] ^ self.v[3]) << 64);
@@ -142,7 +142,7 @@ fn OutType SipHash.final(&self)
}
fn void SipHash.round(&self) @local
fn void siphash_round(SipHash* self) @local
{
self.v[0] += self.v[1];
self.v[1] = self.v[1].rotl(13);

View File

@@ -66,14 +66,14 @@ fn void Whirlpool.update(&self, char[] data)
// If the algorithm is finalized during a 'partial' block, it has its own padding to fill the remaining gap.
if (data.len < to_pad) return;
self.process_block(&self.block);
_process_block(self, &self.block);
data = data[to_pad..];
}
// Digest blocks wholesale.
while (data.len >= BLOCK_SIZE)
{
self.process_block(data);
_process_block(self, data);
data = (data.len > BLOCK_SIZE) ? data[BLOCK_SIZE..] : {};
}
@@ -100,7 +100,7 @@ fn char[HASH_SIZE] Whirlpool.final(&self)
{
if (remainder < 64) self.block[remainder..63] = 0x00;
self.process_block(&self.block);
_process_block(self, &self.block);
remainder = 0;
}
@@ -113,7 +113,7 @@ fn char[HASH_SIZE] Whirlpool.final(&self)
self.block_128[3] = $$bswap(self.counter_low << 3);
// Process the final block.
self.process_block(&self.block);
_process_block(self, &self.block);
// Each ulong in the resultant hash should be bit-swapped before the final return.
char[HASH_SIZE] hash @align(ulong.alignof);
@@ -153,7 +153,7 @@ const ulong[10] RC @private = {
};
const ROUNDS = 10;
fn void Whirlpool.process_block(&self, char* block) @local
fn void _process_block(Whirlpool* self, char* block) @local
{
ulong[2 * 8] k; // key
ulong[2 * 8] state; // state

View File

@@ -39,11 +39,16 @@ fn bool is_dir(String path)
return os::native_is_dir(path);
}
fn usz? get_size(String path)
fn ulong? get_size(String path)
{
return os::native_file_size(path);
}
fn void? set_modified_time(String path, Time_t time)
{
return os::native_set_modified_time(path, time);
}
fn void? delete(String filename)
{
return os::native_remove(filename) @inline;
@@ -63,10 +68,25 @@ fn void? File.reopen(&self, String filename, String mode)
*>
fn usz? File.seek(&self, isz offset, Seek seek_mode = Seek.SET) @dynamic
{
os::native_fseek(self.file, offset, seek_mode)!;
return os::native_ftell(self.file);
os::native_fseek(self.file, offset, (SeekOrigin)seek_mode.ordinal)!;
return (usz)os::native_ftell(self.file);
}
<*
@require self.file != null
*>
fn void? File.set_cursor(&self, long offset, SeekOrigin whence = FROM_START) @dynamic
{
return os::native_fseek(self.file, offset, whence);
}
<*
@require self.file != null
*>
fn long? File.cursor(&self) @dynamic
{
return os::native_ftell(self.file);
}
/*
Implement later
@@ -118,6 +138,14 @@ fn void? File.close(&self) @inline @dynamic
self.file = null;
}
fn ulong? File.size(&self) @dynamic
{
long curr = self.cursor()!;
defer (void)self.set_cursor(curr);
self.set_cursor(0, FROM_END)!;
return self.cursor()!;
}
<*
@require self.file != null
*>
@@ -171,9 +199,8 @@ fn char[]? load_buffer(String filename, char[] buffer)
{
File file = open(filename, "rb")!;
defer (void)file.close();
usz len = file.seek(0, END)!;
long len = file.size()!;
if (len > buffer.len) return io::OVERFLOW~;
file.seek(0, SET)!;
usz read = 0;
while (read < len)
{
@@ -187,16 +214,16 @@ fn char[]? load(Allocator allocator, String filename)
{
File file = open(filename, "rb")!;
defer (void)file.close();
usz len = file.seek(0, END)!;
file.seek(0, SET)!;
char* data = allocator::malloc_try(allocator, len)!;
ulong len = file.size()!;
if (len > usz.max) return io::OUT_OF_SPACE~;
char* data = allocator::malloc_try(allocator, (usz)len)!;
defer catch allocator::free(allocator, data);
usz read = 0;
while (read < len)
while (read < (usz)len)
{
read += file.read(data[read:len - read])!;
read += file.read(data[read:(usz)len - read])!;
}
return data[:len];
return data[:(usz)len];
}
fn char[]? load_path(Allocator allocator, Path path) => load(allocator, path.str_view());

View File

@@ -45,10 +45,9 @@ fn FileMmap? mmap_file(File file, usz offset = 0, usz len = 0, VirtualMemoryAcce
{
if (len == 0)
{
usz cur = file.seek(0, CURSOR)!;
defer file.seek(cur, SET)!!;
usz file_size = file.seek(0, END)!;
len = file_size - offset;
ulong new_len = file.size()! - offset;
if (new_len > (ulong)isz.max) return mem::OUT_OF_MEMORY~;
len = (usz)new_len;
}
// get the page size

View File

@@ -91,16 +91,7 @@ fn void Formatter.init(&self, OutputFn out_fn, void* data = null)
*self = { .data = data, .out_fn = out_fn};
}
fn usz? Formatter.out(&self, char c) @private
{
if (catch err = self.out_fn(self.data, c))
{
if (self.first_fault) return self.first_fault~;
self.first_fault = err;
return err~;
}
return 1;
}
fn usz? Formatter.print_with_function(&self, Printable arg)
{
@@ -115,7 +106,7 @@ fn usz? Formatter.print_with_function(&self, Printable arg)
self.width = old_width;
self.prec = old_prec;
}
if (!arg) return self.out_substr("(null)");
if (!arg) return formatter_out_substr(self, "(null)");
return arg.to_format(self);
}
if (&arg.to_constant_string)
@@ -129,161 +120,12 @@ fn usz? Formatter.print_with_function(&self, Printable arg)
self.width = old_width;
self.prec = old_prec;
}
if (!arg) return self.out_substr("(null)");
return self.out_substr(arg.to_constant_string());
if (!arg) return formatter_out_substr(self, "(null)");
return formatter_out_substr(self, arg.to_constant_string());
}
return NOT_FOUND~;
}
fn usz? Formatter.out_unknown(&self, String category, any arg) @private
{
return self.out_substr("<") + self.out_substr(category) + self.out_substr(" type:") + self.ntoa((iptr)arg.type, false, 16) + self.out_substr(", addr:") + self.ntoa((iptr)arg.ptr, false, 16) + self.out_substr(">");
}
fn usz? Formatter.out_collection(&self, any arg, String open, String close) @private
{
typeid inner = arg.type.inner;
if (inner == void.typeid) inner = char.typeid;
usz size = inner.sizeof;
usz alen;
void* data_ptr;
if (arg.type.kindof == SLICE)
{
String* temp = arg.ptr;
data_ptr = temp.ptr;
alen = temp.len;
}
else
{
data_ptr = arg.ptr;
alen = arg.type.len;
}
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.flags = {};
self.width = 0;
usz len = self.out_substr(open)!;
for (usz i = 0; i < alen; i++)
{
if (i != 0) len += self.out_substr(", ")!;
len += self.out_str(any_make(data_ptr, inner))!;
data_ptr += size;
}
len += self.out_substr(close)!;
return len;
}
fn usz? Formatter.out_str(&self, any arg) @private
{
switch (arg.type.kindof)
{
case VOID:
return self.out_substr("void");
case FAULT:
fault f = *(fault*)arg.ptr;
return self.out_substr(f ? f.nameof : "(empty-fault)");
case INTERFACE:
any a = *(any*)arg;
return a ? self.out_str(a) : self.out_substr("(empty-interface)");
case ANY:
any a = *(any*)arg;
return a ? self.out_str(a) : self.out_substr("(empty-any)");
case OPTIONAL:
unreachable();
case SIGNED_INT:
case UNSIGNED_INT:
case FLOAT:
case FUNC:
case POINTER:
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.flags = {};
self.width = 0;
switch (arg.type.kindof)
{
case SIGNED_INT:
case UNSIGNED_INT:
return self.ntoa_any(arg, 10) ?? self.out_substr("<INVALID>");
case FLOAT:
return self.ftoa(float_from_any(arg)) ?? self.out_substr("ERR");
case FUNC:
case POINTER:
if (arg.type.kindof == POINTER && arg.type.inner != void.typeid)
{
void** pointer = arg.ptr;
any deref = any_make(*pointer, arg.type.inner);
usz? n = self.print_with_function((Printable)deref);
if (try n) return n;
if (@catch(n) != NOT_FOUND) n!;
}
return self.out_substr("0x")! + self.ntoa_any(arg, 16);
default:
unreachable();
}
case BOOL:
return self.out_substr(*(bool*)arg.ptr ? "true" : "false");
default:
}
usz? n = self.print_with_function((Printable)arg);
if (try n) return n;
if (@catch(n) != NOT_FOUND) n!;
switch (arg.type.kindof)
{
case TYPEID:
return self.out_substr("typeid[")! + self.ntoa((iptr)*(typeid*)arg, false, 16)! + self.out_substr("]")!;
case ENUM:
usz i = types::any_to_enum_ordinal(arg, usz)!!;
assert(i < arg.type.names.len, "Illegal enum value found, numerical value was %d.", i);
return self.out_substr(arg.type.names[i]);
case STRUCT:
return self.out_unknown("struct", arg);
case UNION:
return self.out_unknown("union", arg);
case BITSTRUCT:
return self.out_unknown("bitstruct", arg);
case CONST_ENUM:
case DISTINCT:
if (arg.type == String.typeid)
{
return self.out_substr(*(String*)arg);
}
if (arg.type == ZString.typeid)
{
return self.out_substr(*(ZString*)arg ? ((ZString*)arg).str_view() : "(null)");
}
if (arg.type == DString.typeid)
{
return self.out_substr(*(DString*)arg ? ((DString*)arg).str_view() : "(null)");
}
return self.out_str(arg.as_inner());
case ARRAY:
return self.out_collection(arg, "[", "]");
case VECTOR:
return self.out_collection(arg, "[<", ">]");
case SLICE:
return self.out_collection(arg, "[", "]");
case ANY:
case INTERFACE:
unreachable("Already handled");
default:
}
return self.out_substr("Invalid type");
}
fn void? out_null_fn(void* data @unused, char c @unused) @private
{
@@ -291,7 +133,7 @@ fn void? out_null_fn(void* data @unused, char c @unused) @private
macro usz? @report_fault(Formatter* f, $fault)
{
(void)f.out_substr($fault);
(void)formatter_out_substr(f, $fault);
return INVALID_FORMAT~;
}
@@ -307,69 +149,14 @@ macro usz? @wrap_bad(Formatter* f, #action)
return f.first_err(err)~;
default:
err = f.first_err(INVALID_ARGUMENT);
f.out_substr("<INVALID>")!;
formatter_out_substr(f, "<INVALID>")!;
return err~;
}
}
return len;
}
fn usz? Formatter.out_hex_buffer(&self, any arg) @private @inline
{
char[] out @noinit;
switch (arg.type)
{
case char[]:
case ichar[]:
out = *(char[]*)arg;
default:
if (arg.type.kindof == ARRAY && (arg.type.inner == char.typeid || arg.type.inner == ichar.typeid))
{
out = ((char*)arg.ptr)[:arg.type.sizeof];
break;
}
if (arg.type.kindof == POINTER)
{
// Maybe there is a more idiomatic way here
out = ((*(char**)arg.ptr))[:arg.type.inner.sizeof];
break;
}
return self.out_substr("<INVALID>");
}
usz len = out.len * 2;
usz total;
if (self.flags.left)
{
total += print_hex_chars(self, out, self.flags.uppercase)!;
total += self.pad(' ', self.width, (isz)total)!;
}
else
{
if (self.width) total += self.pad(' ', self.width, (isz)len)!;
total += print_hex_chars(self, out, self.flags.uppercase)!;
}
return total;
}
fn usz? Formatter.out_str_pad(&self, any arg) @private @inline
{
usz total;
if (self.width && !self.flags.left)
{
OutputFn out_fn = self.out_fn;
self.out_fn = (OutputFn)&out_null_fn;
usz len = self.out_str(arg)!;
self.out_fn = out_fn;
total += self.pad(' ', self.width, (isz)len)!;
}
usz len = self.out_str(arg)!;
total += len;
if (self.flags.left)
{
total += self.pad(' ', self.width, (isz)len)!;
}
return total;
}
fn usz? Formatter.vprintf(&self, String format, any[] anys)
{
@@ -389,7 +176,7 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
if (c != '%')
{
// no
total_len += self.out(c)!;
total_len += self.print_char(c)!;
continue;
}
i++;
@@ -397,7 +184,7 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
c = format[i];
if (c == '%')
{
total_len += self.out(c)!;
total_len += self.print_char(c)!;
continue;
}
// evaluate flags
@@ -443,7 +230,7 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
if (variant_index >= anys.len)
{
self.first_err(NOT_ENOUGH_ARGUMENTS);
total_len += self.out_substr("<MISSING>")!;
total_len += formatter_out_substr(self, "<MISSING>")!;
continue;
}
any current = anys[variant_index++];
@@ -471,37 +258,37 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
self.flags.uppercase = true;
nextcase;
case 'a':
total_len += @wrap_bad(self, self.atoa(float_from_any(current)))!;
total_len += @wrap_bad(self, formatter_atoa(self, float_from_any(current)))!;
continue;
case 'F' :
self.flags.uppercase = true;
nextcase;
case 'f':
total_len += @wrap_bad(self, self.ftoa(float_from_any(current)))!;
total_len += @wrap_bad(self, formatter_ftoa(self, float_from_any(current)))!;
continue;
case 'E':
self.flags.uppercase = true;
nextcase;
case 'e':
total_len += @wrap_bad(self, self.etoa(float_from_any(current)))!;
total_len += @wrap_bad(self, formatter_etoa(self, float_from_any(current)))!;
continue;
case 'G':
self.flags.uppercase = true;
nextcase;
case 'g':
total_len += @wrap_bad(self, self.gtoa(float_from_any(current)))!;
total_len += @wrap_bad(self, formatter_gtoa(self, float_from_any(current)))!;
continue;
case 'c':
total_len += self.out_char(current)!;
total_len += formatter_out_char(self, current)!;
continue;
case 'H':
self.flags.uppercase = true;
nextcase;
case 'h':
total_len += self.out_hex_buffer(current)!;
total_len += formatter_out_hex_buffer(self, current)!;
continue;
case 's':
total_len += self.out_str_pad(current)!;
total_len += formatter_out_str_pad(self, current)!;
continue;
case 'p':
self.flags.zeropad = true;
@@ -509,7 +296,7 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
base = 16;
default:
self.first_err(INVALID_FORMAT);
total_len += self.out_substr("<BAD FORMAT>")!;
total_len += formatter_out_substr(self, "<BAD FORMAT>")!;
continue;
}
if (base != 10)
@@ -521,13 +308,25 @@ fn usz? Formatter.vprintf(&self, String format, any[] anys)
if (self.flags.precision) self.flags.zeropad = false;
bool is_neg;
total_len += @wrap_bad(self, self.ntoa(int_from_any(current, &is_neg), is_neg, base))!;
total_len += @wrap_bad(self, formatter_ntoa(self, int_from_any(current, &is_neg), is_neg, base))!;
}
if (self.first_fault) return self.first_fault~;
return total_len;
}
fn usz? Formatter.out(&self, char c) @deprecated("Use print_char") => self.print_char(c);
fn usz? Formatter.print_char(&self, char c)
{
if (catch err = self.out_fn(self.data, c))
{
if (self.first_fault) return self.first_fault~;
self.first_fault = err;
return err~;
}
return 1;
}
fn usz? Formatter.print(&self, String str)
{
@@ -536,6 +335,6 @@ fn usz? Formatter.print(&self, String str)
// use null output function
self.out_fn = &out_null_fn;
}
foreach (c : str) self.out(c)!;
foreach (c : str) self.print_char(c)!;
return str.len;
}

View File

@@ -13,10 +13,10 @@ fn usz? print_hex_chars(Formatter* f, char[] out, bool uppercase) @inline
foreach (c : out)
{
char digit = c >> 4;
f.out(digit + (digit < 10 ? '0' : past_10))!;
f.print_char(digit + (digit < 10 ? '0' : past_10))!;
len++;
digit = c & 0xf;
f.out(digit + (digit < 10 ? '0' : past_10))!;
f.print_char(digit + (digit < 10 ? '0' : past_10))!;
len++;
}
return len;
@@ -29,10 +29,10 @@ macro fault Formatter.first_err(&self, fault f)
return f;
}
fn usz? Formatter.adjust(&self, usz len) @local
fn usz? formatter_adjust(Formatter* f, usz len) @local
{
if (!self.flags.left) return 0;
return self.pad(' ', self.width, len);
if (!f.flags.left) return 0;
return formatter_pad(f, ' ', f.width, len);
}
fn uint128? int_from_any(any arg, bool *is_neg) @private
@@ -43,8 +43,8 @@ fn uint128? int_from_any(any arg, bool *is_neg) @private
case POINTER:
*is_neg = false;
return (uint128)(uptr)*(void**)arg.ptr;
case DISTINCT:
case CONST_ENUM:
case TYPEDEF:
case CONSTDEF:
return int_from_any(arg.as_inner(), is_neg);
default:
break;
@@ -95,7 +95,7 @@ fn FloatType? float_from_any(any arg) @private
$if env::F128_SUPPORT:
if (arg.type == float128.typeid) return (FloatType)*((float128*)arg.ptr);
$endif
if (arg.type.kindof == DISTINCT || arg.type.kindof == CONST_ENUM)
if (arg.type.kindof == TYPEDEF || arg.type.kindof == CONSTDEF)
{
return float_from_any(arg.as_inner());
}
@@ -156,11 +156,11 @@ fn uint simple_atoi(char* buf, usz maxlen, usz* len_ptr) @inline @private
return i;
}
fn usz? Formatter.out_substr(&self, String str) @private
fn usz? formatter_out_substr(Formatter* f, String str) @private
{
usz l = conv::utf8_codepoints(str);
uint prec = self.prec;
if (self.flags.precision && l < prec) l = prec;
uint prec = f.prec;
if (f.flags.precision && l < prec) l = prec;
usz index = 0;
usz chars = str.len;
char* ptr = str.ptr;
@@ -168,17 +168,17 @@ fn usz? Formatter.out_substr(&self, String str) @private
{
char c = ptr[index];
// Break if we have precision set and we ran out...
if (c & 0xC0 != 0x80 && self.flags.precision && !prec--) break;
self.out(c)!;
if (c & 0xC0 != 0x80 && f.flags.precision && !prec--) break;
f.print_char(c)!;
index++;
}
return index;
}
fn usz? Formatter.pad(&self, char c, isz width, isz len) @inline
fn usz? formatter_pad(Formatter* f, char c, isz width, isz len) @inline
{
isz delta = width - len;
for (isz i = 0; i < delta; i++) self.out(c)!;
for (isz i = 0; i < delta; i++) f.print_char(c)!;
return max(0, delta);
}
@@ -191,7 +191,7 @@ fn char* fmt_u(uint128 x, char* s)
fn usz? Formatter.out_chars(&self, char[] s)
{
foreach (c : s) self.out(c)!;
foreach (c : s) self.print_char(c)!;
return s.len;
}
@@ -203,12 +203,12 @@ enum FloatFormatting
HEX
}
fn usz? Formatter.etoa(&self, double y) => self.floatformat(EXPONENTIAL, y);
fn usz? Formatter.ftoa(&self, double y) => self.floatformat(FLOAT, y);
fn usz? Formatter.gtoa(&self, double y) => self.floatformat(ADAPTIVE, y);
fn usz? Formatter.atoa(&self, double y) => self.floatformat(HEX, y);
fn usz? formatter_etoa(Formatter* self, double y) => formatter_floatformat(self, EXPONENTIAL, y);
fn usz? formatter_ftoa(Formatter* self, double y) => formatter_floatformat(self, FLOAT, y);
fn usz? formatter_gtoa(Formatter* self, double y) => formatter_floatformat(self, ADAPTIVE, y);
fn usz? formatter_atoa(Formatter* self, double y) => formatter_floatformat(self, HEX, y);
fn usz? Formatter.floatformat_hex(&self, double y, bool is_neg, isz pl, isz p) @private @inline
fn usz? formatter_floatformat_hex(Formatter* self, double y, bool is_neg, isz pl, isz p) @private @inline
{
double round = 8.0;
// 0x / 0X
@@ -261,18 +261,18 @@ fn usz? Formatter.floatformat_hex(&self, double y, bool is_neg, isz pl, isz p) @
usz l = (usz)(p && outlen - 2 < p
? p + 2 + explen
: outlen + explen);
if (!self.flags.left && !self.flags.zeropad) len += self.pad(' ', self.width, pl + l)!;
if (is_neg || self.flags.plus) len += self.out(is_neg ? '-' : '+')!;
if (!self.flags.left && !self.flags.zeropad) len += formatter_pad(self, ' ', self.width, pl + l)!;
if (is_neg || self.flags.plus) len += self.print_char(is_neg ? '-' : '+')!;
len += self.out_chars(self.flags.uppercase ? "0X" : "0x")!;
if (self.flags.zeropad) len += self.pad('0', self.width, pl + l)!;
if (self.flags.zeropad) len += formatter_pad(self, '0', self.width, pl + l)!;
len += self.out_chars(buf[:outlen])!;
len += self.pad('0', (isz)l - outlen - explen, 0)!;
len += formatter_pad(self, '0', (isz)l - outlen - explen, 0)!;
len += self.out_chars(estr[:explen])!;
if (self.flags.left) len += self.pad(' ', self.width, pl + (isz)l)!;
if (self.flags.left) len += formatter_pad(self, ' ', self.width, pl + (isz)l)!;
return len;
}
fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @private
fn usz? formatter_floatformat(Formatter* self, FloatFormatting formatting, double y) @private
{
// This code is heavily based on musl's printf code
const BUF_SIZE = (math::DOUBLE_MANT_DIG + 28) / 29 + 1
@@ -290,17 +290,17 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
{
usz len;
// Add padding
if (!self.flags.left) len += self.pad(' ', self.width, 3 + pl)!;
if (!self.flags.left) len += formatter_pad(self, ' ', self.width, 3 + pl)!;
String s = self.flags.uppercase ? "INF" : "inf";
if (math::is_nan(y)) s = self.flags.uppercase ? "NAN" : "nan";
if (pl) len += self.out(is_neg ? '-' : '+')!;
if (pl) len += self.print_char(is_neg ? '-' : '+')!;
len += self.out_chars(s)!;
if (self.flags.left) len += self.pad(' ', self.width, 3 + pl)!;
if (self.flags.left) len += formatter_pad(self, ' ', self.width, 3 + pl)!;
return len;
}
isz p = self.flags.precision ? self.prec : -1;
if (formatting == HEX) return self.floatformat_hex(y, is_neg, pl, p);
if (formatting == HEX) return formatter_floatformat_hex(self, y, is_neg, pl, p);
// Rescale
int e2;
@@ -483,9 +483,9 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
}
if (l > int.max - pl) return INTERNAL_BUFFER_EXCEEDED~;
usz len;
if (!self.flags.left && !self.flags.zeropad) len += self.pad(' ', self.width, pl + l)!;
if (is_neg || self.flags.plus) len += self.out(is_neg ? '-' : '+')!;
if (self.flags.zeropad) len += self.pad('0', self.width, pl + l)!;
if (!self.flags.left && !self.flags.zeropad) len += formatter_pad(self, ' ', self.width, pl + l)!;
if (is_neg || self.flags.plus) len += self.print_char(is_neg ? '-' : '+')!;
if (self.flags.zeropad) len += formatter_pad(self, '0', self.width, pl + l)!;
char[9] buf_array;
char* buf = &buf_array[0];
@@ -506,14 +506,14 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
}
len += self.out_chars(s[:buf + 9 - s])!;
}
if (p || self.flags.hash) len += self.out('.')!;
if (p || self.flags.hash) len += self.print_char('.')!;
for (; d < z && p > 0; d++, p -= 9)
{
char* s = fmt_u(*d, buf + 9);
while (s > buf) *--s = '0';
len += self.out_chars(s[:math::min((isz)9, p)])!;
}
len += self.pad('0', p + 9, 9)!;
len += formatter_pad(self, '0', p + 9, 9)!;
}
else
{
@@ -528,21 +528,42 @@ fn usz? Formatter.floatformat(&self, FloatFormatting formatting, double y) @priv
}
else
{
len += self.out(s++[0])!;
if (p > 0 || self.flags.hash) len += self.out('.')!;
len += self.print_char(s++[0])!;
if (p > 0 || self.flags.hash) len += self.print_char('.')!;
}
len += self.out_chars(s[:math::min(buf + 9 - s, p)])!;
p -= buf + 9 - s;
}
len += self.pad('0', p + 18, 18)!;
len += formatter_pad(self, '0', p + 18, 18)!;
len += self.out_chars(estr[:ebuf - estr])!;
}
if (self.flags.left) len += self.pad(' ', self.width, pl + l)!;
if (self.flags.left) len += formatter_pad(self, ' ', self.width, pl + l)!;
return len;
}
fn usz? formatter_out_str_pad(Formatter* self, any arg) @private @inline
{
usz total;
if (self.width && !self.flags.left)
{
OutputFn out_fn = self.out_fn;
self.out_fn = (OutputFn)&out_null_fn;
usz len = formatter_out_str(self, arg)!;
self.out_fn = out_fn;
total += formatter_pad(self, ' ', self.width, (isz)len)!;
}
usz len = formatter_out_str(self, arg)!;
total += len;
if (self.flags.left)
{
total += formatter_pad(self, ' ', self.width, (isz)len)!;
}
return total;
}
const char[201] DIGIT_PAIRS @private =
"00102030405060708090"
"01112131415161718191"
@@ -555,18 +576,18 @@ const char[201] DIGIT_PAIRS @private =
"08182838485868788898"
"09192939495969798999";
fn usz? Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private
fn usz? formatter_ntoa(Formatter* f, uint128 value, bool negative, uint base) @private
{
char[PRINTF_NTOA_BUFFER_SIZE] buf @noinit;
usz len;
// no hash for 0 values
if (!value) self.flags.hash = false;
if (!value) f.flags.hash = false;
// write if precision != 0 or value is != 0
if (!self.flags.precision || value)
if (!f.flags.precision || value)
{
char past_10 = (self.flags.uppercase ? 'A' : 'a') - 10;
char past_10 = (f.flags.uppercase ? 'A' : 'a') - 10;
switch (base)
{
case 2:
@@ -612,27 +633,26 @@ fn usz? Formatter.ntoa(&self, uint128 value, bool negative, uint base) @private
if (len >= PRINTF_NTOA_BUFFER_SIZE) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '0' + (char)value & 0x7;
value >>= 3;
}
while (value);
} while (value);
default:
unreachable();
}
}
return self.ntoa_format((String)buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base);
return formatter_ntoa_format(f, (String)buf[:PRINTF_NTOA_BUFFER_SIZE], len, negative, base);
}
fn usz? Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint base) @private
fn usz? formatter_ntoa_format(Formatter* f, String buf, usz len, bool negative, uint base) @private
{
// pad leading zeros
if (!self.flags.left)
if (!f.flags.left)
{
if (self.width && self.flags.zeropad && (negative || self.flags.plus || self.flags.space)) self.width--;
while (len < self.prec)
if (f.width && f.flags.zeropad && (negative || f.flags.plus || f.flags.space)) f.width--;
while (len < f.prec)
{
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '0';
}
while (self.flags.zeropad && len < self.width)
while (f.flags.zeropad && len < f.width)
{
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '0';
@@ -640,9 +660,9 @@ fn usz? Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint ba
}
// handle hash
if (self.flags.hash && base != 10)
if (f.flags.hash && base != 10)
{
if (!self.flags.precision && len && len == self.prec && len == self.width)
if (!f.flags.precision && len && len == f.prec && len == f.width)
{
len--;
if (len) len--;
@@ -653,11 +673,11 @@ fn usz? Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint ba
switch (base)
{
case 16:
buf[len++] = self.flags.uppercase ? 'X' : 'x';
buf[len++] = f.flags.uppercase ? 'X' : 'x';
case 8:
buf[len++] = self.flags.uppercase ? 'O' : 'o';
buf[len++] = f.flags.uppercase ? 'O' : 'o';
case 2:
buf[len++] = self.flags.uppercase ? 'B' : 'b';
buf[len++] = f.flags.uppercase ? 'B' : 'b';
default:
unreachable();
}
@@ -670,35 +690,35 @@ fn usz? Formatter.ntoa_format(&self, String buf, usz len, bool negative, uint ba
case negative:
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '-';
case self.flags.plus:
case f.flags.plus:
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = '+';
case self.flags.space:
case f.flags.space:
if (len >= buf.len) return INTERNAL_BUFFER_EXCEEDED~;
buf[len++] = ' ';
}
if (len) self.out_reverse(buf[:len])!;
if (len) formatter_out_reverse(f, buf[:len])!;
return len;
}
fn usz? Formatter.ntoa_any(&self, any arg, uint base) @private
fn usz? formatter_ntoa_any(Formatter* f, any arg, uint base) @private
{
bool is_neg;
return self.ntoa(int_from_any(arg, &is_neg)!!, is_neg, base) @inline;
return formatter_ntoa(f, int_from_any(arg, &is_neg)!!, is_neg, base) @inline;
}
fn usz? Formatter.out_char(&self, any arg) @private
fn usz? formatter_out_char(Formatter* f, any arg) @private
{
if (!arg.type.kindof.is_int())
{
return self.out_substr("<NOT CHAR>");
return formatter_out_substr(f, "<NOT CHAR>");
}
usz len = 1;
// pre padding
if (!self.flags.left)
if (!f.flags.left)
{
len += self.pad(' ', self.width, len)!;
len += formatter_pad(f, ' ', f.width, len)!;
}
// char output
@@ -706,42 +726,42 @@ fn usz? Formatter.out_char(&self, any arg) @private
switch (true)
{
case c < 0x7f:
self.out((char)c)!;
f.print_char((char)c)!;
case c < 0x7ff:
self.out((char)(0xC0 | c >> 6))!;
self.out((char)(0x80 | (c & 0x3F)))!;
f.print_char((char)(0xC0 | c >> 6))!;
f.print_char((char)(0x80 | (c & 0x3F)))!;
case c < 0xffff:
self.out((char)(0xE0 | c >> 12))!;
self.out((char)(0x80 | (c >> 6 & 0x3F)))!;
self.out((char)(0x80 | (c & 0x3F)))!;
f.print_char((char)(0xE0 | c >> 12))!;
f.print_char((char)(0x80 | (c >> 6 & 0x3F)))!;
f.print_char((char)(0x80 | (c & 0x3F)))!;
default:
self.out((char)(0xF0 | c >> 18))!;
self.out((char)(0x80 | (c >> 12 & 0x3F)))!;
self.out((char)(0x80 | (c >> 6 & 0x3F)))!;
self.out((char)(0x80 | (c & 0x3F)))!;
f.print_char((char)(0xF0 | c >> 18))!;
f.print_char((char)(0x80 | (c >> 12 & 0x3F)))!;
f.print_char((char)(0x80 | (c >> 6 & 0x3F)))!;
f.print_char((char)(0x80 | (c & 0x3F)))!;
}
if (self.flags.left)
if (f.flags.left)
{
len += self.pad(' ', self.width, len)!;
len += formatter_pad(f, ' ', f.width, len)!;
}
return len;
}
fn usz? Formatter.out_reverse(&self, char[] buf) @private
fn usz? formatter_out_reverse(Formatter* f, char[] buf) @private
{
usz n;
usz len = buf.len;
// pad spaces up to given width
if (!self.flags.zeropad && !self.flags.left)
if (!f.flags.zeropad && !f.flags.left)
{
n += self.pad(' ', self.width, len)!;
n += formatter_pad(f, ' ', f.width, len)!;
}
// reverse string
while (len) n += self.out(buf[--len])!;
while (len) n += f.print_char(buf[--len])!;
// append pad spaces up to given width
n += self.adjust(n)!;
n += formatter_adjust(f, n)!;
return n;
}
@@ -761,3 +781,197 @@ fn int? printf_parse_format_field(
uint? intval = types::any_to_int(val, int);
return intval ?? BAD_FORMAT~;
}
fn usz? formatter_out_hex_buffer(Formatter* self, any arg) @private @inline
{
char[] out @noinit;
switch (arg.type)
{
case char[]:
case ichar[]:
out = *(char[]*)arg;
default:
if (arg.type.kindof == ARRAY && (arg.type.inner == char.typeid || arg.type.inner == ichar.typeid))
{
out = ((char*)arg.ptr)[:arg.type.sizeof];
break;
}
if (arg.type.kindof == POINTER)
{
// Maybe there is a more idiomatic way here
out = ((*(char**)arg.ptr))[:arg.type.inner.sizeof];
break;
}
return formatter_out_substr(self, "<INVALID>");
}
usz len = out.len * 2;
usz total;
if (self.flags.left)
{
total += print_hex_chars(self, out, self.flags.uppercase)!;
total += formatter_pad(self, ' ', self.width, (isz)total)!;
}
else
{
if (self.width) total += formatter_pad(self, ' ', self.width, (isz)len)!;
total += print_hex_chars(self, out, self.flags.uppercase)!;
}
return total;
}
fn usz? formatter_out_unknown(Formatter* self, String category, any arg) @private
{
return formatter_out_substr(self, "<")
+ formatter_out_substr(self, category)
+ formatter_out_substr(self, " type:")
+ formatter_ntoa(self, (iptr)arg.type, false, 16)
+ formatter_out_substr(self, ", addr:")
+ formatter_ntoa(self, (iptr)arg.ptr, false, 16)
+ formatter_out_substr(self, ">");
}
fn usz? formatter_out_collection(Formatter* self, any arg, String open, String close) @private
{
typeid inner = arg.type.inner;
if (inner == void.typeid) inner = char.typeid;
usz size = inner.sizeof;
usz alen;
void* data_ptr;
if (arg.type.kindof == SLICE)
{
String* temp = arg.ptr;
data_ptr = temp.ptr;
alen = temp.len;
}
else
{
data_ptr = arg.ptr;
alen = arg.type.len;
}
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.flags = {};
self.width = 0;
usz len = formatter_out_substr(self, open)!;
for (usz i = 0; i < alen; i++)
{
if (i != 0) len += formatter_out_substr(self, ", ")!;
len += formatter_out_str(self, any_make(data_ptr, inner))!;
data_ptr += size;
}
len += formatter_out_substr(self, close)!;
return len;
}
fn usz? formatter_out_str(Formatter* self, any arg) @private
{
switch (arg.type.kindof)
{
case VOID:
return formatter_out_substr(self, "void");
case FAULT:
fault f = *(fault*)arg.ptr;
return formatter_out_substr(self, f ? f.nameof : "(empty-fault)");
case INTERFACE:
any a = *(any*)arg;
return a ? formatter_out_str(self, a) : formatter_out_substr(self, "(empty-interface)");
case ANY:
any a = *(any*)arg;
return a ? formatter_out_str(self, a) : formatter_out_substr(self, "(empty-any)");
case OPTIONAL:
unreachable();
case SIGNED_INT:
case UNSIGNED_INT:
case FLOAT:
case FUNC:
case POINTER:
PrintFlags flags = self.flags;
uint width = self.width;
defer
{
self.flags = flags;
self.width = width;
}
self.flags = {};
self.width = 0;
switch (arg.type.kindof)
{
case SIGNED_INT:
case UNSIGNED_INT:
return formatter_ntoa_any(self, arg, 10) ?? formatter_out_substr(self, "<INVALID>");
case FLOAT:
return formatter_ftoa(self, float_from_any(arg)) ?? formatter_out_substr(self, "ERR");
case FUNC:
case POINTER:
if (arg.type.kindof == POINTER && arg.type.inner != void.typeid)
{
void** pointer = arg.ptr;
any deref = any_make(*pointer, arg.type.inner);
usz? n = self.print_with_function((Printable)deref);
if (try n) return n;
if (@catch(n) != NOT_FOUND) n!;
}
return formatter_out_substr(self, "0x")! + formatter_ntoa_any(self, arg, 16);
default:
unreachable();
}
case BOOL:
return formatter_out_substr(self, *(bool*)arg.ptr ? "true" : "false");
default:
}
usz? n = self.print_with_function((Printable)arg);
if (try n) return n;
if (@catch(n) != NOT_FOUND) n!;
switch (arg.type.kindof)
{
case TYPEID:
return formatter_out_substr(self, "typeid[")! + formatter_ntoa(self, (iptr)*(typeid*)arg, false, 16)! + formatter_out_substr(self, "]")!;
case ENUM:
usz i = types::any_to_enum_ordinal(arg, usz)!!;
assert(i < arg.type.names.len, "Illegal enum value found, numerical value was %d.", i);
return formatter_out_substr(self, arg.type.names[i]);
case STRUCT:
return formatter_out_unknown(self, "struct", arg);
case UNION:
return formatter_out_unknown(self, "union", arg);
case BITSTRUCT:
return formatter_out_unknown(self, "bitstruct", arg);
case CONSTDEF:
case TYPEDEF:
if (arg.type == String.typeid)
{
return formatter_out_substr(self, *(String*)arg);
}
if (arg.type == ZString.typeid)
{
return formatter_out_substr(self, *(ZString*)arg ? ((ZString*)arg).str_view() : "(null)");
}
if (arg.type == DString.typeid)
{
return formatter_out_substr(self, *(DString*)arg ? ((DString*)arg).str_view() : "(null)");
}
return formatter_out_str(self, arg.as_inner());
case ARRAY:
return formatter_out_collection(self, arg, "[", "]");
case VECTOR:
return formatter_out_collection(self, arg, "[<", ">]");
case SLICE:
return formatter_out_collection(self, arg, "[", "]");
case ANY:
case INTERFACE:
unreachable("Already handled");
default:
}
return formatter_out_substr(self, "Invalid type");
}

View File

@@ -11,6 +11,14 @@ enum Seek
END
}
enum SeekOrigin
{
FROM_START,
FROM_CURSOR,
FROM_END
}
faultdef
ALREADY_EXISTS,
BUSY,
@@ -358,7 +366,7 @@ fn usz? printfn(String format, args...) @format(0) @maydiscard
PutcharBuffer buff;
formatter.init(&out_putchar_buffer_fn, &buff);
usz? len = formatter.vprintf(format, args);
formatter.out('\n')!;
formatter.print_char('\n')!;
write_putchar_buffer(&buff, true)!;
return len + 1;
}

View File

@@ -49,16 +49,16 @@ fn void*? native_freopen(void* file, String filename, String mode) @inline => @
return file ?: file_open_errno()~;
}
fn void? native_fseek(void* file, isz offset, Seek seek_mode) @inline
fn void? native_fseek(void* file, long offset, SeekOrigin seek_mode) @inline
{
if (libc::fseek(file, (SeekIndex)offset, seek_mode.ordinal)) return file_seek_errno()~;
}
fn usz? native_ftell(CFile file) @inline
fn long? native_ftell(CFile file) @inline
{
long index = libc::ftell(file);
return index >= 0 ? (usz)index : file_seek_errno()~;
return index >= 0 ? index : file_seek_errno()~;
}
fn usz? native_fwrite(CFile file, char[] buffer) @inline
@@ -76,6 +76,11 @@ fn usz? native_fread(CFile file, char[] buffer) @inline
return libc::fread(buffer.ptr, 1, buffer.len, file);
}
fn void? native_fflush(CFile file) @inline @maydiscard
{
if (libc::fflush(file) != 0) return io::GENERAL_ERROR~;
}
macro fault file_open_errno() @local
{
switch (libc::errno())
@@ -123,3 +128,22 @@ macro fault file_seek_errno() @local
}
}
struct Utimbuf
{
Time_t actime;
Time_t modtime;
}
extern fn int utime(char* filename, void* times) @if(!env::WIN32);
extern fn int _wutime(WChar* filename, void* times) @if(env::WIN32);
fn void? native_set_modified_time(String filename, libc::Time_t time) => @stack_mem(256; Allocator smem)
{
Utimbuf times = { time, time };
$if env::WIN32:
if (_wutime(filename.to_wstring(smem)!, &times)) return io::GENERAL_ERROR~;
$else
if (utime(filename.zstr_copy(smem), &times)) return io::GENERAL_ERROR~;
$endif
}

View File

@@ -4,12 +4,14 @@ import libc;
alias FopenFn = fn void*?(String, String);
alias FreopenFn = fn void*?(void*, String, String);
alias FcloseFn = fn void?(void*);
alias FseekFn = fn void?(void*, isz, Seek);
alias FtellFn = fn usz?(void*);
alias FseekFn = fn void?(void*, long, SeekOrigin);
alias FtellFn = fn long?(void*);
alias FwriteFn = fn usz?(void*, char[] buffer);
alias FreadFn = fn usz?(void*, char[] buffer);
alias FflushFn = fn void?(void*);
alias RemoveFn = fn void?(String);
alias FputcFn = fn void?(int, void*);
alias SetModifiedTimeFn = fn void?(String, libc::Time_t);
FopenFn native_fopen_fn @weak @if(!$defined(native_fopen_fn));
FcloseFn native_fclose_fn @weak @if(!$defined(native_fclose_fn));
@@ -18,8 +20,10 @@ FseekFn native_fseek_fn @weak @if(!$defined(native_fseek_fn));
FtellFn native_ftell_fn @weak @if(!$defined(native_ftell_fn));
FwriteFn native_fwrite_fn @weak @if(!$defined(native_fwrite_fn));
FreadFn native_fread_fn @weak @if(!$defined(native_fread_fn));
FflushFn native_fflush_fn @weak @if(!$defined(native_fflush_fn));
RemoveFn native_remove_fn @weak @if(!$defined(native_remove_fn));
FputcFn native_fputc_fn @weak @if(!$defined(native_fputc_fn));
SetModifiedTimeFn native_set_modified_time_fn @weak @if(!$defined(native_set_modified_time_fn));
<*
@require mode.len > 0
@@ -52,13 +56,13 @@ fn void*? native_freopen(void* file, String filename, String mode) @inline
return io::UNSUPPORTED_OPERATION~;
}
fn void? native_fseek(void* file, isz offset, Seek seek_mode) @inline
fn void? native_fseek(void* file, long offset, SeekOrigin whence) @inline
{
if (native_fseek_fn) return native_fseek_fn(file, offset, seek_mode);
if (native_fseek_fn) return native_fseek_fn(file, offset, whence);
return io::UNSUPPORTED_OPERATION~;
}
fn usz? native_ftell(CFile file) @inline
fn ulong? native_ftell(CFile file) @inline
{
if (native_ftell_fn) return native_ftell_fn(file);
return io::UNSUPPORTED_OPERATION~;
@@ -76,8 +80,20 @@ fn usz? native_fread(CFile file, char[] buffer) @inline
return io::UNSUPPORTED_OPERATION~;
}
fn void? native_fflush(CFile file) @inline @maydiscard
{
if (native_fflush_fn) return native_fflush_fn(file);
return io::UNSUPPORTED_OPERATION~;
}
fn void? native_fputc(CInt c, CFile stream) @inline
{
if (native_fputc_fn) return native_fputc_fn(c, stream);
return io::UNSUPPORTED_OPERATION~;
}
fn void? native_set_modified_time(String filename, libc::Time_t time) @inline
{
if (native_set_modified_time_fn) return native_set_modified_time_fn(filename, time);
return io::UNSUPPORTED_OPERATION~;
}

View File

@@ -47,14 +47,15 @@ fn usz? native_file_size(String path) @if(env::WIN32) => @pool()
return (usz)size.quadPart;
}
fn usz? native_file_size(String path) @if(!env::WIN32 && !env::DARWIN && !env::LINUX && !env::ANDROID && !env::BSD_FAMILY)
fn ulong? native_file_size(String path) @if(!env::WIN32 && !env::DARWIN && !env::LINUX && !env::ANDROID && !env::BSD_FAMILY)
{
File f = file::open(path, "r")!;
defer (void)f.close();
return f.seek(0, Seek.END)!;
f.set_cursor(0, FROM_END)!;
return f.cursor();
}
fn usz? native_file_size(String path) @if(env::DARWIN || env::LINUX || env::ANDROID || env::BSD_FAMILY)
fn ulong? native_file_size(String path) @if(env::DARWIN || env::LINUX || env::ANDROID || env::BSD_FAMILY)
{
Stat stat;
native_stat(&stat, path)!;

View File

@@ -36,7 +36,7 @@ fn Path? cwd(Allocator allocator)
fn bool is_dir(Path path) => os::native_is_dir(path.str_view());
fn bool is_file(Path path) => os::native_is_file(path.str_view());
fn usz? file_size(Path path) => os::native_file_size(path.str_view());
fn ulong? file_size(Path path) => os::native_file_size(path.str_view());
fn bool exists(Path path) => os::native_file_or_dir_exists(path.str_view());
fn Path? tcwd() => cwd(tmem) @inline;

View File

@@ -1,12 +1,20 @@
module std::io;
import std::math;
alias SetCursorFn = fn void?(void*, long offset, SeekOrigin whence = START);
interface InStream
{
fn void? close() @optional;
fn long? cursor() @optional;
fn void? set_cursor(long offset, SeekOrigin whence = FROM_START) @optional;
fn usz? seek(isz offset, Seek seek) @optional;
fn usz len() @optional;
fn usz? available() @optional;
fn ulong? size() @optional;
fn ulong? available() @optional;
fn usz? read(char[] buffer);
fn char? read_byte();
fn usz? write_to(OutStream out) @optional;
@@ -24,15 +32,23 @@ interface OutStream
fn usz? read_to(InStream in) @optional;
}
fn usz? available(InStream s)
fn ulong? available(InStream s)
{
if (&s.available) return s.available();
if (&s.set_cursor && &s.cursor)
{
long curr = s.cursor()!;
s.set_cursor(0, FROM_END)!;
ulong len = s.cursor()!;
s.set_cursor(curr)!;
return len - curr;
}
if (&s.seek)
{
usz curr = s.seek(0, Seek.CURSOR)!;
usz len = s.seek(0, Seek.END)!;
s.seek(curr, Seek.SET)!;
return len - curr;
return (ulong)len - (ulong)curr;
}
return io::UNSUPPORTED_OPERATION~;
}
@@ -177,6 +193,11 @@ macro usz? write_using_write_byte(s, char[] bytes)
macro void? pushback_using_seek(s)
{
if (&s.set_cursor)
{
s.set_cursor(-1, FROM_CURSOR)!;
return;
}
s.seek(-1, CURSOR)!;
}
@@ -407,11 +428,11 @@ macro ulong? read_le_ulong(stream)
{
ulong val = (ulong)stream.read_byte()!;
val += (ulong)stream.read_byte()! << 8;
val += (ulong)stream.read_byte()! << 16;
val += (ulong)stream.read_byte()! << 16;
val += (ulong)stream.read_byte()! << 24;
val += (ulong)stream.read_byte()! << 32;
val += (ulong)stream.read_byte()! << 40;
val += (ulong)stream.read_byte()! << 48;
val += (ulong)stream.read_byte()! << 48;
return val + (ulong)stream.read_byte()! << 56;
}
@@ -621,24 +642,30 @@ macro void? skip(stream, usz bytes)
{
if (!bytes) return;
$switch:
$case !$defined(stream.seek):
for (usz i = 0; i < bytes; i++)
{
stream.read()!;
}
return;
$case $typeof(stream) == InStream:
if (!&stream.seek)
{
for (usz i = 0; i < bytes; i++)
{
stream.read()!;
}
return;
}
if (!&stream.seek && !&stream.set_cursor)
{
for (usz i = 0; i < bytes; i++)
{
stream.read()!;
}
return;
}
if (!&stream.set_cursor)
{
stream.seek(bytes, CURSOR)!;
return;
}
stream.set_cursor(bytes, FROM_CURSOR)!;
$case $defined(stream.set_cursor):
stream.set_cursor(bytes, FROM_CURSOR)!;
$case $defined(stream.seek):
stream.seek(bytes, CURSOR)!;
$default:
stream.seek(bytes, CURSOR)!;
for (usz i = 0; i < bytes; i++)
{
stream.read()!;
}
$endswitch
}

View File

@@ -38,7 +38,7 @@ fn usz? ReadBuffer.read(&self, char[] bytes) @dynamic
// Read directly into the input buffer.
return self.wrapped_stream.read(bytes)!;
}
self.refill()!;
readbuffer_refill(self)!;
}
usz n = min(self.write_idx - self.read_idx, bytes.len);
bytes[:n] = self.bytes[self.read_idx:n];
@@ -48,14 +48,14 @@ fn usz? ReadBuffer.read(&self, char[] bytes) @dynamic
fn char? ReadBuffer.read_byte(&self) @dynamic
{
if (self.read_idx == self.write_idx) self.refill()!;
if (self.read_idx == self.write_idx) readbuffer_refill(self)!;
if (self.read_idx == self.write_idx) return io::EOF~;
char c = self.bytes[self.read_idx];
self.read_idx++;
return c;
}
fn void? ReadBuffer.refill(&self) @local @inline
fn void? readbuffer_refill(ReadBuffer* self) @local @inline
{
self.read_idx = 0;
self.write_idx = self.wrapped_stream.read(self.bytes)!;
@@ -92,7 +92,7 @@ fn void? WriteBuffer.close(&self) @dynamic
fn void? WriteBuffer.flush(&self) @dynamic
{
self.write_pending()!;
write_buffer_write_pending(self)!;
if (&self.wrapped_stream.flush) self.wrapped_stream.flush()!;
}
@@ -106,7 +106,7 @@ fn usz? WriteBuffer.write(&self, char[] bytes) @dynamic
self.index += bytes.len;
return bytes.len;
}
self.write_pending()!;
write_buffer_write_pending(self)!;
if (bytes.len >= self.bytes.len)
{
// Write directly to the stream.
@@ -123,13 +123,13 @@ fn void? WriteBuffer.write_byte(&self, char c) @dynamic
usz n = self.bytes.len - self.index;
if (n == 0)
{
self.write_pending()!;
write_buffer_write_pending(self)!;
}
self.bytes[self.index] = c;
self.index += 1;
}
fn void? WriteBuffer.write_pending(&self) @local
fn void? write_buffer_write_pending(WriteBuffer* self) @local
{
self.index -= self.wrapped_stream.write(self.bytes[:self.index])!;
if (self.index != 0) return INCOMPLETE_WRITE~;

View File

@@ -104,28 +104,37 @@ fn void? ByteBuffer.pushback_byte(&self) @dynamic
self.has_last = false;
}
fn usz? ByteBuffer.seek(&self, isz offset, Seek seek) @dynamic
fn long? ByteBuffer.cursor(&self) @dynamic
{
switch (seek)
{
case SET:
if (offset < 0 || offset > self.write_idx) return INVALID_POSITION~;
self.read_idx = offset;
return offset;
case CURSOR:
if ((offset < 0 && self.read_idx < -offset) ||
(offset > 0 && self.read_idx + offset > self.write_idx)) return INVALID_POSITION~;
self.read_idx += offset;
case END:
if (offset < 0 || offset > self.write_idx) return INVALID_POSITION~;
self.read_idx = self.write_idx - offset;
}
return self.read_idx;
}
fn usz? ByteBuffer.available(&self) @inline @dynamic
fn void? ByteBuffer.set_cursor(&self, long offset, SeekOrigin whence = FROM_START) @dynamic
{
return self.write_idx - self.read_idx;
switch (whence)
{
case FROM_START:
if (offset < 0 || offset > self.write_idx) return INVALID_POSITION~;
self.read_idx = (usz)offset;
case FROM_CURSOR:
if ((offset < 0 && self.read_idx < -offset) ||
(offset > 0 && self.read_idx + offset > self.write_idx)) return INVALID_POSITION~;
self.read_idx += (usz)offset;
case FROM_END:
if (offset < 0 || offset > self.write_idx) return INVALID_POSITION~;
self.read_idx = self.write_idx - (usz)offset;
}
}
fn usz? ByteBuffer.seek(&self, isz offset, Seek seek) @dynamic
{
self.set_cursor(offset, (SeekOrigin)seek.ordinal)!;
return (usz)self.cursor();
}
fn ulong? ByteBuffer.available(&self) @inline @dynamic
{
return (ulong)self.write_idx - self.read_idx;
}
fn void ByteBuffer.grow(&self, usz n)

View File

@@ -41,16 +41,26 @@ fn void? ByteReader.pushback_byte(&self) @dynamic
fn usz? ByteReader.seek(&self, isz offset, Seek seek) @dynamic
{
isz new_index;
switch (seek)
self.set_cursor((long)offset, (SeekOrigin)seek.ordinal)!;
return (usz)self.cursor();
}
fn long? ByteReader.cursor(&self) @dynamic
{
return self.index;
}
fn void? ByteReader.set_cursor(&self, long offset, SeekOrigin whence = FROM_START) @dynamic
{
long new_index;
switch (whence)
{
case SET: new_index = offset;
case CURSOR: new_index = self.index + offset;
case END: new_index = self.bytes.len + offset;
case FROM_START: new_index = offset;
case FROM_CURSOR: new_index = self.index + offset;
case FROM_END: new_index = self.bytes.len + offset;
}
if (new_index < 0) return INVALID_POSITION~;
self.index = new_index;
return new_index;
if (new_index < 0 || new_index > self.bytes.len) return INVALID_POSITION~;
self.index = (usz)new_index;
}
fn usz? ByteReader.write_to(&self, OutStream writer) @dynamic
@@ -62,7 +72,7 @@ fn usz? ByteReader.write_to(&self, OutStream writer) @dynamic
return written;
}
fn usz? ByteReader.available(&self) @inline @dynamic
fn ulong? ByteReader.available(&self) @inline @dynamic
{
return max(0, self.bytes.len - self.index);
}
}

View File

@@ -86,9 +86,10 @@ fn usz? ByteWriter.read_from(&self, InStream reader) @dynamic
usz start_index = self.index;
if (&reader.available)
{
while (usz available = reader.available()!)
while (ulong available = reader.available()!)
{
self.ensure_capacity(self.index + available)!;
if (available > usz.max) return OUT_OF_SPACE~;
self.ensure_capacity(self.index + (usz)available)!;
usz read = reader.read(self.bytes[self.index..])!;
self.index += read;
}

View File

@@ -38,7 +38,7 @@ fn char? LimitReader.read_byte(&self) @dynamic
return self.wrapped_stream.read_byte();
}
fn usz? LimitReader.available(&self) @inline @dynamic
fn ulong? LimitReader.available(&self) @inline @dynamic
{
return self.limit;
}

View File

@@ -168,13 +168,13 @@ bitstruct Tc_lflags : CUInt
bool extproc : 16;
}
const enum T_nldly : char
constdef T_nldly : char
{
NL0 = 0b0,
NL1 = 0b1,
}
const enum T_crdly : char
constdef T_crdly : char
{
CR0 = 0b00,
CR1 = 0b01,
@@ -182,7 +182,7 @@ const enum T_crdly : char
CR3 = 0b11,
}
const enum T_tabdly : char
constdef T_tabdly : char
{
TAB0 = 0b00,
TAB1 = 0b01,
@@ -191,25 +191,25 @@ const enum T_tabdly : char
XTABS = TAB3,
}
const enum T_bsdly : char
constdef T_bsdly : char
{
BS0 = 0b0,
BS1 = 0b1,
}
const enum T_ffdly : char
constdef T_ffdly : char
{
FF0 = 0b0,
FF1 = 0b1,
}
const enum T_vtdly : char
constdef T_vtdly : char
{
VT0 = 0b0,
VT1 = 0b1,
}
const enum T_csize : char
constdef T_csize : char
{
CS5 = 0b00,
CS6 = 0b01,
@@ -217,7 +217,7 @@ const enum T_csize : char
CS8 = 0b11,
}
const enum Speed : CUInt
constdef Speed : CUInt
{
B0 = 0o0000000,
B50 = 0o0000001,
@@ -253,7 +253,7 @@ const enum Speed : CUInt
MAX_BAUD = B4000000,
}
const enum Cc : inline char
constdef Cc : inline char
{
VINTR = 0,
VQUIT = 1,
@@ -274,7 +274,7 @@ const enum Cc : inline char
VEOL2 = 16,
}
const enum Tcactions : CInt
constdef Tcactions : CInt
{
TCOOFF = 0,
TCOON = 1,

View File

@@ -37,7 +37,7 @@ fn BigInt* BigInt.init(&self, int128 value)
assert(value < 0 || tmp == 0 || !self.is_negative());
assert(value >= 0 || tmp == -1 || self.is_negative());
self.len = len;
self.reduce_len();
reduce_len(self);
return self;
}
@@ -153,15 +153,11 @@ fn void BigInt.add_this(&self, BigInt other) @operator(+=)
self.data[self.len++] = (uint)carry;
}
self.reduce_len();
reduce_len(self);
assert(sign != sign_arg || sign == self.is_negative(), "Overflow in addition");
}
fn void BigInt.reduce_len(&self) @private
{
self.len = max(find_length(&self.data, self.len), 1);
}
macro uint find_length(uint* data, uint length)
{
@@ -226,7 +222,7 @@ fn void BigInt.mult_this(&self, BigInt bi2) @operator(*=)
res.len = min(self.len + bi2.len, (uint)MAX_LEN);
res.reduce_len();
reduce_len(&res);
// overflow check (result is -ve)
assert(!res.is_negative(), "Multiplication overflow");
@@ -265,7 +261,7 @@ fn void BigInt.negate(&self)
assert(self.is_negative() != was_negative, "Overflow in negation");
self.len = MAX_LEN;
self.reduce_len();
reduce_len(self);
}
macro bool BigInt.is_zero(&self) => self.len == 1 && self.data[0] == 0;
@@ -301,7 +297,7 @@ fn BigInt* BigInt.sub_this(&self, BigInt other) @operator(-=)
self.len = MAX_LEN;
}
self.reduce_len();
reduce_len(self);
// overflow check
@@ -311,7 +307,7 @@ fn BigInt* BigInt.sub_this(&self, BigInt other) @operator(-=)
fn int BigInt.bitcount(&self)
{
self.reduce_len();
reduce_len(self);
uint val = self.data[self.len - 1];
uint mask = 0x80000000;
int bits = 32;
@@ -364,11 +360,11 @@ fn void BigInt.div_this(&self, BigInt other) @operator(/=)
if (other.len == 1)
{
self.single_byte_divide(&other, &quotient, &remainder);
single_byte_divide(self, &other, &quotient, &remainder);
}
else
{
self.multi_byte_divide(&other, &quotient, &remainder);
multi_byte_divide(self, &other, &quotient, &remainder);
}
if (negate_answer)
{
@@ -407,11 +403,11 @@ fn void BigInt.mod_this(&self, BigInt bi2) @operator(%=)
if (bi2.len == 1)
{
self.single_byte_divide(&bi2, &quotient, &remainder);
single_byte_divide(self, &bi2, &quotient, &remainder);
}
else
{
self.multi_byte_divide(&bi2, &quotient, &remainder);
multi_byte_divide(self, &bi2, &quotient, &remainder);
}
if (negate_answer)
{
@@ -425,7 +421,7 @@ fn void BigInt.bit_negate_this(&self)
foreach (&r : self.data) *r = ~*r;
self.len = MAX_LEN;
self.reduce_len();
reduce_len(self);
}
fn BigInt BigInt.bit_negate(self) @operator(~)
@@ -523,7 +519,7 @@ fn usz? BigInt.to_format(&self, Formatter* format) @dynamic
usz len;
if (negative)
{
format.out('-')!;
format.print_char('-')!;
len++;
a.negate();
}
@@ -580,7 +576,7 @@ fn String BigInt.to_string_with_radix(&self, int radix, Allocator allocator)
while (!a.is_zero())
{
a.single_byte_divide(&big_radix, &quotient, &remainder);
single_byte_divide(&a, &big_radix, &quotient, &remainder);
if (remainder.data[0] < 10)
{
@@ -740,7 +736,7 @@ fn BigInt barrett_reduction(BigInt x, BigInt n, BigInt constant)
}
r2.len = k_plus_one;
r2.reduce_len();
reduce_len(&r2);
r1.sub_this(r2);
if (r1.is_negative())
@@ -816,7 +812,7 @@ fn void BigInt.bit_and_this(&self, BigInt bi2)
}
self.len = MAX_LEN;
self.reduce_len();
reduce_len(self);
}
fn BigInt BigInt.bit_or(self, BigInt bi2) @operator(|)
@@ -834,7 +830,7 @@ fn void BigInt.bit_or_this(&self, BigInt bi2)
}
self.len = MAX_LEN;
self.reduce_len();
reduce_len(self);
}
fn BigInt BigInt.bit_xor(self, BigInt bi2) @operator(^)
@@ -852,7 +848,7 @@ fn void BigInt.bit_xor_this(&self, BigInt bi2)
}
self.len = MAX_LEN;
self.reduce_len();
reduce_len(self);
}
fn void BigInt.shl_this(&self, int shift) @operator(<<=)
@@ -928,13 +924,18 @@ fn void BigInt.randomize_bits(&self, Random random, int bits)
module std::math::bigint @private;
fn void reduce_len(BigInt* self) @private
{
self.len = max(find_length(&self.data, self.len), 1);
}
<*
@param [&inout] quotient
@param [&in] bi2
@param [&inout] remainder
*>
fn void BigInt.single_byte_divide(&self, BigInt* bi2, BigInt* quotient, BigInt* remainder)
fn void single_byte_divide(BigInt* self, BigInt* bi2, BigInt* quotient, BigInt* remainder) @private
{
uint[MAX_LEN] result;
int result_pos = 0;
@@ -977,8 +978,8 @@ fn void BigInt.single_byte_divide(&self, BigInt* bi2, BigInt* quotient, BigInt*
}
quotient.data[j..] = 0;
quotient.reduce_len();
remainder.reduce_len();
reduce_len(quotient);
reduce_len(remainder);
}
<*
@@ -986,7 +987,7 @@ fn void BigInt.single_byte_divide(&self, BigInt* bi2, BigInt* quotient, BigInt*
@param [&in] other
@param [&inout] remainder
*>
fn void BigInt.multi_byte_divide(&self, BigInt* other, BigInt* quotient, BigInt* remainder)
fn void multi_byte_divide(BigInt* self, BigInt* other, BigInt* quotient, BigInt* remainder)
{
uint[MAX_LEN] result;
uint[MAX_LEN] r;
@@ -1081,7 +1082,7 @@ fn void BigInt.multi_byte_divide(&self, BigInt* other, BigInt* quotient, BigInt*
quotient.data[y] = 0;
}
quotient.reduce_len();
reduce_len(quotient);
remainder.len = shift_right(&r, remainder_len, shift);

View File

@@ -25,7 +25,7 @@ macro void @loop_over_ai(AddrInfo* ai; @body(NativeSocket fd, AddrInfo* ai))
const Duration POLL_FOREVER = (Duration)-1;
const enum PollSubscribe
constdef PollSubscribe
{
ANY_READ = os::POLLIN,
PRIO_READ = os::POLLPRI,
@@ -44,7 +44,7 @@ const PollSubscribe SUBSCRIBE_ANY_WRITE = (PollSubscribe)os::POLLOUT;
const PollSubscribe SUBSCRIBE_OOB_WRITE = (PollSubscribe)os::POLLWRBAND;
const PollSubscribe SUBSCRIBE_WRITE = (PollSubscribe)os::POLLWRNORM;
const enum PollEvent : ushort
constdef PollEvent : ushort
{
READ_PRIO = os::POLLPRI,
READ_OOB = os::POLLRDBAND,

View File

@@ -18,7 +18,7 @@ const uint EPOLLWAKEUP = EpollEvents.EPOLLWAKEUP;
const uint EPOLLONESHOT = EpollEvents.EPOLLONESHOT;
const uint EPOLLET = EpollEvents.EPOLLET;
const enum EpollEvents : inline uint
constdef EpollEvents : inline uint
{
EPOLLIN = 0x001,
EPOLLPRI = 0x002,

View File

@@ -186,7 +186,7 @@ fn ulong? elf_module_image_base(String path) @local
bool is_little_endian = file.read_byte()! == 1;
// Actually, not supported.
if (!is_little_endian) return backtrace::IMAGE_NOT_FOUND~;
file.seek(0)!;
file.set_cursor(0)!;
if (is_64)
{
Elf64_Ehdr file_header;
@@ -195,7 +195,7 @@ fn ulong? elf_module_image_base(String path) @local
for (isz i = 0; i < file_header.e_phnum; i++)
{
Elf64_Phdr header;
file.seek((usz)file_header.e_phoff + (usz)file_header.e_phentsize * i)!;
file.set_cursor(file_header.e_phoff + (long)file_header.e_phentsize * i)!;
io::read_any(&file, &header)!;
if (header.p_type == PT_PHDR) return header.p_vaddr - header.p_offset;
}
@@ -207,7 +207,7 @@ fn ulong? elf_module_image_base(String path) @local
for (isz i = 0; i < file_header.e_phnum; i++)
{
Elf32_Phdr header;
file.seek(file_header.e_phoff + (usz)file_header.e_phentsize * i)!;
file.set_cursor(file_header.e_phoff + (long)file_header.e_phentsize * i)!;
io::read_any(&file, &header)!;
if (header.p_type == PT_PHDR) return (ulong)header.p_vaddr - header.p_offset;
}

View File

@@ -40,7 +40,7 @@ extern fn CFTypeRef CFType.retain(&self) @cname("CFRetain");
extern fn void CFType.release(&self) @cname("CFRelease");
extern fn CFIndex CFType.getRetainCount(&self) @cname("CFGetRetainCount");
const enum CFStringEncoding : uint
constdef CFStringEncoding : uint
{
INVALID_ID = 0xffffffffU,
MAC_ROMAN = 0,

View File

@@ -1,7 +1,7 @@
module std::os::darwin @if(env::DARWIN) @link("Foundation.framework");
import std::os::macos::cf, std::os::macos::objc, std::io;
const enum NSSearchPathDomainMask : NSUInteger
constdef NSSearchPathDomainMask : NSUInteger
{
USER = 1,
LOCAL = 2,
@@ -10,7 +10,7 @@ const enum NSSearchPathDomainMask : NSUInteger
ALL = 0x0ffff
}
const enum NSSearchPathDirectory : NSUInteger
constdef NSSearchPathDirectory : NSUInteger
{
APPLICATION = 1,
DEMO_APPLICATION,

View File

@@ -88,7 +88,7 @@ enum ApplicationActivationPolicy : (int val) @deprecated("Use NSApplicationActiv
PROHIBITED { 2 },
}
const enum NSApplicationActivationPolicy : inline NSInteger
constdef NSApplicationActivationPolicy : inline NSInteger
{
REGULAR = 0,
ACCESSORY = 1,
@@ -119,7 +119,7 @@ enum BackingStore : (int val) @deprecated("Use NSBackingStoreType.")
BUFFERED { 2 }
}
const enum NSBackingStoreType : inline NSUInteger
constdef NSBackingStoreType : inline NSUInteger
{
RETAINED = 0,
NONRETAINED = 1,
@@ -164,7 +164,7 @@ enum EventType : (long val) @deprecated("Use NSEventType.")
CHANGE_MODE { 38 },
}
const enum NSEventType : inline NSUInteger
constdef NSEventType : inline NSUInteger
{
LEFT_MOUSE_DOWN = 1,
LEFT_MOUSE_UP = 2,
@@ -280,7 +280,7 @@ enum EventMask : (long val) @deprecated("Use NSEventMask.")
ANY { long.max },
}
const enum NSEventMask : inline ulong
constdef NSEventMask : inline ulong
{
LEFT_MOUSE_DOWN = 1ul << NSEventType.LEFT_MOUSE_DOWN,
LEFT_MOUSE_UP = 1ul << NSEventType.LEFT_MOUSE_UP,
@@ -333,7 +333,7 @@ enum EventModifierFlag : (int val) @deprecated("Use NSEventModifierFlags.")
HELP { 1 << 22 },
}
const enum NSEventModifierFlags : inline NSUInteger
constdef NSEventModifierFlags : inline NSUInteger
{
CAPS_LOCK = 1 << 16,
SHIFT = 1 << 17,
@@ -346,7 +346,7 @@ const enum NSEventModifierFlags : inline NSUInteger
DEVICE_INDEPENDENT_FLAGS_MASK = 0xffff0000UL,
}
const enum NSWindowCollectionBehavior : inline NSUInteger
constdef NSWindowCollectionBehavior : inline NSUInteger
{
DEFAULT = 0,
CAN_JOIN_ALL_SPACES = 1 << 0,
@@ -366,7 +366,7 @@ const enum NSWindowCollectionBehavior : inline NSUInteger
CAN_JOIN_ALL_APPLICATIONS = 1 << 18,
}
const enum NSWindowLevel : inline NSInteger
constdef NSWindowLevel : inline NSInteger
{
NORMAL = 0,
FLOATING = 3,
@@ -379,7 +379,7 @@ const enum NSWindowLevel : inline NSInteger
SCREEN_SAVER = 1000,
}
const enum NSWindowStyleMask : inline NSUInteger
constdef NSWindowStyleMask : inline NSUInteger
{
BORDERLESS = 0,
TITLED = 1 << 0,
@@ -396,20 +396,20 @@ const enum NSWindowStyleMask : inline NSUInteger
HUD_WINDOW = 1 << 13
}
const enum NSWindowTabbingMode : inline NSInteger
constdef NSWindowTabbingMode : inline NSInteger
{
AUTOMATIC = 0,
DISALLOWED = 2,
PREFERRED = 1,
}
const enum NSStatusItemLength : inline CGFloat
constdef NSStatusItemLength : inline CGFloat
{
VARIABLE = -1.0,
SQUARE = -2.0
}
const enum NSApplicationTerminateReply : inline NSUInteger
constdef NSApplicationTerminateReply : inline NSUInteger
{
CANCEL = 0,
NOW = 1,

View File

@@ -1,5 +1,5 @@
module std::os::process @if(env::WIN32 || env::POSIX);
import std::io, libc, std::os;
import std::io, libc, std::os, std::net::os;
// This code is based on https://github.com/sheredom/subprocess.h
@@ -321,10 +321,24 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
if (posix::pipe(&stdoutfd)) return FAILED_TO_OPEN_STDOUT~;
if (!options.combined_stdout_stderr && posix::pipe(&stderrfd)) return FAILED_TO_OPEN_STDERR~;
if (options.read_async)
{
((NativeSocket)stdoutfd[0]).set_non_blocking(true)!;
((NativeSocket)stdoutfd[1]).set_non_blocking(true)!;
if (!options.combined_stdout_stderr)
{
((NativeSocket)stderrfd[0]).set_non_blocking(true)!;
((NativeSocket)stderrfd[1]).set_non_blocking(true)!;
}
}
if (posix::spawn_file_actions_addclose(&actions, stdinfd[1])) return FAILED_TO_OPEN_STDIN~;
if (posix::spawn_file_actions_adddup2(&actions, stdinfd[0], libc::STDIN_FD)) return FAILED_TO_OPEN_STDIN~;
if (posix::spawn_file_actions_addclose(&actions, stdinfd[0])) return FAILED_TO_OPEN_STDIN~;
if (posix::spawn_file_actions_addclose(&actions, stdoutfd[0])) return FAILED_TO_OPEN_STDOUT~;
if (posix::spawn_file_actions_adddup2(&actions, stdoutfd[1], libc::STDOUT_FD)) return FAILED_TO_OPEN_STDOUT~;
if (posix::spawn_file_actions_addclose(&actions, stdoutfd[1])) return FAILED_TO_OPEN_STDERR~;
if (options.combined_stdout_stderr)
{
@@ -334,13 +348,14 @@ fn SubProcess? create(String[] command_line, SubProcessOptions options = {}, Str
{
if (posix::spawn_file_actions_addclose(&actions, stderrfd[0])) return FAILED_TO_OPEN_STDERR~;
if (posix::spawn_file_actions_adddup2(&actions, stderrfd[1], libc::STDERR_FD)) return FAILED_TO_OPEN_STDERR~;
if (posix::spawn_file_actions_addclose(&actions, stderrfd[1])) return FAILED_TO_OPEN_STDERR~;
}
}
Pid_t child;
@stack_mem(2048; Allocator mem)
{
ZString* command_line_copy = copy_command_line(mem, command_line);
ZString* command_line_copy = copy_command_line(mem, command_line);
ZString* used_environment = options.inherit_environment ? posix::environ : copy_env(mem, environment);
if (options.search_user_path)
{
@@ -485,10 +500,15 @@ fn usz? read_from_file_win32(CFile file, Win32_HANDLE event_handle, char* buffer
}
return bytes_read;
}
fn usz? read_from_file_posix(CFile file, char* buffer, usz size) @if(env::POSIX) @local
{
isz bytes_read = libc::read(libc::fileno(file), buffer, size);
if (bytes_read < 0) return READ_FAILED~;
if (bytes_read < 0)
{
if (libc::errno() == errno::EAGAIN) return 0; // nothing to read but O_NONBLOCK (async) is used
return READ_FAILED~;
}
return bytes_read;
}
@@ -537,5 +557,6 @@ fn bool? SubProcess.is_running(&self)
fn usz? SubProcess.write_to_stdin(&self, char[] buffer)
{
if (!self.stdin_file) return FAILED_TO_OPEN_STDIN~;
defer try (void)os::native_fflush(self.stdin_file);
return os::native_fwrite(self.stdin_file, buffer);
}

View File

@@ -1,6 +1,6 @@
module std::os::win32 @if(env::WIN32);
const enum Win32_CODEPAGE : Win32_UINT
constdef Win32_CODEPAGE : Win32_UINT
{
IBM037 = 037, // IBM EBCDIC US-Canada
IBM437 = 437, // OEM United States

View File

@@ -1,6 +1,6 @@
module std::os::win32 @if(env::WIN32);
const enum Win32_AllocationType
constdef Win32_AllocationType
{
MEM_COMMIT = 0x00001000,
MEM_RESERVE = 0x00002000,
@@ -12,7 +12,7 @@ const enum Win32_AllocationType
MEM_WRITE_WATCH = 0x00200000
}
const enum Win32_Protect : Win32_DWORD
constdef Win32_Protect : Win32_DWORD
{
PAGE_EXECUTE = 0x10,
PAGE_EXECUTE_READ = 0x20,
@@ -29,7 +29,7 @@ const enum Win32_Protect : Win32_DWORD
PAGE_WRITECOMBINE = 0x400,
}
const enum Win32_FreeType : Win32_DWORD
constdef Win32_FreeType : Win32_DWORD
{
MEM_DECOMMIT = 0x00004000,
MEM_RELEASE = 0x00008000,

View File

@@ -13,7 +13,7 @@ enum Win32_MEM_EXTENDED_PARAMETER_TYPE : CInt
}
alias Win32_PMEM_EXTENDED_PARAMETER_TYPE = Win32_MEM_EXTENDED_PARAMETER_TYPE;
const enum Win32_MEM_EXTENDED_PARAMETER_ATTRIBUTE : Win32_DWORD64
constdef Win32_MEM_EXTENDED_PARAMETER_ATTRIBUTE : Win32_DWORD64
{
<* The allocation is non-pageable. *>
NONPAGED = 0x02,

View File

@@ -142,7 +142,7 @@ fn void NativeTimedMutex.destroy(&mtx)
*mtx = {};
}
fn void? NativeTimedMutex.wait_cond_var(&mtx, uint ms) @local
fn void? _wait_cond_var(NativeTimedMutex *mtx, uint ms) @local
{
if (!win32::sleepConditionVariableSRW(&mtx.cond_var, &mtx.srw_lock, ms, 0))
{
@@ -169,7 +169,7 @@ fn void NativeTimedMutex.lock(&mtx)
while (mtx.locks)
{
mtx.wait_cond_var(win32::INFINITE)!!;
_wait_cond_var(mtx, win32::INFINITE)!!;
}
mtx.locks = 1;
mtx.owner_thread = current_thread;
@@ -206,7 +206,7 @@ fn void? NativeTimedMutex.lock_timeout(&mtx, ulong ms)
{
ulong remaining_ms = remaining.to_ms();
if (remaining_ms > uint.max) remaining_ms = uint.max;
mtx.wait_cond_var((uint)remaining_ms)!;
_wait_cond_var(mtx, (uint)remaining_ms)!;
if (!mtx.locks)
{
// Got the lock

View File

@@ -3,13 +3,13 @@
## 0.7.10 Change list
### Changes / improvements
- Method resolution and `$define` now works together well unless definitions are out of order for real.
- C3 is now using its own LLVM libraries when building releases.
- Method resolution and `$defined` now works together well unless definitions are out of order for real.
- Improve error message when using functions as values #2856
- Improve support for Android with Termux.
- Integrated download of the MSVC SDK when compiling for Windows.
- For `c3c init` with library templates, provide example exported functions. #2898
- `unsigned % signed` and `unsigned / signed` is no longer allowed without explicit casts, except for const denominators. #2928
- New const enum declaration syntax.
- New enum associated value syntax.
- Individual warning settings added.
- Change typedef and const enums to not convert from literals by default.
@@ -17,7 +17,14 @@
- Include actual element count in the error message when the array initializer size does not match the expected size.
- Add `--print-large-functions` for checking which functions likely dominate the compile time.
- Improve error message when providing `alias` with a typeid expression where a type was expected. #2944
- Const enums removed.
- Constdef declarations introduced.
- Properly support `@deprecated` as contract.
- Support deprecating enum values.
- Improve error when trying to use an extern const as a compile time constant. #2969
- `vendor-fetch` command now lists all available packages by default. #2976
- Typekind enums are changed CONST_ENUM -> CONSTDEF, DISTINCT -> TYPEDEF.
### Stdlib changes
- Summarize sort macros as generic function wrappers to reduce the amount of generated code. #2831
- Remove dependency on temp allocator in String.join.
@@ -30,6 +37,10 @@
- Add `array::even`, `array::odd`, and `array::unlace` macros. #2892
- Add discrete and continuous distributions in `std::math::distributions`.
- Add bitorder functions `store_le`, `load_le`, `store_be`, `store_le`.
- Stream functions now use long/ulong rather than isz/usz for seek/available.
- `instream.seek` is replaced by `set_cursor` and `cursor`.
- `instream.available`, `cursor` etc are long/ulong rather than isz/usz to be correct on 32-bit.
- Enable asynchronous, non-blocking reads of subprocess STDOUT/STDERR pipes on POSIX systems.
### Fixes
- Add error message if directory with output file name already exists
@@ -45,10 +56,12 @@
- Improved underlining errors/warnings when unicode is used. #2887
- Fix std::io::Formatter integer issue for large uint128 decimal values.
- `--safe=no` disabled compile-time errors on compile-time known runtime @require checks #2936
- On assert known false, the message was not show for no-args.
- On assert known false, the message was not shown for no-args.
- Adding the incorrect sized vector to a pointer vector would cause a crash.
- Member access on a struct returned by the assignment expression, cause crash #2947
- Trying to slice an indexable type leads to misleading error message #2958
- Warn on use of visibility modifiers on methods. #2962
- Compiler crash using `??` with a `void?` macro #2973
## 0.7.9 Change list

View File

@@ -1,15 +1,13 @@
module gl;
alias BitField = int;
enum BufferBit : int (int value)
constdef BufferBit : int
{
COLOR = 0x00004000,
STENCIL = 0x00000400,
DEPTH = 0x00000100,
}
enum Primitive : int (int value)
constdef Primitive : int
{
POINTS = 0,
LINES = 1,
@@ -23,9 +21,9 @@ enum Primitive : int (int value)
POLYGON = 9,
}
extern fn void clear(BitField mask) @cname("glClear") @public;
extern fn void clear(BufferBit mask) @cname("glClear") @public;
extern fn void begin(BitField mask) @cname("glBegin") @public;
extern fn void begin(BufferBit mask) @cname("glBegin") @public;
extern fn void end() @cname("glEnd") @public;

View File

@@ -45,11 +45,11 @@ alias CameraUpdateFn = fn void(RLCamera2D* camera, Player* player, EnvItem[] env
enum CameraUpdateType : (ZString text, CameraUpdateFn func)
{
CENTER = { "Follow player center", &update_camera_center },
CENTER_INSIDE_MAP = { "Follow player center, but clamp to map edges", &update_camera_center_inside_map },
CENTER_SMOOTH_FOLLOW = { "Follow player center; smoothed", &update_camera_center_smooth_follow },
EVEN_OUT_ON_LANDING = { "Follow player center horizontally; update player center vertically after landing", &update_camera_even_out_on_landing },
PLAYER_BOUNDS_PUSH = { "Player push camera on getting too close to screen edge", &update_camera_player_bounds_push }
CENTER { "Follow player center", &update_camera_center },
CENTER_INSIDE_MAP { "Follow player center, but clamp to map edges", &update_camera_center_inside_map },
CENTER_SMOOTH_FOLLOW { "Follow player center; smoothed", &update_camera_center_smooth_follow },
EVEN_OUT_ON_LANDING { "Follow player center horizontally; update player center vertically after landing", &update_camera_even_out_on_landing },
PLAYER_BOUNDS_PUSH { "Player push camera on getting too close to screen edge", &update_camera_player_bounds_push }
}
//------------------------------------------------------------------------------------

View File

@@ -18,10 +18,10 @@ const int SCREEN_HEIGHT = 450;
enum SnakeDirection : (RLVector2 dir)
{
RIGHT = { SQUARE_SIZE, 0 },
DOWN = { 0, SQUARE_SIZE },
LEFT = { -SQUARE_SIZE, 0 },
UP = { 0, -SQUARE_SIZE }
RIGHT {{ SQUARE_SIZE, 0 }},
DOWN {{ 0, SQUARE_SIZE }},
LEFT {{ -SQUARE_SIZE, 0 }},
UP {{ 0, -SQUARE_SIZE }}
}
struct Snake
{

View File

@@ -97,6 +97,7 @@ typedef struct
WarningLevel deprecation;
WarningLevel methods_not_resolved;
WarningLevel dead_code;
WarningLevel method_visibility;
} Warnings;
typedef enum

View File

@@ -142,6 +142,7 @@ static void usage(bool full)
print_opt("--use-old-compact-eq", "Enable the old ability to use '@compact' to make a struct comparable.");
print_opt("--print-large-functions", "Print functions with large compile size.");
print_opt("--warn-deadcode=<yes|no|error>", "Print warning on dead-code: yes, no, error.");
print_opt("--warn-methodvisibility=<yes|no|error>", "Print warning when methods have ignored visibility attributes.");
print_opt("--warn-methodsnotresolved=<yes|no|error>", "Print warning on methods not resolved when accessed: yes, no, error.");
print_opt("--warn-deprecation=<yes|no|error>", "Print warning when using deprecated code and constructs: yes, no, error.");
}
@@ -444,7 +445,6 @@ static void parse_command(BuildOptions *options)
if (arg_match("vendor-fetch"))
{
options->command = COMMAND_VENDOR_FETCH;
if (at_end() || next_is_opt()) error_exit("error: vendor-fetch needs at least one library.");
while (!at_end() && !next_is_opt())
{
const char *lib = next_arg();
@@ -906,6 +906,11 @@ static void parse_option(BuildOptions *options)
options->warnings.dead_code = parse_opt_select(WarningLevel, argopt, warnings);
return;
}
if ((argopt = match_argopt("warn-methodvisibility")))
{
options->warnings.method_visibility = parse_opt_select(WarningLevel, argopt, warnings);
return;
}
if ((argopt = match_argopt("warn-methodsnotresolved")))
{
options->warnings.methods_not_resolved = parse_opt_select(WarningLevel, argopt, warnings);
@@ -1594,6 +1599,7 @@ BuildOptions parse_arguments(int argc, const char *argv[])
.win_debug = WIN_DEBUG_DEFAULT,
.fp_math = FP_DEFAULT,
.x86_cpu_set = X86CPU_DEFAULT,
.riscv_cpu_set = RISCV_CPU_DEFAULT,
.riscv_abi = RISCV_ABI_DEFAULT,
.memory_environment = MEMORY_ENV_NOT_SET,
.win.crt_linking = WIN_CRT_DEFAULT,
@@ -1666,7 +1672,8 @@ BuildOptions parse_arguments(int argc, const char *argv[])
{
FAIL_WITH_ERR("Missing a compiler command such as 'compile' or 'build'.");
}
if (build_options.arch_os_target_override == ANDROID_AARCH64)
if (build_options.arch_os_target_override == ANDROID_AARCH64 ||
build_options.arch_os_target_override == ANDROID_X86_64)
{
if (!build_options.android.ndk_path)
{

View File

@@ -536,6 +536,7 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
update_warning_if_not_set(&target->warnings.deprecation, WARNING_SILENT);
update_warning_if_not_set(&target->warnings.methods_not_resolved, WARNING_WARN);
update_warning_if_not_set(&target->warnings.dead_code, WARNING_SILENT);
update_warning_if_not_set(&target->warnings.method_visibility, WARNING_WARN);
break;
case VALIDATION_NOT_SET:
target->validation_level = VALIDATION_STRICT;
@@ -544,16 +545,19 @@ static void update_build_target_from_options(BuildTarget *target, BuildOptions *
update_warning_if_not_set(&target->warnings.methods_not_resolved, WARNING_WARN);
update_warning_if_not_set(&target->warnings.deprecation, WARNING_WARN);
update_warning_if_not_set(&target->warnings.dead_code, WARNING_WARN);
update_warning_if_not_set(&target->warnings.method_visibility, WARNING_WARN);
break;
case VALIDATION_OBNOXIOUS:
update_warning_if_not_set(&target->warnings.methods_not_resolved, WARNING_ERROR);
update_warning_if_not_set(&target->warnings.deprecation, WARNING_ERROR);
update_warning_if_not_set(&target->warnings.dead_code, WARNING_ERROR);
update_warning_if_not_set(&target->warnings.method_visibility, WARNING_ERROR);
break;
}
update_warning(&target->warnings.deprecation, options->warnings.deprecation);
update_warning(&target->warnings.dead_code, options->warnings.dead_code);
update_warning(&target->warnings.methods_not_resolved, options->warnings.methods_not_resolved);
update_warning(&target->warnings.method_visibility, options->warnings.method_visibility);
target->print_linking = options->print_linking || options->verbosity_level > 1;

View File

@@ -116,7 +116,7 @@ static bool x86_should_return_type_in_reg(Type *type)
case TYPE_FUNC_RAW:
case TYPE_INTERFACE:
case TYPE_OPTIONAL:
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
case TYPE_ALIAS:
case TYPE_TYPEID:
case TYPE_VECTOR:

View File

@@ -590,7 +590,19 @@ static void init_asm_x86(PlatformTarget* target)
Clobbers rax_mask = clobbers_make(X86_RAX, -1);
Clobbers cc_flag_mask = clobbers_make(X86_CC, -1);
Clobbers rax_cc_mask = clobbers_make_from(cc_flag_mask, X86_RAX, -1);
Clobbers rcx_cc_mask = clobbers_make_from(cc_flag_mask, X86_RCX, -1);
Clobbers rax_rdx_cc_mask = clobbers_make_from(cc_flag_mask, X86_RAX, X86_RDX, -1);
Clobbers xmm_0_7_cc_mask = clobbers_make_from(cc_flag_mask, X86_XMM0, X86_XMM1, X86_XMM2, X86_XMM3, X86_XMM4, X86_XMM5, X86_XMM6, X86_XMM7, -1);
Clobbers xmm_0_2_xmm_4_6_cc_mask = clobbers_make_from(cc_flag_mask, X86_XMM0, X86_XMM1, X86_XMM2, X86_XMM4, X86_XMM5, X86_XMM6, -1);
Clobbers xmm_0_6_cc_mask = clobbers_make_from(cc_flag_mask, X86_XMM0, X86_XMM1, X86_XMM2, X86_XMM3, X86_XMM4, X86_XMM5, X86_XMM6, -1);
Clobbers xmm0_mask = clobbers_make(X86_XMM0, -1);
Clobbers xmm0_cc_mask = clobbers_make_from(cc_flag_mask, X86_XMM0, -1);
Clobbers rax_xmm0_cc_mask = clobbers_make_from(cc_flag_mask, X86_RAX, X86_XMM0, -1);
Clobbers lo16_vec_mask = clobbers_make(X86_XMM0, X86_XMM1, X86_XMM2, X86_XMM3, X86_XMM4, X86_XMM5, X86_XMM6, X86_XMM7,
X86_XMM8, X86_XMM9, X86_XMM10, X86_XMM11, X86_XMM12, X86_XMM13, X86_XMM14, X86_XMM15, -1);
bool is_x64 = target->arch == ARCH_TYPE_X86_64;
if (!is_x64)
{
@@ -610,9 +622,6 @@ static void init_asm_x86(PlatformTarget* target)
reg_instr_clob(target, "adcl", cc_flag_mask, "rw:r32/mem, r32/mem/imm32/immi8");
reg_instr_clob(target, "adcq", cc_flag_mask, "rw:r64/mem, r64/mem/immi32/immi8");
reg_instr_clob(target, "adcxl", cc_flag_mask, "r32, rw:r32/mem");
reg_instr_clob(target, "adcxq", cc_flag_mask, "r64, rw:r64/mem");
reg_instr_clob(target, "addb", cc_flag_mask, "rw:r8/mem, r8/mem/imm8");
reg_instr_clob(target, "addw", cc_flag_mask, "rw:r16/mem, r16/mem/imm16/immi8");
reg_instr_clob(target, "addl", cc_flag_mask, "rw:r32/mem, r32/mem/imm32/immi8");
@@ -626,13 +635,45 @@ static void init_asm_x86(PlatformTarget* target)
reg_instr(target, "vaddps", "w:v128/v256/v512, v128/v256/v512, v128/v256/v512/mem");
reg_instr(target, "vaddsd", "w:v128, v128, v128/mem");
reg_instr(target, "vaddss", "w:v128, v128, v128/mem");
reg_instr(target, "bswapl", "rw:r32");
reg_instr(target, "bswapq", "rw:r64");
reg_instr_clob(target, "cbtw", rax_mask, NULL);
reg_instr_clob(target, "cwtl", rax_mask, NULL);
reg_instr_clob(target, "cltq", rax_mask, NULL);
reg_instr_clob(target, "clc", rax_mask, NULL);
reg_instr_clob(target, "cld", rax_mask, NULL);
reg_instr(target, "clflush", "mem");
reg_instr(target, "cmovaw", "w:r16, r16/mem");
reg_instr(target, "cmoval", "w:r32, r32/mem");
reg_instr(target, "cmovaq", "w:r64, r64/mem");
reg_instr(target, "cmovaew", "w:r16, r16/mem");
reg_instr(target, "cmovael", "w:r32, r32/mem");
reg_instr(target, "cmovaeq", "w:r64, r64/mem");
reg_instr(target, "cmovbw", "w:r16, r16/mem");
reg_instr(target, "cmovbl", "w:r32, r32/mem");
reg_instr(target, "cmovbq", "w:r64, r64/mem");
reg_instr(target, "cmovbew", "w:r16, r16/mem");
reg_instr(target, "cmovbel", "w:r32, r32/mem");
reg_instr(target, "cmovbeq", "w:r64, r64/mem");
reg_instr_clob(target, "cmpxchgb", rax_cc_mask, "rw:r8/mem, r8");
reg_instr_clob(target, "cmpxchgl", rax_cc_mask, "rw:r16/mem, r16");
reg_instr_clob(target, "cmpxchgw", rax_cc_mask, "rw:r32/mem, r32");
reg_instr_clob(target, "cmpxchgq", rax_cc_mask, "rw:r64/mem, r64");
reg_instr_clob(target, "cmpxchg8b", rax_cc_mask, "rw:r64/mem");
reg_instr_clob(target, "cmpxchg16b", rax_cc_mask, "rw:r64/mem");
reg_instr_clob(target, "decb", cc_flag_mask, "r8/mem");
reg_instr_clob(target, "decw", cc_flag_mask, "r16/mem");
reg_instr_clob(target, "decl", cc_flag_mask, "r32/mem");
reg_instr_clob(target, "decq", cc_flag_mask, "r64/mem");
reg_instr_clob(target, "divb", rax_rdx_cc_mask, "r8/mem");
reg_instr_clob(target, "divw", rax_rdx_cc_mask, "r16/mem");
reg_instr_clob(target, "divl", rax_rdx_cc_mask, "r32/mem");
reg_instr_clob(target, "divq", rax_rdx_cc_mask, "r64/mem");
reg_instr_clob(target, "idivb", rax_rdx_cc_mask, "r8/mem");
reg_instr_clob(target, "idivw", rax_rdx_cc_mask, "r16/mem");
reg_instr_clob(target, "idivl", rax_rdx_cc_mask, "r32/mem");
reg_instr_clob(target, "idivq", rax_rdx_cc_mask, "r64/mem");
reg_instr(target, "movb", "w:r8/mem, r8/mem/imm8");
reg_instr(target, "movsbw", "w:r16/mem, r8/mem");
reg_instr(target, "movzbw", "w:r16/mem, r8/mem");
@@ -715,10 +756,123 @@ static void init_asm_x86(PlatformTarget* target)
reg_instr(target, "senduipi", "r64");
reg_instr(target, "uiret", NULL);
reg_instr_clob(target, "xaddb", cc_flag_mask, "rw:r8/mem, rw:r8");
reg_instr_clob(target, "xaddw", cc_flag_mask, "rw:r16/mem, rw:r16");
reg_instr_clob(target, "xaddl", cc_flag_mask, "rw:r32/mem, rw:r32");
reg_instr_clob(target, "xaddq", cc_flag_mask, "rw:r64/mem, rw:r64");
reg_instr(target, "xchgb", "rw:r8/mem, rw:r8/mem");
reg_instr(target, "xchgw", "rw:r16/mem, rw:r16/mem");
reg_instr(target, "xchgl", "rw:r32/mem, rw:r32/mem");
reg_instr(target, "xchgq", "rw:r64/mem, rw:r64/mem");
reg_instr_clob(target, "popcntw", cc_flag_mask, "w:r16, r16/mem");
reg_instr_clob(target, "popcntl", cc_flag_mask, "w:r32, r32/mem");
reg_instr_clob(target, "popcntq", cc_flag_mask, "w:r64, r64/mem");
reg_instr_clob(target, "xgetbv", rax_rdx_cc_mask , NULL);
// BMI1
reg_instr_clob(target, "andn", cc_flag_mask, "w:r32/r64, r32/r64, r32/r64/mem");
reg_instr_clob(target, "bextr", cc_flag_mask, "w:r32/r64, r32/r64/mem, r32/r64");
reg_instr_clob(target, "blsi", cc_flag_mask, "w:r32/r64, r32/r64/mem");
reg_instr_clob(target, "blsmsk", cc_flag_mask, "w:r32/r64, r32/r64/mem");
reg_instr_clob(target, "blsr", cc_flag_mask, "w:r32/r64, r32/r64/mem");
reg_instr_clob(target, "tzcnt", cc_flag_mask, "w:r16/r32/r64, r16/r32/r64/mem");
// LZCNT
reg_instr_clob(target, "lzcnt", cc_flag_mask, "w:r16/r32/r64, r16/r32/r64/mem");
// BMI2
reg_instr(target, "bzhi", "w:r32/r64, r32/r64/mem, r32/r64");
reg_instr(target, "mulx", "w:r32/r64, r32/r64, r32/r64/mem");
reg_instr(target, "pdep", "w:r32/r64, r32/r64, r32/r64/mem");
reg_instr(target, "pext", "w:r32/r64, r32/r64, r32/r64/mem");
reg_instr(target, "rorx", "w:r32/r64, r32/r64/mem, imm8");
reg_instr(target, "sarx", "w:r32/r64, r32/r64/mem, r32/r64");
reg_instr(target, "shlx", "w:r32/r64, r32/r64/mem, r32/r64");
reg_instr(target, "shrx", "w:r32/r64, r32/r64/mem, r32/r64");
// ADX
reg_instr_clob(target, "adcx", cc_flag_mask, "rw:r32/r64, r32/r64/mem");
reg_instr_clob(target, "adox", cc_flag_mask, "rw:r32/r64, r32/r64/mem");
// PCLMULQDQ
reg_instr(target, "pclmulqdq", "rw:v128, v128/mem, imm8");
reg_instr(target, "vpclmulqdq", "w:v128/v256/v512, v128/v256/v512, v128/v256/v512/mem, imm8");
// SSE4.2 and VEX versions (no EVEX PCMPGTQ)
// Wish I could split crc32[l,q] here since it's got weird encodings, but AT&T does it's suffixes off of
// the source here, which I thought was worse. Ideally this has no suffixes anyway.
reg_instr_clob(target, "crc32", cc_flag_mask, "rw:r32/r64, r8/r16/r32/r64/mem");
reg_instr_clob(target, "pcmpestri", rcx_cc_mask, "v128, v128, imm8");
reg_instr_clob(target, "vpcmpestri", rcx_cc_mask, "v128, v128, imm8");
reg_instr_clob(target, "pcmpestrm", xmm0_cc_mask, "v128, v128, imm8");
reg_instr_clob(target, "vpcmpestrm", xmm0_cc_mask, "v128, v128, imm8");
reg_instr_clob(target, "pcmpistri", rcx_cc_mask, "v128, v128, imm8");
reg_instr_clob(target, "vpcmpistri", rcx_cc_mask, "v128, v128, imm8");
reg_instr_clob(target, "popcnt", cc_flag_mask, "w:r16/r32/r64, r16/r32/r64/mem");
reg_instr(target, "pcmpgtq", "rw:v128, v128/mem");
reg_instr(target, "vpcmpgtq", "w:v128/v256, v128/v256, v128/v256/mem");
// VZERO*
reg_instr_clob(target, "vzeroupper", lo16_vec_mask, NULL);
reg_instr_clob(target, "vzeroall", lo16_vec_mask, NULL);
// AES VAES
reg_instr(target, "aesdec", "rw:v128, v128/mem");
reg_instr(target, "vaesdec", "w:v128/v256/v512, v128/v256/v512, v128/v256/v512/mem");
reg_instr(target, "aesdeclast", "rw:v128, v128/mem");
reg_instr(target, "vaesdeclast", "w:v128/v256/v512, v128/v256/v512, v128/v256/v512/mem");
reg_instr(target, "aesenc", "rw:v128, v128/mem");
reg_instr(target, "vaesenc", "w:v128/v256/v512, v128/v256/v512, v128/v256/v512/mem");
reg_instr(target, "aesenclast", "rw:v128, v128/mem");
reg_instr(target, "vaesenclast", "w:v128/v256/v512, v128/v256/v512, v128/v256/v512/mem");
reg_instr(target, "aesimc", "w:v128, v128/mem");
reg_instr(target, "vaesimc", "w:v128, v128/mem");
reg_instr(target, "aeskeygenassist", "w:v128, v128/mem, imm8");
reg_instr(target, "vaeskeygenassist", "w:v128, v128/mem, imm8");
// AESKLE
reg_instr_clob(target, "aesdec128kl", cc_flag_mask, "rw:v128, mem"); // 384 bit mem load
reg_instr_clob(target, "aesdec256kl", cc_flag_mask, "rw:v128, mem"); // 512 bit mem load
reg_instr_clob(target, "aesenc128kl", cc_flag_mask, "rw:v128, mem"); // 384 bit mem load
reg_instr_clob(target, "aesenc256kl", cc_flag_mask, "rw:v128, mem"); // 512 bit mem load
reg_instr_clob(target, "encodekey128", xmm_0_2_xmm_4_6_cc_mask, "r32, r32");
reg_instr_clob(target, "encodekey256", xmm_0_6_cc_mask, "r32, r32");
// AES_WIDE
reg_instr_clob(target, "aesdecwide128kl", xmm_0_7_cc_mask, "mem"); // 384 bit mem load
reg_instr_clob(target, "aesdecwide256kl", xmm_0_7_cc_mask, "mem"); // 512 bit mem load
reg_instr_clob(target, "aesencwide128kl", xmm_0_7_cc_mask, "mem"); // 384 bit mem load
reg_instr_clob(target, "aesencwide256kl", xmm_0_7_cc_mask, "mem"); // 512 bit mem load
// KEY_LOCKER
reg_instr_clob(target, "loadiwkey", rax_xmm0_cc_mask, "v128, v128");
// SHA
reg_instr(target, "sha1msg1", "rw:v128, v128/mem");
reg_instr(target, "sha1msg2", "rw:v128, v128/mem");
reg_instr(target, "sha1nexte", "rw:v128, v128/mem");
reg_instr(target, "sha1rnds4", "rw:v128, v128/mem, imm8");
reg_instr(target, "sha256msg1", "rw:v128, v128/mem");
reg_instr(target, "sha256msg2", "rw:v128, v128/mem");
reg_instr_clob(target, "sha256rnds2", xmm0_mask, "rw:v128, v128/mem");
// SHA512
reg_instr(target, "vsha512msg1", "rw:v256, v128");
reg_instr(target, "vsha512msg2", "rw:v256, v256");
reg_instr(target, "vsha512rnds2", "rw:v256, v256, v128");
// SM3
reg_instr(target, "vsm3msg1", "rw:v128, v128, v128/mem");
reg_instr(target, "vsm3msg2", "rw:v128, v128, v128/mem");
reg_instr(target, "vsm3rnds2", "rw:v128, v128, v128/mem, imm8");
// SM4
reg_instr(target, "vsm4key4", "w:v128/v256, v128/v256, v128/v256/mem");
reg_instr(target, "vsm4rnds4", "w:v128/v256, v128/v256, v128/v256/mem");
// RDRAND
reg_instr_clob(target, "rdrand", cc_flag_mask, "w:r16/r32/r64");
// RDSEED
reg_instr_clob(target, "rdseed", cc_flag_mask, "w:r16/r32/r64");
target->clobber_name_list = X86ClobberNames;
target->extra_clobbers = "~{flags},~{dirflag},~{fspr}";

View File

@@ -68,8 +68,8 @@ Decl *decl_new_with_type(const char *name, SourceSpan span, DeclKind decl_type)
case DECL_ENUM:
kind = TYPE_ENUM;
break;
case DECL_CONST_ENUM:
kind = TYPE_CONST_ENUM;
case DECL_CONSTDEF:
kind = TYPE_CONSTDEF;
break;
case DECL_TYPEDEF:
kind = TYPE_TYPEDEF;
@@ -127,7 +127,7 @@ const char *decl_to_a_name(Decl *decl)
case DECL_ALIAS: case DECL_ALIAS_PATH: case DECL_TYPE_ALIAS: return "an alias";
case DECL_TYPEDEF: return "a distinct type";
case DECL_ENUM: return "an enum";
case DECL_CONST_ENUM: return "a raw enum";
case DECL_CONSTDEF: return "a set of constants";
case DECL_ENUM_CONSTANT: return "an enum value";
case DECL_ERASED: return "an erased declaration";
case DECL_FAULT: return "a fault";
@@ -143,6 +143,7 @@ const char *decl_to_a_name(Decl *decl)
case DECL_INTERFACE: return "an interface";
case DECL_STRUCT: return "a struct";
case DECL_UNION: return "a union";
case DECL_CONTRACT: return "a contract";
case DECL_VAR:
switch (decl->var.kind)
{
@@ -346,7 +347,8 @@ bool decl_may_be_generic(Decl *decl)
case DECL_GENERIC_INSTANCE:
case DECL_IMPORT:
case DECL_LABEL:
case DECL_CONST_ENUM:
case DECL_CONSTDEF:
case DECL_CONTRACT:
return false;
case DECL_ATTRIBUTE:
case DECL_BITSTRUCT:
@@ -548,53 +550,6 @@ bool ast_supports_continue(Ast *stmt)
return stmt->for_stmt.cond || !stmt->flow.skip_first;
}
Ast *ast_contract_has_any(AstId contracts)
{
while (contracts)
{
Ast *current = astptr(contracts);
contracts = current->next;
ASSERT(current->ast_kind == AST_CONTRACT);
switch (current->contract_stmt.kind)
{
case CONTRACT_UNKNOWN:
case CONTRACT_PURE:
case CONTRACT_PARAM:
case CONTRACT_OPTIONALS:
case CONTRACT_ENSURE:
case CONTRACT_REQUIRE:
return current;
case CONTRACT_COMMENT:
continue;
}
}
return NULL;
}
Ast *ast_contract_has_any_non_require(AstId contracts)
{
while (contracts)
{
Ast *current = astptr(contracts);
contracts = current->next;
ASSERT(current->ast_kind == AST_CONTRACT);
switch (current->contract_stmt.kind)
{
case CONTRACT_UNKNOWN:
case CONTRACT_PURE:
case CONTRACT_PARAM:
case CONTRACT_OPTIONALS:
case CONTRACT_ENSURE:
return current;
case CONTRACT_REQUIRE:
case CONTRACT_COMMENT:
continue;
}
}
return NULL;
}
static void scratch_buffer_append_but_mangle_underscore_dot(const char *name, const char *end, const char *suffix)
{
char c;

View File

@@ -95,7 +95,7 @@ static const char *c_type_name(GenContext *c, Type *type)
case TYPE_FUNC_RAW:
case TYPE_ALIAS:
case TYPE_ENUM:
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
case TYPE_UNTYPED_LIST:
case TYPE_INFERRED_ARRAY:
case TYPE_INFERRED_VECTOR:
@@ -149,7 +149,7 @@ static bool c_emit_type_decl(GenContext *c, Type *type)
case TYPE_FUNC_RAW:
case TYPE_ALIAS:
case TYPE_ENUM:
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
case TYPE_UNTYPED_LIST:
case TYPE_INFERRED_ARRAY:
case TYPE_INFERRED_VECTOR:
@@ -654,7 +654,7 @@ static void c_emit_local_decl(GenContext *c, Decl *decl, CValue *value)
case TYPE_POISONED:
case TYPE_VOID:
case TYPE_TYPEDEF:
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
case TYPE_FUNC_RAW:
case TYPE_BITSTRUCT:
case TYPE_ALIAS:
@@ -858,10 +858,6 @@ static void c_emit_stmt(GenContext *c, Ast *stmt)
break;
case AST_NEXTCASE_STMT:
break;
case AST_CONTRACT:
break;
case AST_CONTRACT_FAULT:
break;
}
PRINT("/* TODO */\n");
}

View File

@@ -33,7 +33,7 @@ static inline LoweredType *type_lowering(Type *type)
case TYPE_TYPEDEF:
type = type->decl->distinct->type;
continue;
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
case TYPE_ENUM:
type = enum_inner_type(type);
continue;
@@ -106,7 +106,7 @@ static inline LoweredType *type_lowering_abi(Type *type)
case TYPE_TYPEDEF:
type = type->decl->distinct->type;
continue;
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
case TYPE_ENUM:
type = enum_inner_type(type);
continue;

View File

@@ -4,6 +4,7 @@
#include "compiler_internal.h"
#include "../build/project.h"
#include "../utils/json.h"
#include <compiler_tests/benchmark.h>
#include "../utils/whereami.h"
#if LLVM_AVAILABLE
@@ -1004,6 +1005,50 @@ void vendor_fetch(BuildOptions *options)
const char** fetched_libraries = NULL;
int total_libraries = (int)vec_size(options->libraries_to_fetch);
if (total_libraries == 0)
{
const char *tmp_dir = dir_make_temp_dir();
const char *tmp_file = file_append_path(tmp_dir, "vendor_list.json");
const char *error = download_file("https://api.github.com", "/repos/c3lang/vendor/contents/libraries/", tmp_file);
if (error)
{
error_exit("Failed to fetch library list: %s", error);
}
size_t size;
char *json_str = file_read_all(tmp_file, &size);
JsonParser parser;
json_init_string(&parser, json_str);
JSONObject *obj = json_parse(&parser);
if (!obj || obj->type != J_ARRAY)
{
error_exit("Failed to parse library list.");
}
printf("Available vendor libraries:\n");
FOREACH(JSONObject *, entry, obj->elements)
{
JSONObject *name = json_map_get(entry, "name");
JSONObject *type = json_map_get(entry, "type");
if (name && type && str_eq(type->str, "dir"))
{
const char *n = name->str;
if (n[0] == '.') continue;
if (str_has_suffix(n, ".c3l"))
{
printf(" %s\n", str_remove_suffix(n, ".c3l"));
}
else
{
printf(" %s\n", n);
}
}
}
file_delete_file(tmp_file);
file_delete_dir(tmp_dir);
return;
}
for(int i = 0; i < total_libraries; i++)
{
const char *lib = options->libraries_to_fetch[i];
@@ -1784,6 +1829,7 @@ static bool is_posix(OsType os)
case OS_TYPE_FREEBSD:
case OS_TYPE_OPENBSD:
case OS_TYPE_SOLARIS:
case OS_TYPE_ANDROID:
return true;
case OS_TYPE_WIN32:
case OS_TYPE_WASI:

View File

@@ -562,10 +562,10 @@ typedef struct
OperatorOverload operator : 6;
Signature signature;
AstId body;
AstId docs;
DeclId docs;
union
{
struct
struct // Function related
{
bool attr_inline : 1;
bool attr_noinline : 1;
@@ -593,7 +593,7 @@ typedef struct
Decl **lambda_ct_parameters;
};
};
struct
struct // Macro related
{
DeclId body_param;
CompilationUnit *unit;
@@ -604,7 +604,7 @@ typedef struct
typedef struct
{
Signature signature;
AstId docs;
DeclId docs;
} FnTypeDecl;
@@ -617,8 +617,8 @@ typedef struct
typedef struct
{
const char **parameters;
AstId contracts;
unsigned id;
Expr **requires;
Decl **instances;
Decl *owner;
Decl **decls;
@@ -655,6 +655,34 @@ typedef struct
};
} DefineDecl;
typedef struct
{
SourceSpan span;
const char *name;
InOutModifier modifier : 4;
bool by_ref : 1;
} ContractParam;
typedef struct
{
Expr *decl_exprs;
const char *comment;
const char *expr_string;
} ExprContract;
typedef struct
{
Expr **requires;
Expr **ensures;
ContractParam *params;
bool pure;
union
{
Expr **opt_returns;
Decl **opt_returns_resolved;
};
} ContractsDecl;
typedef struct
{
Path *alias_path;
@@ -775,6 +803,7 @@ typedef struct Decl_
Ast *ct_echo_decl;
Decl** ct_else_decl;
Decl** decls;
ContractsDecl contracts_decl;
DefineDecl define_decl;
ModuleAliasDecl module_alias_decl;
EnumConstantDecl enum_constant;
@@ -1272,6 +1301,7 @@ struct Expr_
ExprBuiltin builtin_expr; // 16
ExprCall call_expr; // 40
ExprCast cast_expr; // 12
ExprContract contract_expr;
ExprUnresolvedCatch unresolved_catch_expr; // 24
ExprCatch catch_expr; // 24
Expr** cond_expr; // 8
@@ -1565,49 +1595,6 @@ typedef struct
} AstAssertStmt;
typedef struct
{
bool resolved;
bool expanding;
union
{
Expr *expr;
Decl *decl;
};
} AstDocFault;
typedef struct AstDocDirective_
{
ContractKind kind : 4;
union
{
struct
{
const char *name;
SourceSpan span;
InOutModifier modifier : 4;
bool by_ref : 1;
} param;
Ast **faults;
struct
{
Expr *decl_exprs;
const char *comment;
const char *expr_string;
} contract;
struct
{
const char *directive_name;
const char *rest_of_line;
} generic;
struct
{
const char *string;
size_t strlen;
};
};
} AstContractStmt;
typedef struct Ast_
{
SourceSpan span;
@@ -1625,8 +1612,6 @@ typedef struct Ast_
AstCompoundStmt compound_stmt; // 12
AstId ct_compound_stmt;
AstContinueBreakStmt contbreak_stmt;// 24
AstContractStmt contract_stmt; // 32
AstDocFault contract_fault; // 24
AstId ct_else_stmt; // 4
AstCtTypeAssignStmt ct_type_assign_stmt;
AstCtForeachStmt ct_foreach_stmt; // 40
@@ -2174,8 +2159,6 @@ bool ast_is_not_empty(Ast *ast);
bool ast_is_compile_time(Ast *ast);
bool ast_supports_continue(Ast *stmt);
Ast *ast_contract_has_any(AstId contracts);
Ast *ast_contract_has_any_non_require(AstId contracts);
INLINE void ast_append(AstId **succ, Ast *next);
INLINE void ast_prepend(AstId *first, Ast *ast);
INLINE bool ast_ok(Ast *ast);
@@ -2299,6 +2282,7 @@ UNUSED bool i128_get_bit(const Int128 *op, int bit);
void copy_begin(void);
void copy_end(void);
Expr *copy_expr_single(Expr *source_expr);
Expr **copy_exprlist_macro(Expr **source_expr_list);
Decl **copy_decl_list_single(Decl **decl_list);
Decl **copy_decl_list_single_for_generic(Decl **decl_list, Decl *generic_instance);
Attr **copy_attributes_single(Attr** attr_list);
@@ -2907,7 +2891,7 @@ INLINE bool type_may_implement_interface(Type *type)
case TYPE_STRUCT:
case TYPE_UNION:
case TYPE_ENUM:
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
case TYPE_TYPEDEF:
case TYPE_BITSTRUCT:
return true;
@@ -3006,7 +2990,7 @@ INLINE bool type_is_atomic(Type *type_flat)
case ALL_SIGNED_INTS:
case ALL_FLOATS:
case TYPE_ENUM:
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
case TYPE_ANYFAULT:
case TYPE_TYPEID:
case TYPE_BOOL:
@@ -3107,7 +3091,7 @@ INLINE const char *type_invalid_storage_type_name(Type *type)
INLINE Type *enum_inner_type(Type *enum_type)
{
assert(enum_type->type_kind == TYPE_ENUM || enum_type->type_kind == TYPE_CONST_ENUM);
assert(enum_type->type_kind == TYPE_ENUM || enum_type->type_kind == TYPE_CONSTDEF);
return enum_type->decl->enums.type_info->type;
}
@@ -3174,7 +3158,7 @@ INLINE Type *type_flatten_for_bitstruct(Type *type)
{
type = type->decl->distinct->type;
}
if (type->type_kind == TYPE_ENUM || type->type_kind == TYPE_CONST_ENUM)
if (type->type_kind == TYPE_ENUM || type->type_kind == TYPE_CONSTDEF)
{
type = enum_inner_type(type)->canonical;
goto RETRY;
@@ -3233,7 +3217,7 @@ static inline Type *type_base(Type *type)
type = type->decl->distinct->type;
break;
case TYPE_ENUM:
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
type = enum_inner_type(type);
break;
case TYPE_OPTIONAL:
@@ -3250,7 +3234,7 @@ static inline Type *type_base(Type *type)
static const bool is_distinct_like[TYPE_LAST + 1] = {
[TYPE_ENUM] = true,
[TYPE_CONST_ENUM] = true,
[TYPE_CONSTDEF] = true,
[TYPE_TYPEDEF] = true
};
@@ -3305,7 +3289,7 @@ static inline Type *type_flatten_and_inline(Type *type)
case TYPE_TYPEDEF:
type = type->decl->distinct->type;
continue;
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
type = type->decl->enums.type_info->type;
continue;
case TYPE_ENUM:
@@ -3337,7 +3321,7 @@ static inline Type *type_flat_distinct_enum_inline(Type *type)
if (!decl->is_substruct) return type;;
type = decl->distinct->type;
continue;
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
decl = type->decl;
if (!decl->is_substruct) return type;
type = decl->enums.type_info->type;
@@ -3362,7 +3346,7 @@ INLINE bool type_is_user_defined(Type *type)
{
static const bool user_defined_types[TYPE_LAST + 1] = {
[TYPE_ENUM] = true,
[TYPE_CONST_ENUM] = true,
[TYPE_CONSTDEF] = true,
[TYPE_STRUCT] = true,
[TYPE_FUNC_RAW] = true,
[TYPE_UNION] = true,
@@ -3404,7 +3388,7 @@ static inline Type *type_flatten_to_int(Type *type)
case TYPE_BITSTRUCT:
type = type->decl->strukt.container_type->type;
break;
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
type = type->decl->enums.type_info->type;
break;
case TYPE_ENUM:
@@ -3431,7 +3415,7 @@ static inline CanonicalType *type_distinct_inline(Type *type)
case TYPE_ENUM:
if (!type->decl->is_substruct) return type;
FALLTHROUGH;
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
type = enum_inner_type(type);
break;
case TYPE_TYPEDEF:
@@ -3454,7 +3438,7 @@ static inline FlatType *type_flatten(Type *type)
type = type->canonical;
switch (type->type_kind)
{
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
type = enum_inner_type(type);
break;
case TYPE_TYPEDEF:
@@ -3485,7 +3469,7 @@ static inline Type *type_flatten_no_export(Type *type)
if (type->decl->is_export) return type;
type = type->decl->distinct->type;
break;
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
if (type->decl->is_export) return type;
type = enum_inner_type(type);
break;
@@ -3595,7 +3579,7 @@ static inline Type *type_flat_for_arithmethics(Type *type)
case TYPE_OPTIONAL:
type = type->optional;
continue;
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
case TYPE_TYPEDEF:
inner = type_inline(type);
if (type->decl->is_substruct)
@@ -3978,6 +3962,7 @@ static inline void expr_set_span(Expr *expr, SourceSpan loc)
case EXPR_RVALUE:
case EXPR_CT_SUBSCRIPT:
case EXPR_IOTA_DECL:
case EXPR_CONTRACT:
break;
}
}
@@ -4328,7 +4313,7 @@ INLINE void expr_rewrite_const_int(Expr *expr, Type *type, uint64_t v)
expr->type = type;
expr->resolve_status = RESOLVE_DONE;
TypeKind kind = type_flatten(type)->type_kind;
(&expr->const_expr)->ixx.i.high = 0;
expr->const_expr.ixx.i.high = 0;
if (type_kind_is_signed(kind))
{
if (v > (uint64_t)INT64_MAX) (&expr->const_expr)->ixx.i.high = UINT64_MAX;
@@ -4350,10 +4335,10 @@ INLINE void expr_rewrite_const_int(Expr *expr, Type *type, uint64_t v)
break;
}
}
(&expr->const_expr)->ixx.i.low = v;
(&expr->const_expr)->ixx.type = kind;
(&expr->const_expr)->is_character = false;
(&expr->const_expr)->const_kind = CONST_INTEGER;
expr->const_expr.ixx.i.low = v;
expr->const_expr.ixx.type = kind;
expr->const_expr.is_character = false;
expr->const_expr.const_kind = CONST_INTEGER;
}
INLINE void expr_rewrite_to_int_to_float(Expr *expr, Type *type)

View File

@@ -159,12 +159,13 @@ void decl_register(CompilationUnit *unit, Decl *decl)
case DECL_IMPORT:
case DECL_LABEL:
case DECL_POISONED:
case DECL_CONTRACT:
UNREACHABLE_VOID
case DECL_ATTRIBUTE:
case DECL_BITSTRUCT:
case DECL_TYPEDEF:
case DECL_ENUM:
case DECL_CONST_ENUM:
case DECL_CONSTDEF:
case DECL_STRUCT:
case DECL_TYPE_ALIAS:
case DECL_UNION:
@@ -273,7 +274,7 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl)
vec_add(unit->generic_defines, decl);
decl_register(unit, decl);
return;
case DECL_CONST_ENUM:
case DECL_CONSTDEF:
case DECL_ENUM:
ASSERT(decl->name);
vec_add(unit->enums, decl);
@@ -293,6 +294,7 @@ void unit_register_global_decl(CompilationUnit *unit, Decl *decl)
case DECL_GENERIC_INSTANCE:
case DECL_IMPORT:
case DECL_LABEL:
case DECL_CONTRACT:
UNREACHABLE_VOID
case DECL_CT_EXEC:
case DECL_CT_INCLUDE:

View File

@@ -193,6 +193,13 @@ Expr *copy_expr_single(Expr *source_expr)
return expr;
}
Expr **copy_exprlist_macro(Expr **source_expr_list)
{
ASSERT(copy_struct.copy_in_use);
Expr **list = copy_expr_list(&copy_struct, source_expr_list);
return list;
}
void copy_range(CopyStruct *c, Range *range)
{
switch (range->range_type)
@@ -306,6 +313,9 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr)
MACRO_COPY_EXPR(expr->two_expr.first);
MACRO_COPY_EXPR(expr->two_expr.last);
return expr;
case EXPR_CONTRACT:
MACRO_COPY_EXPR(expr->contract_expr.decl_exprs);
return expr;
case EXPR_TYPECALL:
case EXPR_CT_SUBSCRIPT:
UNREACHABLE
@@ -607,24 +617,6 @@ Expr *copy_expr(CopyStruct *c, Expr *source_expr)
UNREACHABLE
}
void doc_ast_copy(CopyStruct *c, AstContractStmt *doc)
{
switch (doc->kind)
{
case CONTRACT_REQUIRE:
case CONTRACT_ENSURE:
MACRO_COPY_EXPR(doc->contract.decl_exprs);
break;
case CONTRACT_OPTIONALS:
MACRO_COPY_AST_LIST(doc->faults);
break;
case CONTRACT_PARAM:
case CONTRACT_PURE:
case CONTRACT_UNKNOWN:
case CONTRACT_COMMENT:
break;
}
}
static void copy_expr_asm_arg(CopyStruct *c, ExprAsmArg *arg)
{
@@ -683,19 +675,6 @@ RETRY:
case AST_DECLS_STMT:
MACRO_COPY_DECL_LIST(ast->decls_stmt);
break;
case AST_CONTRACT_FAULT:
if (ast->contract_fault.resolved)
{
fixup_decl(c, &ast->contract_fault.decl);
}
else
{
MACRO_COPY_EXPR(ast->contract_fault.expr);
}
break;
case AST_CONTRACT:
doc_ast_copy(c, &source->contract_stmt);
break;
case AST_ASM_BLOCK_STMT:
if (ast->asm_block_stmt.is_string)
{
@@ -1079,6 +1058,18 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
case DECL_GENERIC:
case DECL_GENERIC_INSTANCE:
UNREACHABLE;
case DECL_CONTRACT:
MACRO_COPY_EXPR_LIST(copy->contracts_decl.requires);
MACRO_COPY_EXPR_LIST(copy->contracts_decl.ensures);
if (copy->resolve_status == RESOLVE_DONE)
{
MACRO_COPY_DECL_LIST(copy->contracts_decl.opt_returns_resolved);
}
else
{
MACRO_COPY_EXPR_LIST(copy->contracts_decl.opt_returns);
}
break;
case DECL_INTERFACE:
copy_decl_type(copy);
MACRO_COPY_TYPE_LIST(copy->interfaces);
@@ -1117,7 +1108,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
break;
case DECL_FAULT:
break;
case DECL_CONST_ENUM:
case DECL_CONSTDEF:
copy_decl_type(copy);
MACRO_COPY_TYPE_LIST(copy->interfaces);
MACRO_COPY_DECL_METHODS(copy->method_table);
@@ -1139,7 +1130,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
case DECL_FUNC:
copy_decl_type(copy);
MACRO_COPY_TYPEID(copy->func_decl.type_parent);
MACRO_COPY_ASTID(copy->func_decl.docs);
MACRO_COPY_DECLID(copy->func_decl.docs);
copy_signature_deep(c, &copy->func_decl.signature);
MACRO_COPY_ASTID(copy->func_decl.body);
break;
@@ -1204,7 +1195,7 @@ Decl *copy_decl(CopyStruct *c, Decl *decl)
case DECL_ALIAS_PATH:
break;
case DECL_MACRO:
MACRO_COPY_ASTID(copy->func_decl.docs);
MACRO_COPY_DECLID(copy->func_decl.docs);
MACRO_COPY_TYPEID(decl->func_decl.type_parent);
copy_signature_deep(c, &copy->func_decl.signature);
MACRO_COPY_ASTID(decl->func_decl.body);

View File

@@ -317,7 +317,7 @@ void print_deprecation_at(SourceSpan loc, const char *message, ...)
if (!compiler.build.lsp_output && !deprecation_hint)
{
deprecation_hint = true;
eprintf("HINT: You may use --silence-deprecation to silence deprecation warnings.\n\n");
eprintf("HINT: You may use --warn-deprecation=no to silence deprecation warnings.\n\n");
}
va_end(args);
}

View File

@@ -553,8 +553,6 @@ typedef enum
AST_CASE_STMT,
AST_COMPOUND_STMT,
AST_CONTINUE_STMT,
AST_CONTRACT,
AST_CONTRACT_FAULT,
AST_CT_ASSERT,
AST_CT_COMPOUND_STMT,
AST_CT_ECHO_STMT,
@@ -973,17 +971,6 @@ typedef enum
CONST_MEMBER,
} ConstKind;
typedef enum
{
CONTRACT_UNKNOWN,
CONTRACT_COMMENT,
CONTRACT_PURE,
CONTRACT_REQUIRE,
CONTRACT_PARAM,
CONTRACT_OPTIONALS,
CONTRACT_ENSURE,
} ContractKind;
typedef enum
{
CONV_NO = -1,
@@ -1034,6 +1021,7 @@ typedef enum
DECL_CT_EXEC,
DECL_CT_INCLUDE,
DECL_DECLARRAY,
DECL_CONTRACT,
DECL_ALIAS,
DECL_ALIAS_PATH,
DECL_TYPEDEF,
@@ -1050,7 +1038,7 @@ typedef enum
DECL_LABEL,
DECL_MACRO,
DECL_INTERFACE,
DECL_CONST_ENUM,
DECL_CONSTDEF,
DECL_STRUCT,
DECL_TYPE_ALIAS,
DECL_UNION,
@@ -1117,6 +1105,7 @@ typedef enum
EXPR_COMPOUND_LITERAL,
EXPR_COND,
EXPR_CONST,
EXPR_CONTRACT,
EXPR_CT_ARG,
EXPR_CT_ASSIGNABLE,
EXPR_CT_CALL,
@@ -1225,7 +1214,7 @@ typedef enum
INTROSPECT_TYPE_ANYFAULT = 6,
INTROSPECT_TYPE_ANY = 7,
INTROSPECT_TYPE_ENUM = 8,
INTROSPECT_TYPE_CONST_ENUM = 9,
INTROSPECT_TYPE_CONSTDEF = 9,
INTROSPECT_TYPE_STRUCT = 10,
INTROSPECT_TYPE_UNION = 11,
INTROSPECT_TYPE_BITSTRUCT = 12,
@@ -1579,7 +1568,7 @@ typedef enum
TOKEN_BREAK,
TOKEN_CASE,
TOKEN_CATCH,
TOKEN_CENUM,
TOKEN_CONSTDEF,
TOKEN_CONST,
TOKEN_CONTINUE,
TOKEN_DEFAULT,
@@ -1754,7 +1743,7 @@ typedef enum
TYPE_FUNC_PTR,
TYPE_POINTER,
TYPE_TYPEDEF,
TYPE_CONST_ENUM,
TYPE_CONSTDEF,
TYPE_ENUM,
TYPE_FUNC_RAW,
TYPE_STRUCT,
@@ -2141,7 +2130,7 @@ typedef enum
case DECL_DECLARRAY: case DECL_ATTRIBUTE: case DECL_LABEL: \
case DECL_ALIAS: case DECL_CT_ASSERT: case DECL_CT_EXEC: case DECL_FAULT: \
case DECL_CT_ECHO: case DECL_CT_INCLUDE: case DECL_GROUP: \
case DECL_BODYPARAM: case DECL_VAR: case DECL_ENUM_CONSTANT: \
case DECL_BODYPARAM: case DECL_VAR: case DECL_ENUM_CONSTANT: case DECL_CONTRACT: \
case DECL_POISONED: case DECL_ALIAS_PATH: case DECL_GENERIC: case DECL_GENERIC_INSTANCE
// -- Expr helper macros
@@ -2149,7 +2138,7 @@ typedef enum
case EXPR_CT_DEFINED: \
case EXPR_CT_ASSIGNABLE: case EXPR_CT_IS_CONST: \
case EXPR_CT_ARG: case EXPR_TYPEINFO: case EXPR_HASH_IDENT: \
case EXPR_COMPILER_CONST: case EXPR_CT_CALL: \
case EXPR_COMPILER_CONST: case EXPR_CT_CALL: case EXPR_CONTRACT: \
case EXPR_SPLAT: case EXPR_STRINGIFY: case EXPR_TYPECALL: \
case EXPR_CT_EVAL: case EXPR_MAYBE_DEREF
@@ -2188,7 +2177,7 @@ static_assert(EXPR_LAST < 128, "Too many expression types");
#define LOWERED_TYPES CT_TYPES: case TYPE_ENUM: case TYPE_ALIAS: case TYPE_TYPEID: \
case TYPE_TYPEDEF: case TYPE_ANYFAULT: case TYPE_BITSTRUCT: \
case TYPE_OPTIONAL: case TYPE_INTERFACE: case TYPE_CONST_ENUM
case TYPE_OPTIONAL: case TYPE_INTERFACE: case TYPE_CONSTDEF
#define CT_TYPES TYPE_TYPEINFO: case TYPE_INFERRED_ARRAY: case TYPE_INFERRED_VECTOR: case TYPE_UNTYPED_LIST: \
case TYPE_POISONED: case TYPE_MEMBER: case TYPE_WILDCARD

View File

@@ -490,6 +490,7 @@ bool expr_is_runtime_const(Expr *expr)
case EXPR_ASM:
case EXPR_SUBSCRIPT_ASSIGN:
case EXPR_NAMED_ARGUMENT:
case EXPR_CONTRACT:
UNREACHABLE
case EXPR_NOP:
return true;
@@ -687,7 +688,7 @@ void expr_rewrite_to_const_zero(Expr *expr, Type *type)
expr->const_expr.fault = NULL;
expr->resolve_status = RESOLVE_DONE;
break;
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
expr_rewrite_const_int(expr, type, 0);
return;
case TYPE_ENUM:
@@ -800,6 +801,7 @@ bool expr_is_pure(Expr *expr)
switch (expr->expr_kind)
{
case UNRESOLVED_EXPRS:
case EXPR_CONTRACT:
UNREACHABLE
case EXPR_BUILTIN:
case EXPR_BENCHMARK_HOOK:

View File

@@ -164,7 +164,7 @@ static void header_print_type(HeaderContext *c, Type *type)
case TYPE_STRUCT:
case TYPE_UNION:
case TYPE_ENUM:
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
PRINTF("%s", decl_get_extname(type->decl));
return;
case TYPE_BITSTRUCT:
@@ -554,7 +554,7 @@ RETRY:
case TYPE_ENUM:
header_gen_enum(c, 0, type->decl);
return;
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
// TODO;
type = type_flatten(type);
goto RETRY;

View File

@@ -17,17 +17,18 @@
#define FOREACH_DECL_END } } }
#define INSERT_COMMA do { if (first) { first = false; } else { fputs(",\n", file); } } while(0)
static bool emit_docs(FILE *file, AstId contracts, int tabs)
static bool emit_docs(FILE *file, DeclId contracts, int tabs)
{
if (!contracts) return false;
Ast *ast = astptr(contracts);
if (ast->contract_stmt.kind != CONTRACT_COMMENT) return false;
return false;
/*
if (!contract->contracts_decl.comment) return false;
for (int i = 0; i < tabs; i++) PRINT("\t");
PRINT("\"comment\": \"");
bool last_is_whitespace = true;
for (size_t i = 0; i < ast->contract_stmt.strlen; i++)
for (size_t i = 0; i < contract->contracts_decl.comment_len; i++)
{
unsigned char c = ast->contract_stmt.string[i];
unsigned char c = contract->contracts_decl.comment[i];
if (char_is_whitespace(c) || c < 31)
{
if (last_is_whitespace) continue;
@@ -56,7 +57,7 @@ static bool emit_docs(FILE *file, AstId contracts, int tabs)
}
}
PRINT("\"");
return true;
return true;*/
}
static inline void emit_modules(FILE *file)
{
@@ -95,7 +96,7 @@ static inline const char *decl_type_to_string(Decl *type)
case DECL_STRUCT: return "struct";
case DECL_UNION: return "union";
case DECL_TYPE_ALIAS: return "type_alias";
case DECL_CONST_ENUM: return "raw_enum";
case DECL_CONSTDEF: return "constdef";
case DECL_BODYPARAM:
case DECL_DECLARRAY:
case DECL_ERASED:
@@ -104,6 +105,7 @@ static inline const char *decl_type_to_string(Decl *type)
case DECL_VAR:
case DECL_GENERIC:
case DECL_GENERIC_INSTANCE:
case DECL_CONTRACT:
UNREACHABLE
}
UNREACHABLE

View File

@@ -355,7 +355,9 @@ static const char *get_linux_crt_arch_glob(void)
case LINUX_AARCH64:
return "/usr/lib/aarch64*linux*/crt1.o";
case LINUX_RISCV32:
return "/usr/lib/riscv32*linux*/crt1.o";
case LINUX_RISCV64:
return "/usr/lib/riscv64*linux*/crt1.o";
default:
return "/usr/lib/*/crt1.o";
}
@@ -373,7 +375,9 @@ static const char *get_linux_crt_begin_arch_glob(void)
case LINUX_AARCH64:
return "/usr/lib/gcc/aarch64*linux*/*/crtbegin.o";
case LINUX_RISCV32:
return "/usr/lib/gcc/riscv32*linux*/*/crtbegin.o";
case LINUX_RISCV64:
return "/usr/lib/gcc/riscv64*linux*/*/crtbegin.o";
default:
return "/usr/lib/gcc/*/*/crtbegin.o";
}
@@ -382,29 +386,60 @@ static const char *get_linux_crt_begin_arch_glob(void)
static const char *find_linux_crt(void)
{
if (compiler.build.linuxpaths.crt) return compiler.build.linuxpaths.crt;
const char *arch_linux_crt1_path = "/usr/lib/crt1.o";
const char *arch_linux_64_crt1_path = "/usr/lib64/crt1.o";
if (file_exists(arch_linux_crt1_path))
{
const char *arch_linux_path = "/usr/lib";
INFO_LOG("Found crt at %s", arch_linux_path);
return arch_linux_path;
}
if (file_exists(arch_linux_64_crt1_path))
{
const char* arch_linux_path = "/usr/lib64";
INFO_LOG("Found crt at %s", arch_linux_path);
return arch_linux_path;
}
const char *arch_glob_path = get_linux_crt_arch_glob();
const char *path = find_arch_glob_path(arch_glob_path, 6);
if (!path)
if (compiler.platform.arch == ARCH_TYPE_RISCV64 || compiler.platform.arch == ARCH_TYPE_RISCV32)
{
INFO_LOG("No crt in /usr/{lib,lib64}/*/");
return NULL;
const char *is_64 = compiler.platform.arch == ARCH_TYPE_RISCV64 ? "64" : "32";
char *p1 = str_printf("/usr/*riscv%s*linux*/usr/lib/crt1.o", is_64);
const char *path = find_arch_glob_path(p1, 6);
if (path)
{
INFO_LOG("Found crt at %s", path);
return path;
}
char *p2 = str_printf("/usr/lib/riscv%s*linux*/crt1.o", is_64);
path = find_arch_glob_path(p2, 6);
if (path)
{
INFO_LOG("Found crt at %s", path);
return path;
}
}
INFO_LOG("Found crt at %s", path);
return path;
else
{
const char *path = find_arch_glob_path(arch_glob_path, 6);
if (path)
{
INFO_LOG("Found crt at %s", path);
return path;
}
}
bool is_host_arch = compiler.platform.arch == target_host_arch();
if (is_host_arch)
{
const char *arch_linux_crt1_path = "/usr/lib/crt1.o";
const char *arch_linux_64_crt1_path = "/usr/lib64/crt1.o";
if (file_exists(arch_linux_crt1_path))
{
const char *arch_linux_path = "/usr/lib";
INFO_LOG("Found crt at %s", arch_linux_path);
return arch_linux_path;
}
if (file_exists(arch_linux_64_crt1_path))
{
const char* arch_linux_path = "/usr/lib64";
INFO_LOG("Found crt at %s", arch_linux_path);
return arch_linux_path;
}
}
INFO_LOG("No crt found in standard paths or via globs.");
return NULL;
}
static const char *find_linux_crt_begin(void)
@@ -423,45 +458,57 @@ static const char *find_linux_crt_begin(void)
static const char *find_linux_ld(void)
{
if (compiler.platform.environment_type == ENV_TYPE_ANDROID) return "--dynamic-linker=/system/ld-android.so";
if (compiler.platform.environment_type == ENV_TYPE_ANDROID) return "/system/ld-android.so";
switch (compiler.build.linuxpaths.libc)
{
case LINUX_LIBC_MUSL:
switch (compiler.platform.arch)
{
case ARCH_TYPE_ARM: return "--dynamic-linker=/lib/ld-musl-arm.so.1";
case ARCH_TYPE_ARMB: return "--dynamic-linker=/lib/ld-musl-armeb.so.1";
case ARCH_TYPE_AARCH64: return "--dynamic-linker=/lib/ld-musl-aarch64.so.1";
case ARCH_TYPE_AARCH64_BE: return "--dynamic-linker=/lib/ld-musl-aarch64_be.so.1";
case ARCH_TYPE_MIPS: return "--dynamic-linker=/lib/ld-musl-mips.so.1";
case ARCH_TYPE_MIPSEL: return "--dynamic-linker=/lib/ld-musl-mipsel.so.1";
case ARCH_TYPE_MIPS64: return "--dynamic-linker=/lib/ld-musl-mips64.so.1";
case ARCH_TYPE_MIPS64EL: return "--dynamic-linker=/lib/ld-musl-mips64el.so.1";
case ARCH_TYPE_PPC: return "--dynamic-linker=/lib/ld-musl-powerpc.so.1";
case ARCH_TYPE_PPC64: return "--dynamic-linker=/lib/ld-musl-powerpc64.so.1";
case ARCH_TYPE_RISCV32: return "--dynamic-linker=/lib/ld-musl-riscv32.so.1";
case ARCH_TYPE_RISCV64: return "--dynamic-linker=/lib/ld-musl-riscv64.so.1";
case ARCH_TYPE_X86: return "--dynamic-linker=/lib/ld-musl-i386.so.1";
case ARCH_TYPE_X86_64: return "--dynamic-linker=/lib/ld-musl-x86_64.so.1";
default: return "--dynamic-linker=/lib/ld-musl-unknown.so.1"; // a placeholder for now
case ARCH_TYPE_ARM: return "/lib/ld-musl-arm.so.1";
case ARCH_TYPE_ARMB: return "/lib/ld-musl-armeb.so.1";
case ARCH_TYPE_AARCH64: return "/lib/ld-musl-aarch64.so.1";
case ARCH_TYPE_AARCH64_BE: return "/lib/ld-musl-aarch64_be.so.1";
case ARCH_TYPE_MIPS: return "/lib/ld-musl-mips.so.1";
case ARCH_TYPE_MIPSEL: return "/lib/ld-musl-mipsel.so.1";
case ARCH_TYPE_MIPS64: return "/lib/ld-musl-mips64.so.1";
case ARCH_TYPE_MIPS64EL: return "/lib/ld-musl-mips64el.so.1";
case ARCH_TYPE_PPC: return "/lib/ld-musl-powerpc.so.1";
case ARCH_TYPE_PPC64: return "/lib/ld-musl-powerpc64.so.1";
case ARCH_TYPE_RISCV32: return "/lib/ld-musl-riscv32.so.1";
case ARCH_TYPE_RISCV64: return "/lib/ld-musl-riscv64.so.1";
case ARCH_TYPE_X86: return "/lib/ld-musl-i386.so.1";
case ARCH_TYPE_X86_64: return "/lib/ld-musl-x86_64.so.1";
default: return "/lib/ld-musl-unknown.so.1"; // a placeholder for now
}
UNREACHABLE;
break;
case LINUX_LIBC_GNU:
switch (compiler.platform.arch)
{
case ARCH_TYPE_ARM: return "--dynamic-linker=/lib/ld-linux.so.3";
case ARCH_TYPE_AARCH64: return "--dynamic-linker=/lib/ld-linux-aarch64.so.1";
case ARCH_TYPE_MIPS: return "--dynamic-linker=/lib/ld-linux-mipsn8.so.1";
case ARCH_TYPE_MIPSEL: return "--dynamic-linker=/lib/ld-linux-mipsn8.so.1";
case ARCH_TYPE_MIPS64: return "--dynamic-linker=/lib/ld-linux-mipsn8.so.1";
case ARCH_TYPE_MIPS64EL: return "--dynamic-linker=/lib/ld-linux-mipsn8.so.1";
case ARCH_TYPE_RISCV32: return "-dynamic-linker=/lib/ld-linux-riscv32-ilp32.so.1";
case ARCH_TYPE_RISCV64: return "-dynamic-linker=/lib/ld-linux-riscv64-lp64.so.1";
case ARCH_TYPE_SPARCV9: return "--dynamic-linker=/lib/ld-linux.so.2";
case ARCH_TYPE_X86: return "--dynamic-linker=/lib64/ld-linux.so.2";
case ARCH_TYPE_X86_64: return "--dynamic-linker=/lib64/ld-linux-x86-64.so.2";
default: return "--dynamic-linker=/lib/ld-linux-unknown.so.2"; // another placeholder until we have all of them
case ARCH_TYPE_ARM: return "/lib/ld-linux.so.3";
case ARCH_TYPE_AARCH64: return "/lib/ld-linux-aarch64.so.1";
case ARCH_TYPE_MIPS: return "/lib/ld-linux-mipsn8.so.1";
case ARCH_TYPE_MIPSEL: return "/lib/ld-linux-mipsn8.so.1";
case ARCH_TYPE_MIPS64: return "/lib/ld-linux-mipsn8.so.1";
case ARCH_TYPE_MIPS64EL: return "/lib/ld-linux-mipsn8.so.1";
case ARCH_TYPE_RISCV32:
{
unsigned flen = compiler.platform.riscv.flen;
if (flen == 8) return "/lib/ld-linux-riscv32-ilp32d.so.1";
if (flen == 4) return "/lib/ld-linux-riscv32-ilp32f.so.1";
return "/lib/ld-linux-riscv32-ilp32.so.1";
}
case ARCH_TYPE_RISCV64:
{
unsigned flen = compiler.platform.riscv.flen;
if (flen == 8) return "/lib/ld-linux-riscv64-lp64d.so.1";
if (flen == 4) return "/lib/ld-linux-riscv64-lp64f.so.1";
return "/lib/ld-linux-riscv64-lp64.so.1";
}
case ARCH_TYPE_SPARCV9: return "/lib/ld-linux.so.2";
case ARCH_TYPE_X86: return "/lib64/ld-linux.so.2";
case ARCH_TYPE_X86_64: return "/lib64/ld-linux-x86-64.so.2";
default: return "/lib/ld-linux-unknown.so.2"; // another placeholder until we have all of them
}
FALLTHROUGH;
default:
@@ -507,6 +554,15 @@ static void linker_setup_linux(const char ***args_ref, Linker linker_type, bool
{
error_exit("Failed to find the C runtime at link time.");
}
add_plain_arg("-m");
add_plain_arg(ld_target(compiler.platform.arch));
if (link_libc())
{
add_plain_arg("--dynamic-linker");
add_plain_arg(find_linux_ld());
}
if (is_pie_pic(compiler.platform.reloc_model))
{
add_concat_file_arg(crt_dir, "crti.o");
@@ -523,14 +579,29 @@ static void linker_setup_linux(const char ***args_ref, Linker linker_type, bool
}
add_concat_file_arg(crt_dir, "crtn.o");
add_concat_quote_arg("-L", crt_dir);
add_plain_arg(find_linux_ld());
add_concat_quote_arg("-L", crt_begin_dir);
if (compiler.platform.arch == ARCH_TYPE_RISCV64 || compiler.platform.arch == ARCH_TYPE_RISCV32)
{
const char *is_64 = compiler.platform.arch == ARCH_TYPE_RISCV64 ? "64" : "32";
char *sysroot = str_printf("/usr/riscv%s-linux-gnu", is_64);
if (file_is_dir(sysroot))
{
add_plain_arg(str_printf("--sysroot=%s", sysroot));
}
char *p1 = str_printf("/usr/riscv%s-linux-gnu/lib", is_64);
if (file_is_dir(p1)) add_plain_arg(str_printf("-L%s", p1));
char *p2 = str_printf("/usr/riscv%s-linux-gnu/usr/lib", is_64);
if (file_is_dir(p2)) add_plain_arg(str_printf("-L%s", p2));
}
if (compiler.linking.link_math) linking_add_link(&compiler.linking, "m");
linking_add_link(&compiler.linking, "pthread");
linking_add_link(&compiler.linking, "gcc");
linking_add_link(&compiler.linking, "gcc_s");
linking_add_link(&compiler.linking, "c");
add_plain_arg("-L/usr/lib/");
add_plain_arg("-L/lib/");
add_plain_arg("-m");
add_plain_arg(ld_target(compiler.platform.arch));
}
static void linker_setup_android(const char ***args_ref, Linker linker_type, bool is_dylib)

View File

@@ -1036,7 +1036,7 @@ static void llvm_emit_type_decls(GenContext *context, Decl *decl)
case DECL_UNION:
case DECL_ENUM:
case DECL_BITSTRUCT:
case DECL_CONST_ENUM:
case DECL_CONSTDEF:
llvm_get_typeid(context, decl->type);
break;
}
@@ -1389,7 +1389,7 @@ LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl)
case DECL_CT_ASSERT:
case DECL_TYPEDEF:
case DECL_ENUM:
case DECL_CONST_ENUM:
case DECL_CONSTDEF:
case DECL_ENUM_CONSTANT:
case DECL_IMPORT:
case DECL_ALIAS_PATH:
@@ -1407,6 +1407,7 @@ LLVMValueRef llvm_get_ref(GenContext *c, Decl *decl)
case DECL_INTERFACE:
case DECL_GENERIC:
case DECL_GENERIC_INSTANCE:
case DECL_CONTRACT:
UNREACHABLE;
}
UNREACHABLE

View File

@@ -656,7 +656,7 @@ static inline LLVMMetadataRef llvm_get_debug_type_internal(GenContext *c, Type *
return type->backend_debug_type = llvm_debug_pointer_type(c, type);
case TYPE_ENUM:
return type->backend_debug_type = llvm_debug_enum_type(c, type, scope);
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
return type->backend_debug_type = llvm_debug_raw_enum_type(c, type, scope);
case TYPE_FUNC_RAW:
return type->backend_debug_type = llvm_debug_func_type(c, type);

View File

@@ -3595,7 +3595,7 @@ static void llvm_emit_array_comp(GenContext *c, BEValue *result, BEValue *lhs, B
case ALL_INTS:
case TYPE_POINTER:
case TYPE_ENUM:
case TYPE_CONST_ENUM:
case TYPE_CONSTDEF:
case TYPE_FUNC_PTR:
case TYPE_INTERFACE:
case TYPE_ANY:
@@ -3921,7 +3921,7 @@ static void llvm_emit_else(GenContext *c, BEValue *be_value, Expr *expr)
assert(success_end_block && else_block_exit);
// We might have a void here
if (!real_value.value)
if (!real_value.value || LLVMIsUndef(real_value.value))
{
assert(type_flatten(expr->type) == type_void);
assert(!else_value.value);
@@ -6618,7 +6618,7 @@ static inline void llvm_emit_typeid_info(GenContext *c, BEValue *value, Expr *ex
INTROSPECT_TYPE_ARRAY, INTROSPECT_TYPE_POINTER,
INTROSPECT_TYPE_VECTOR, INTROSPECT_TYPE_ENUM,
INTROSPECT_TYPE_SLICE, INTROSPECT_TYPE_DISTINCT,
INTROSPECT_TYPE_CONST_ENUM, INTROSPECT_TYPE_BITSTRUCT,
INTROSPECT_TYPE_CONSTDEF, INTROSPECT_TYPE_BITSTRUCT,
INTROSPECT_TYPE_OPTIONAL,
};
for (int i = 0; i < 8; i++)

View File

@@ -165,6 +165,26 @@ void gencontext_begin_module(GenContext *c)
{
unsigned frame_pointer = compiler.platform.arch == ARCH_TYPE_AARCH64 ? 1 : 2;
llvm_set_module_flag(c, LLVMModuleFlagBehaviorWarning, "frame-pointer", frame_pointer, type_uint);
if (compiler.platform.arch == ARCH_TYPE_RISCV64 || compiler.platform.arch == ARCH_TYPE_RISCV32)
{
const char *abi_str = NULL;
if (compiler.platform.arch == ARCH_TYPE_RISCV64)
{
unsigned flen = compiler.platform.riscv.flen;
if (flen == 8) abi_str = "lp64d";
else if (flen == 4) abi_str = "lp64f";
else abi_str = "lp64";
}
else
{
unsigned flen = compiler.platform.riscv.flen;
if (flen == 8) abi_str = "ilp32d";
else if (flen == 4) abi_str = "ilp32f";
else abi_str = "ilp32";
}
LLVMMetadataRef val = LLVMValueAsMetadata(LLVMMDStringInContext(c->context, abi_str, strlen(abi_str)));
LLVMAddModuleFlag(c->module, LLVMModuleFlagBehaviorError, "target-abi", 10, val);
}
}
if (compiler.build.debug_info != DEBUG_INFO_NONE)

View File

@@ -1655,10 +1655,8 @@ void llvm_emit_stmt(GenContext *c, Ast *ast)
{
case AST_POISONED:
case AST_FOREACH_STMT:
case AST_CONTRACT:
case AST_ASM_STMT:
case AST_ASM_LABEL:
case AST_CONTRACT_FAULT:
case AST_CASE_STMT:
case AST_DEFAULT_STMT:
case CT_AST:

View File

@@ -25,7 +25,7 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *c, Decl *decl)
UNREACHABLE_VOID
case DECL_TYPE_ALIAS:
return llvm_get_type(c, decl->type);
case DECL_CONST_ENUM:
case DECL_CONSTDEF:
return llvm_get_type(c, decl->enums.type_info->type);
case DECL_TYPEDEF:
return llvm_get_type(c, decl->distinct->type);
@@ -643,8 +643,8 @@ LLVMValueRef llvm_get_typeid(GenContext *c, Type *type)
return llvm_generate_introspection_global(c, NULL, type, INTROSPECT_TYPE_DISTINCT, type_inline(type), 0, NULL, false);
case TYPE_ENUM:
return llvm_get_introspection_for_enum(c, type);
case TYPE_CONST_ENUM:
return llvm_generate_introspection_global(c, NULL, type, INTROSPECT_TYPE_CONST_ENUM, type_inline(type), vec_size(type->decl->enums.values), NULL, false);
case TYPE_CONSTDEF:
return llvm_generate_introspection_global(c, NULL, type, INTROSPECT_TYPE_CONSTDEF, type_inline(type), vec_size(type->decl->enums.values), NULL, false);
case TYPE_STRUCT:
case TYPE_UNION:
return llvm_get_introspection_for_struct_union(c, type);

View File

@@ -414,7 +414,7 @@ static Expr *parse_lambda(ParseContext *c, Expr *left, SourceSpan lhs_span UNUSE
sig->params = decls;
sig->rtype = return_type ? type_infoid(return_type) : 0;
sig->variadic = variadic;
if (!parse_attributes(c, &func->attributes, NULL, NULL, NULL)) return poisoned_expr;
if (!parse_attributes(c, &func->attributes, NULL, NULL, NULL, "on lambda declarations")) return poisoned_expr;
RANGE_EXTEND_PREV(func);
if (tok_is(c, TOKEN_IMPLIES))
{

View File

@@ -13,8 +13,8 @@ typedef enum FunctionParse_
FUNC_PARSE_INTERFACE,
} FunctionParse;
static inline Decl *parse_enum_declaration(ParseContext *c, bool is_const);
static inline Decl *parse_func_definition(ParseContext *c, AstId contracts, FunctionParse parse_kind);
static inline Decl *parse_enum_declaration(ParseContext *c);
static inline Decl *parse_func_definition(ParseContext *c, ContractDescription *contracts, FunctionParse parse_kind);
static inline bool parse_bitstruct_body(ParseContext *c, Decl *decl);
static inline bool parse_enum_param_list(ParseContext *c, Decl*** parameters_ref, ArrayIndex *inline_index);
static Decl *parse_ct_include(ParseContext *c);
@@ -22,8 +22,7 @@ static Decl *parse_exec(ParseContext *c);
static bool parse_attributes_for_global(ParseContext *c, Decl *decl);
INLINE bool parse_decl_initializer(ParseContext *c, Decl *decl);
INLINE Decl *decl_new_var_current(ParseContext *c, TypeInfo *type, VarDeclKind kind);
static bool parse_contracts(ParseContext *c, AstId *contracts_ref);
static Ast *contracts_first_real(AstId contracts);
static bool parse_contracts(ParseContext *c, ContractDescription *contracts_ref);
INLINE Decl *decl_new_var_current(ParseContext *c, TypeInfo *type, VarDeclKind kind)
{
@@ -233,28 +232,23 @@ NEXT:;
decl->generic_decl.id = generic_id++;
}
bool parse_attach_contracts(Decl *generics, AstId contracts)
bool parse_attach_contracts(Decl *generics, ContractDescription *contracts)
{
if (!contracts) return true;
if (!generics)
{
Ast *first_contract = ast_contract_has_any(contracts);
if (first_contract) RETURN_PRINT_ERROR_AT(false, first_contract, "Contracts can only be used with '@generic' declarations and modules.");
if (contracts->has_contracts)
{
print_error_at(contracts->first_contract, "Contracts can only be used with '@generic' declarations and modules.");
return false;
}
return true;
}
Ast *first_invalid_contract = ast_contract_has_any_non_require(contracts);
if (first_invalid_contract)
if (contracts->first_non_require.a)
{
RETURN_PRINT_ERROR_AT(false, first_invalid_contract, "Invalid constraint - only '@require' is valid for '@generic' declarations and modules.");
}
if (generics->generic_decl.contracts)
{
ast_last(astptrzero(generics->generic_decl.contracts))->next = contracts;
}
else
{
generics->generic_decl.contracts = contracts;
print_error_at(contracts->first_non_require, "Invalid constraint - only '@require' is valid for '@generic' declarations and modules.");
return false;
}
FOREACH(Expr *, e, contracts->requires) vec_add(generics->generic_decl.requires, e);
return true;
}
void parse_attach_generics(ParseContext *c, Decl *generic_decl)
@@ -267,7 +261,7 @@ void parse_attach_generics(ParseContext *c, Decl *generic_decl)
/**
* module ::= MODULE module_path ('{' module_params '}')? (@public|@private|@local|@test|@export|@cname) EOS
*/
bool parse_module(ParseContext *c, AstId contracts)
bool parse_module(ParseContext *c, ContractDescription *contracts)
{
if (tok_is(c, TOKEN_STRING))
{
@@ -316,7 +310,7 @@ bool parse_module(ParseContext *c, AstId contracts)
ASSIGN_DECL_OR_RET(Decl *generic_decl, parse_generic_decl(c), false);
if (!parse_attributes(c, &attrs, &visibility, NULL, &is_cond)) return false;
if (!parse_attributes(c, &attrs, &visibility, NULL, &is_cond, NULL)) return false;
if (generic_decl_old)
{
SEMA_DEPRECATED(generic_decl_old, "Module-based generics is deprecated, use `<...>` instead.");
@@ -937,7 +931,7 @@ Decl *parse_local_decl_after_type(ParseContext *c, TypeInfo *type)
advance(c);
bool is_cond;
if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond)) return poisoned_decl;
if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond, "on local variables")) return poisoned_decl;
decl->is_cond = is_cond;
if (tok_is(c, TOKEN_EQ))
{
@@ -1018,7 +1012,7 @@ Decl *parse_const_declaration(ParseContext *c, bool is_global, bool is_extern)
else
{
bool is_cond;
if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond)) return poisoned_decl;
if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond, "on local declarations")) return poisoned_decl;
decl->is_cond = is_cond;
}
@@ -1049,7 +1043,7 @@ Decl *parse_var_decl(ParseContext *c)
case TOKEN_IDENT:
decl = decl_new_var_current(c, NULL, VARDECL_LOCAL);
advance(c);
if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond)) return poisoned_decl;
if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond, "on local declarations")) return poisoned_decl;
decl->is_cond = is_cond;
if (!tok_is(c, TOKEN_EQ))
{
@@ -1064,7 +1058,7 @@ Decl *parse_var_decl(ParseContext *c)
decl = decl_new_var_current(c, NULL, c->tok == TOKEN_CT_IDENT ? VARDECL_LOCAL_CT : VARDECL_LOCAL_CT_TYPE);
advance(c);
span = c->span;
if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond)) return poisoned_decl;
if (!parse_attributes(c, &decl->attributes, NULL, NULL, &is_cond, "on local declarations")) return poisoned_decl;
if (is_cond || decl->attributes)
{
print_error_at(span, "Attributes are not allowed on compile time variables.");
@@ -1326,7 +1320,8 @@ static bool parse_attributes_for_global(ParseContext *c, Decl *decl)
bool is_cond;
bool can_be_generic = decl_may_be_generic(decl);
ASSIGN_DECL_OR_RET(Decl *generics, parse_generic_decl(c), false);
if (!parse_attributes(c, &decl->attributes, &visibility, decl_needs_prefix(decl) ? &is_builtin : NULL, &is_cond)) return false;
bool is_method = decl->decl_kind == DECL_FUNC && decl->func_decl.type_parent;
if (!parse_attributes(c, &decl->attributes, &visibility, decl_needs_prefix(decl) ? &is_builtin : NULL, &is_cond, is_method ? "for method declarations" : NULL)) return false;
if (generics)
{
if (!can_be_generic)
@@ -1351,7 +1346,22 @@ static bool parse_attributes_for_global(ParseContext *c, Decl *decl)
return true;
}
static inline bool parse_attribute_list(ParseContext *c, Attr ***attributes_ref, Visibility *visibility_ref, bool *builtin_ref, bool *cond_ref, bool use_comma)
static inline bool warn_method_visibility(Attr *attr, const char *mod, const char *err)
{
switch (compiler.build.warnings.method_visibility)
{
case WARNING_WARN:
sema_warning_at(attr->span, "'%s' modifiers are ignored %s.", mod, err);
return true;
case WARNING_ERROR:
print_error_at(attr->span, "'%s' modifiers are not allowed %s.", mod, err);
return false;
default:
return true;
}
}
static inline bool parse_attribute_list(ParseContext *c, Attr ***attributes_ref, Visibility *visibility_ref, bool *builtin_ref, bool *cond_ref, bool use_comma, const char *reject_visibility)
{
Visibility visibility = -1; // NOLINT
if (cond_ref) *cond_ref = false;
@@ -1370,12 +1380,36 @@ static inline bool parse_attribute_list(ParseContext *c, Attr ***attributes_ref,
switch (attr->attr_kind)
{
case ATTRIBUTE_PUBLIC:
if (reject_visibility && attributes_ref)
{
if (!warn_method_visibility(attr, "@public", reject_visibility)) return false;
}
else if (reject_visibility)
{
RETURN_PRINT_ERROR_AT(false, attr, "Visibility modifiers are not allowed %s.", reject_visibility);
}
parsed_visibility = VISIBLE_PUBLIC;
break;
case ATTRIBUTE_PRIVATE:
if (reject_visibility && attributes_ref)
{
if (!warn_method_visibility(attr, "@private", reject_visibility)) return false;
}
else if (reject_visibility)
{
RETURN_PRINT_ERROR_AT(false, attr, "Visibility modifiers are not allowed %s.", reject_visibility);
}
parsed_visibility = VISIBLE_PRIVATE;
break;
case ATTRIBUTE_LOCAL:
if (reject_visibility && attributes_ref)
{
if (!warn_method_visibility(attr, "@local", reject_visibility)) return false;
}
else if (reject_visibility)
{
RETURN_PRINT_ERROR_AT(false, attr, "Visibility modifiers are not allowed %s.", reject_visibility);
}
parsed_visibility = VISIBLE_LOCAL;
break;
case ATTRIBUTE_BUILTIN:
@@ -1464,9 +1498,9 @@ Decl *parse_generic_decl(ParseContext *c)
*
* @return true if parsing succeeded, false if recovery is needed
*/
bool parse_attributes(ParseContext *c, Attr ***attributes_ref, Visibility *visibility_ref, bool *builtin_ref, bool *cond_ref)
bool parse_attributes(ParseContext *c, Attr ***attributes_ref, Visibility *visibility_ref, bool *builtin_ref, bool *cond_ref, const char *reject_visibility)
{
return parse_attribute_list(c, attributes_ref, visibility_ref, builtin_ref, cond_ref, false);
return parse_attribute_list(c, attributes_ref, visibility_ref, builtin_ref, cond_ref, false, reject_visibility);
}
/**
@@ -1600,7 +1634,7 @@ static inline bool parse_enum_param_decl(ParseContext *c, Decl*** parameters)
if (token_is_some_ident(c->tok)) RETURN_PRINT_ERROR_HERE("Expected a name starting with a lower-case letter.");
RETURN_PRINT_ERROR_HERE("Expected a member name here.");
}
if (!parse_attributes(c, &param->attributes, NULL, NULL, NULL)) return false;
if (!parse_attributes(c, &param->attributes, NULL, NULL, NULL, "on parameter declarations")) return false;
vec_add(*parameters, param);
RANGE_EXTEND_PREV(param);
return true;
@@ -1853,7 +1887,7 @@ CHECK_ELLIPSIS:
Decl *param = decl_new_var(name, span, type, param_kind);
param->var.type_info = type ? type_infoid(type) : 0;
param->var.self_addr = ref;
if (!parse_attributes(c, &param->attributes, NULL, NULL, NULL)) return false;
if (!parse_attributes(c, &param->attributes, NULL, NULL, NULL, "on parameters")) return false;
if (!no_name)
{
if (try_consume(c, TOKEN_EQ))
@@ -1913,22 +1947,19 @@ static inline bool parse_fn_parameter_list(ParseContext *c, Signature *signature
// --- Parse types
static bool parse_element_contract(ParseContext *c, const char *error)
static bool parse_element_contract(ParseContext *c, ContractDescription *contracts, const char *error)
{
AstId contracts = 0;
if (!parse_contracts(c, &contracts)) return false;
if (!contracts) return true;
Ast *ast = astptr(contracts);
while (ast)
{
if (ast->ast_kind != AST_CONTRACT || ast->contract_stmt.kind != CONTRACT_COMMENT)
{
RETURN_PRINT_ERROR_AT(false, ast, "No constraints are allowed on %s.", error);
}
ast = astptrzero(ast->next);
}
return true;
if (!parse_contracts(c, contracts)) return false;
if (!contracts->has_contracts) return true;
print_error_at(contracts->first_contract, "No constraints are allowed on %s.", error);
return false;
}
INLINE void attach_deprecation_from_contract(ParseContext *c, ContractDescription *contract, Decl *decl)
{
if (contract->deprecated) vec_add(decl->attributes, contract->deprecated);
}
/**
* Expect pointer to after '{'
*
@@ -1954,7 +1985,8 @@ static bool parse_struct_body(ParseContext *c, Decl *parent)
ArrayIndex index = 0;
while (!tok_is(c, TOKEN_RBRACE))
{
if (!parse_element_contract(c, "struct/union members")) return decl_poison(parent);
ContractDescription contracts = EMPTY_CONTRACT;
if (!parse_element_contract(c, &contracts, "struct/union members")) return decl_poison(parent);
TokenType token_type = c->tok;
if (token_type == TOKEN_STRUCT || token_type == TOKEN_UNION || token_type == TOKEN_BITSTRUCT)
{
@@ -1993,10 +2025,11 @@ static bool parse_struct_body(ParseContext *c, Decl *parent)
else
{
bool is_cond;
if (!parse_attributes(c, &member->attributes, NULL, NULL, &is_cond)) return false;
if (!parse_attributes(c, &member->attributes, NULL, NULL, &is_cond, "on struct and union fields")) return false;
member->is_cond = true;
if (!parse_struct_body(c, member)) return decl_poison(parent);
}
attach_deprecation_from_contract(c, &contracts, member);
vec_add(parent->strukt.members, member);
index++;
if (index > MAX_MEMBERS)
@@ -2036,7 +2069,7 @@ static bool parse_struct_body(ParseContext *c, Decl *parent)
}
advance(c);
bool is_cond;
if (!parse_attributes(c, &member->attributes, NULL, NULL, &is_cond)) return false;
if (!parse_attributes(c, &member->attributes, NULL, NULL, &is_cond, "on struct and union fields")) return false;
member->is_cond = true;
if (!try_consume(c, TOKEN_COMMA)) break;
if (was_inline)
@@ -2159,10 +2192,11 @@ static inline bool parse_bitstruct_body(ParseContext *c, Decl *decl)
bool is_consecutive = false;
while (!try_consume(c, TOKEN_RBRACE))
{
if (!parse_element_contract(c, "bitstruct members")) return decl_poison(decl);
ContractDescription contracts = EMPTY_CONTRACT;
if (!parse_element_contract(c, &contracts, "bitstruct members")) return decl_poison(decl);
ASSIGN_TYPE_OR_RET(TypeInfo *type, parse_base_type(c), false);
Decl *member_decl = decl_new_var_current(c, type, VARDECL_BITMEMBER);
attach_deprecation_from_contract(c, &contracts, member_decl);
if (!try_consume(c, TOKEN_IDENT))
{
if (try_consume(c, TOKEN_CONST_IDENT) || try_consume(c, TOKEN_TYPE_IDENT))
@@ -2184,7 +2218,7 @@ static inline bool parse_bitstruct_body(ParseContext *c, Decl *decl)
is_consecutive = true;
}
bool is_cond = false;
if (!parse_attributes(c, &member_decl->attributes, NULL, NULL, &is_cond)) return false;
if (!parse_attributes(c, &member_decl->attributes, NULL, NULL, &is_cond, "on bitstruct fields")) return false;
member_decl->is_cond = is_cond;
CONSUME_OR_RET(TOKEN_EOS, false);
unsigned index = vec_size(decl->strukt.members);
@@ -2205,7 +2239,7 @@ static inline bool parse_bitstruct_body(ParseContext *c, Decl *decl)
member_decl->var.end = NULL;
}
bool is_cond = false;
if (!parse_attributes(c, &member_decl->attributes, NULL, NULL, &is_cond)) return false;
if (!parse_attributes(c, &member_decl->attributes, NULL, NULL, &is_cond, "on bitstruct fields")) return false;
member_decl->is_cond = is_cond;
CONSUME_EOS_OR_RET(false);
if (is_consecutive)
@@ -2224,13 +2258,13 @@ INLINE bool parse_interface_body(ParseContext *c, Decl *interface)
Decl **fns = NULL;
while (!try_consume(c, TOKEN_RBRACE))
{
AstId contracts = 0;
if (!parse_contracts(c, &contracts)) return poisoned_decl;
ContractDescription contracts_desc = EMPTY_CONTRACT;
if (!parse_contracts(c, &contracts_desc)) return poisoned_decl;
if (!tok_is(c, TOKEN_FN))
{
RETURN_PRINT_ERROR_HERE("Interfaces can only have function declarations, and they must start with 'fn' as usual.");
}
ASSIGN_DECL_OR_RET(Decl *interface_fn, parse_func_definition(c, contracts, FUNC_PARSE_INTERFACE), false);
ASSIGN_DECL_OR_RET(Decl *interface_fn, parse_func_definition(c, &contracts_desc, FUNC_PARSE_INTERFACE), false);
vec_add(fns, interface_fn);
}
interface->interface_methods = fns;
@@ -2289,10 +2323,6 @@ static inline Decl *parse_bitstruct_declaration(ParseContext *c)
static inline Decl *parse_top_level_const_declaration(ParseContext *c, bool is_extern)
{
if (!is_extern && peek(c) == TOKEN_ENUM)
{
return parse_enum_declaration(c, true);
}
ASSIGN_DECL_OR_RET(Decl *decl, parse_const_declaration(c, true, is_extern), poisoned_decl);
CONSUME_EOS_OR_RET(poisoned_decl);
return decl;
@@ -2346,6 +2376,17 @@ static inline void decl_add_type(Decl *decl, TypeKind kind)
decl->type = type;
}
static DeclId decl_from_contract_description(ContractDescription *description)
{
if (!description->has_contracts) return 0;
Decl *decl = decl_new(DECL_CONTRACT, "contract", description->first);
decl->contracts_decl.ensures = description->ensures;
decl->contracts_decl.requires = description->requires;
decl->contracts_decl.pure = description->pure;
decl->contracts_decl.params = description->params;
decl->contracts_decl.opt_returns = description->opt_returns;
return declid(decl);
}
/**
* typedef_declaration ::= ALIAS TYPE_IDENT attributes? '=' typedef_type ';'
@@ -2353,7 +2394,7 @@ static inline void decl_add_type(Decl *decl, TypeKind kind)
* typedef_type ::= func_typedef | type generic_params?
* func_typedef ::= 'fn' optional_type parameter_type_list
*/
static inline Decl *parse_alias_type(ParseContext *c, AstId contracts)
static inline Decl *parse_alias_type(ParseContext *c, ContractDescription *contracts)
{
advance_and_verify(c, TOKEN_ALIAS);
@@ -2390,12 +2431,13 @@ static inline Decl *parse_alias_type(ParseContext *c, AstId contracts)
decl->type_alias_decl.decl = decl_type;
ASSIGN_TYPE_OR_RET(TypeInfo *type_info, parse_optional_type(c), poisoned_decl);
decl_type->fntype_decl.signature.rtype = type_infoid(type_info);
decl_type->fntype_decl.docs = contracts;
decl_type->fntype_decl.docs = decl_from_contract_description(contracts);
if (!parse_fn_parameter_list(c, &(decl_type->fntype_decl.signature)))
{
return poisoned_decl;
}
if (!parse_attributes(c, &decl_type->attributes, NULL, NULL, NULL)) return poisoned_decl;
if (!parse_attributes(c, &decl_type->attributes, NULL, NULL, NULL, "on the target of an alias (maybe you intended it *before* the '='?)")) return poisoned_decl;
attach_deprecation_from_contract(c, contracts, decl_type);
RANGE_EXTEND_PREV(decl_type);
RANGE_EXTEND_PREV(decl);
CONSUME_EOS_OR_RET(poisoned_decl);
@@ -2487,7 +2529,7 @@ static inline Decl *parse_alias_module(ParseContext *c, Decl *decl, TokenType to
*
* identifier_alias ::= path? (IDENT | CONST_IDENT | AT_IDENT)
*/
static inline Decl *parse_alias_ident(ParseContext *c, AstId contracts)
static inline Decl *parse_alias_ident(ParseContext *c, ContractDescription *contracts)
{
// 1. Store the beginning of the "alias".
advance_and_verify(c, TOKEN_ALIAS);
@@ -2582,7 +2624,7 @@ static inline Decl *parse_attrdef(ParseContext *c)
bool is_cond;
bool is_builtin = false;
if (!parse_attribute_list(c, &attributes, NULL, decl_needs_prefix(decl) ? &is_builtin : NULL, &is_cond, true)) return poisoned_decl;
if (!parse_attribute_list(c, &attributes, NULL, decl_needs_prefix(decl) ? &is_builtin : NULL, &is_cond, true, "cannot be aliased using 'attrdef'")) return poisoned_decl;
decl->attr_decl.attrs = attributes;
CONSUME_EOS_OR_RET(poisoned_decl);
return decl;
@@ -2591,7 +2633,7 @@ static inline Decl *parse_attrdef(ParseContext *c)
/**
* define_decl ::= ALIAS define_type_body
*/
static inline Decl *parse_alias(ParseContext *c, AstId contracts)
static inline Decl *parse_alias(ParseContext *c, ContractDescription *contracts)
{
switch (peek(c))
{
@@ -2697,16 +2739,17 @@ static inline bool parse_func_macro_header(ParseContext *c, Decl *decl)
* macro ::= MACRO macro_header '(' macro_params ')' opt_attributes macro_body
* macro_body ::= IMPLIES expression ';' | compound_statement
*/
static inline Decl *parse_macro_declaration(ParseContext *c, AstId docs)
static inline Decl *parse_macro_declaration(ParseContext *c, ContractDescription *contracts)
{
advance_and_verify(c, TOKEN_MACRO);
Decl *decl = decl_calloc();
decl->decl_kind = DECL_MACRO;
decl->func_decl.docs = docs;
decl->func_decl.docs = decl_from_contract_description(contracts);
if (!parse_func_macro_header(c, decl)) return poisoned_decl;
if (!parse_macro_params(c, decl)) return poisoned_decl;
if (!parse_attributes_for_global(c, decl)) return poisoned_decl;
attach_deprecation_from_contract(c, contracts, decl);
if (tok_is(c, TOKEN_IMPLIES))
{
ASSIGN_ASTID_OR_RET(decl->func_decl.body,
@@ -2719,10 +2762,12 @@ static inline Decl *parse_macro_declaration(ParseContext *c, AstId docs)
static inline Decl *parse_fault(ParseContext *c)
{
if (!parse_element_contract(c, "faults")) return poisoned_decl;
ContractDescription contracts = EMPTY_CONTRACT;
if (!parse_element_contract(c, &contracts, "faults")) return poisoned_decl;
Decl *decl = decl_new(DECL_FAULT, symstr(c), c->span);
if (!consume_const_name(c, "fault")) return poisoned_decl;
if (!parse_attributes_for_global(c, decl)) return poisoned_decl;
attach_deprecation_from_contract(c, &contracts, decl);
return decl;
}
@@ -2795,15 +2840,16 @@ static inline bool parse_enum_param_list(ParseContext *c, Decl*** parameters_ref
return true;
}
static bool parse_enum_values(ParseContext *c, Decl*** values_ref, Visibility visibility, bool is_single_value, bool is_const_enum)
static bool parse_enum_values(ParseContext *c, Decl*** values_ref, Visibility visibility, bool is_single_value, bool is_constdef)
{
Decl **values = NULL;
bool deprecate_warn = true;
while (!try_consume(c, TOKEN_RBRACE))
{
if (!parse_element_contract(c, "enum values")) return false;
ContractDescription contracts = EMPTY_CONTRACT;
if (!parse_element_contract(c, &contracts, "enum values")) return false;
Decl *enum_const = decl_new(DECL_ENUM_CONSTANT, symstr(c), c->span);
if (is_const_enum) enum_const->enum_constant.is_raw = is_const_enum;
if (is_constdef) enum_const->enum_constant.is_raw = is_constdef;
enum_const->visibility = visibility;
const char *name = enum_const->name;
if (!consume_const_name(c, "enum constant")) return false;
@@ -2817,18 +2863,19 @@ static bool parse_enum_values(ParseContext *c, Decl*** values_ref, Visibility vi
}
}
if (!parse_attributes_for_global(c, enum_const)) return false;
attach_deprecation_from_contract(c, &contracts, enum_const);
if (try_consume(c, TOKEN_EQ))
{
Expr **args = NULL;
if (!is_const_enum && deprecate_warn)
if (!is_constdef && deprecate_warn)
{
deprecate_warn = false;
print_deprecation_at(c->prev_span, "Use () declaration of associated values instead.");
PRINT_DEPRECATED_AT(c->prev_span, "Use {} declaration of associated values instead.");
}
if (is_single_value || !tok_is(c, TOKEN_LBRACE))
{
ASSIGN_EXPR_OR_RET(Expr *single, parse_constant_expr(c), false);
if (is_const_enum)
if (is_constdef)
{
enum_const->enum_constant.value = single;
goto NEXT;
@@ -2862,7 +2909,7 @@ static bool parse_enum_values(ParseContext *c, Decl*** values_ref, Visibility vi
}
enum_const->enum_constant.associated = args;
}
else if (!is_const_enum && try_consume(c, TOKEN_LBRACE))
else if (!is_constdef && try_consume(c, TOKEN_LBRACE))
{
Expr **args = NULL;
while (1)
@@ -2913,13 +2960,13 @@ NEXT:
* enum_body ::= enum_def (',' enum_def)* ','?
* enum_def ::= CONST_IDENT ('(' arg_list ')')?
*/
static inline Decl *parse_enum_declaration(ParseContext *c, bool is_const)
static inline Decl *parse_enum_declaration(ParseContext *c)
{
if (is_const) advance_and_verify(c, TOKEN_CONST);
if (tok_is(c, TOKEN_CENUM))
bool is_constdef = false;
if (tok_is(c, TOKEN_CONSTDEF))
{
advance_and_verify(c, TOKEN_CENUM);
is_const = true;
advance_and_verify(c, TOKEN_CONSTDEF);
is_constdef = true;
}
else
{
@@ -2928,23 +2975,22 @@ static inline Decl *parse_enum_declaration(ParseContext *c, bool is_const)
const char *name = symstr(c);
SourceSpan span = c->span;
if (!consume_type_name(c, "enum")) return poisoned_decl;
if (!consume_type_name(c, is_constdef ? "constdef" : "enum" )) return poisoned_decl;
TypeInfo **interfaces = NULL;
if (!parse_interface_impls(c, &interfaces)) return poisoned_decl;
TypeInfo *type = NULL;
bool val_is_inline = false;
ArrayIndex inline_index = -1;
bool is_const_enum = is_const;
Decl **param_list = NULL;
if (try_consume(c, TOKEN_COLON))
{
if (!is_const)
if (!is_constdef)
{
is_const_enum = try_consume(c, TOKEN_CONST);
if (is_const_enum)
is_constdef = try_consume(c, TOKEN_CONST);
if (is_constdef)
{
print_deprecation_at(c->prev_span, "Declare const enums using 'const enum' instead.");
PRINT_DEPRECATED_AT(c->prev_span, "Declare constdefs using 'constdef' instead.");
}
}
if (!tok_is(c, TOKEN_LPAREN) && !tok_is(c, TOKEN_LBRACE))
@@ -2953,14 +2999,14 @@ static inline Decl *parse_enum_declaration(ParseContext *c, bool is_const)
ASSIGN_TYPE_OR_RET(type, parse_optional_type_no_generic(c), poisoned_decl);
if (type->optional)
{
RETURN_PRINT_ERROR_AT(poisoned_decl, type, "An enum can't have an optional type.");
RETURN_PRINT_ERROR_AT(poisoned_decl, type, "An enum or constdef can't have an optional type.");
}
}
if (is_const_enum)
if (is_constdef)
{
if (tok_is(c, TOKEN_LPAREN))
{
PRINT_ERROR_HERE("Const enums cannot have associated values.");
PRINT_ERROR_HERE("Constdefs cannot have associated values.");
return poisoned_decl;
}
}
@@ -2970,7 +3016,7 @@ static inline Decl *parse_enum_declaration(ParseContext *c, bool is_const)
}
}
Decl *decl = decl_new_with_type(name, span, is_const_enum ? DECL_CONST_ENUM : DECL_ENUM);
Decl *decl = decl_new_with_type(name, span, is_constdef ? DECL_CONSTDEF : DECL_ENUM);
decl->interfaces = interfaces;
if (param_list) decl->enums.parameters = param_list;
if (!parse_attributes_for_global(c, decl)) return poisoned_decl;
@@ -2980,9 +3026,9 @@ static inline Decl *parse_enum_declaration(ParseContext *c, bool is_const)
decl->enums.type_info = type ? type : type_info_new_base(type_int, decl->span);
decl->enums.inline_index = (int16_t)inline_index;
decl->enums.inline_value = is_const_enum ? false : val_is_inline;
if (is_const_enum && val_is_inline) decl->is_substruct = true;
if (!parse_enum_values(c, &decl->enums.values, visibility, is_const_enum || expected_parameters == 1, is_const_enum)) return poisoned_decl;
decl->enums.inline_value = is_constdef ? false : val_is_inline;
if (is_constdef && val_is_inline) decl->is_substruct = true;
if (!parse_enum_values(c, &decl->enums.values, visibility, is_constdef || expected_parameters == 1, is_constdef)) return poisoned_decl;
return decl;
}
@@ -2995,12 +3041,12 @@ static inline Decl *parse_enum_declaration(ParseContext *c, bool is_const)
* func_body ::= ('=>' short_body) | compound_stmt
*
*/
static inline Decl *parse_func_definition(ParseContext *c, AstId contracts, FunctionParse parse_kind)
static inline Decl *parse_func_definition(ParseContext *c, ContractDescription *contracts, FunctionParse parse_kind)
{
advance_and_verify(c, TOKEN_FN);
Decl *func = decl_calloc();
func->decl_kind = DECL_FUNC;
func->func_decl.docs = contracts;
func->func_decl.docs = decl_from_contract_description(contracts);
func->func_decl.attr_interface_method = parse_kind == FUNC_PARSE_INTERFACE;
if (!parse_func_macro_header(c, func)) return poisoned_decl;
if (func->name[0] == '@')
@@ -3009,6 +3055,7 @@ static inline Decl *parse_func_definition(ParseContext *c, AstId contracts, Func
}
if (!parse_fn_parameter_list(c, &(func->func_decl.signature))) return poisoned_decl;
if (!parse_attributes_for_global(c, func)) return poisoned_decl;
attach_deprecation_from_contract(c, contracts, func);
if (parse_kind != FUNC_PARSE_REGULAR)
{
if (tok_is(c, TOKEN_LBRACE) || tok_is(c, TOKEN_IMPLIES))
@@ -3123,17 +3170,6 @@ static inline bool parse_import(ParseContext *c)
return true;
}
INLINE void append_docs(AstId **next, AstId *first, Ast *new_doc)
{
if (!*first)
{
*first = astid(new_doc);
}
**next = astid(new_doc);
*next = &new_doc->next;
}
INLINE bool parse_doc_to_eol(ParseContext *c)
{
if (try_consume(c, TOKEN_DOCS_EOL)) return true;
@@ -3187,27 +3223,20 @@ static bool parse_doc_direct_comment(ParseContext *c)
/**
* contract ::= expression_list (':'? STRING)?
*/
static inline bool parse_doc_contract(ParseContext *c, AstId *docs, AstId **docs_next, ContractKind kind)
static inline bool parse_doc_contract(ParseContext *c, Expr ***list_ref, const char *prefix)
{
Ast *ast = ast_new_curr(c, AST_CONTRACT);
ast->contract_stmt.kind = kind;
Expr *expr = expr_new(EXPR_CONTRACT, c->span);
const char *start = c->lexer.data.lex_start;
advance(c);
ASSIGN_EXPR_OR_RET(ast->contract_stmt.contract.decl_exprs, parse_expression_list(c, false), false);
ASSIGN_EXPR_OR_RET(expr->contract_expr.decl_exprs, parse_expression_list(c, false), false);
RANGE_EXTEND_PREV(expr);
const char *end = start + 1;
while (end[0] != '\n' && end[0] != '\0') end++;
if (end > c->data.lex_start) end = c->data.lex_start;
while (is_space(end[-1])) end--;
scratch_buffer_clear();
switch (kind)
{
case CONTRACT_ENSURE:
scratch_buffer_append("@ensure \"");
break;
default:
scratch_buffer_append("@require \"");
break;
}
scratch_buffer_append(prefix);
scratch_buffer_append(" \"");
scratch_buffer_append_remove_space(start, (int)(end - start));
scratch_buffer_append("\" violated");
bool docs_to_comment = false;
@@ -3225,18 +3254,18 @@ static inline bool parse_doc_contract(ParseContext *c, AstId *docs, AstId **docs
scratch_buffer_append(": '");
if (!parse_joined_strings(c, NULL, NULL)) return false;
scratch_buffer_append("'.");
ast->contract_stmt.contract.comment = scratch_buffer_copy();
expr->contract_expr.comment = scratch_buffer_copy();
if (!docs_to_comment)
{
SEMA_DEPRECATED(ast, "Not using ':' before the description is deprecated");
SEMA_DEPRECATED(expr, "Not using ':' before the description is deprecated");
}
}
else
{
scratch_buffer_append(".");
ast->contract_stmt.contract.expr_string = scratch_buffer_copy();
expr->contract_expr.expr_string = scratch_buffer_copy();
}
append_docs(docs_next, docs, ast);
vec_add(*list_ref, expr);
return true;
}
@@ -3244,15 +3273,14 @@ static inline bool parse_doc_contract(ParseContext *c, AstId *docs, AstId **docs
* param_contract ::= '@param' inout_attribute? any_identifier ( ':' STRING )?
* inout_attribute ::= '[' '&'? ('in' | 'inout' | 'out') ']'
*/
static inline bool parse_contract_param(ParseContext *c, AstId *docs, AstId **docs_next)
static inline bool parse_contract_param(ParseContext *c, ContractParam **list_ref)
{
Ast *ast = ast_new_curr(c, AST_CONTRACT);
ast->contract_stmt.kind = CONTRACT_PARAM;
advance(c);
// [inout] [in] [out]
bool is_ref = false;
InOutModifier mod = INOUT_ANY;
SourceSpan span = c->span;
if (try_consume(c, TOKEN_LBRACKET))
{
is_ref = try_consume(c, TOKEN_AMP);
@@ -3277,16 +3305,17 @@ static inline bool parse_contract_param(ParseContext *c, AstId *docs, AstId **do
CONSUME_OR_RET(TOKEN_RBRACKET, false);
}
ContractParam param = { .span = span };
switch (c->tok)
{
case TOKEN_IDENT:
case TOKEN_CT_IDENT:
case TOKEN_CT_TYPE_IDENT:
case TOKEN_HASH_IDENT:
ast->contract_stmt.param.name = symstr(c);
param.name = symstr(c);
break;
case TOKEN_ELLIPSIS:
ast->contract_stmt.param.name = NULL;
param.name = NULL;
break;
case TOKEN_TYPE_IDENT:
case TOKEN_CT_CONST_IDENT:
@@ -3295,11 +3324,11 @@ static inline bool parse_contract_param(ParseContext *c, AstId *docs, AstId **do
default:
RETURN_PRINT_ERROR_HERE("Expected a parameter name here.");
}
ast->contract_stmt.param.modifier = mod;
ast->contract_stmt.param.span = c->span;
ast->contract_stmt.param.by_ref = is_ref;
advance(c);
param.modifier = mod;
param.by_ref = is_ref;
advance(c);
RANGE_EXTEND_PREV(&param);
if (parse_docs_to_comment(c))
{
if (!parse_doc_check_skip_string_eos(c))
@@ -3327,55 +3356,40 @@ static inline bool parse_contract_param(ParseContext *c, AstId *docs, AstId **do
}
else
{
RANGE_EXTEND_PREV(ast);
SEMA_DEPRECATED(ast, "Not using ':' before the string is deprecated.");
SEMA_DEPRECATED(&param, "Not using ':' before the string is deprecated.");
}
}
append_docs(docs_next, docs, ast);
vec_add(*list_ref, param);
return true;
}
static inline bool parse_doc_optreturn(ParseContext *c, AstId *docs, AstId **docs_next)
static inline bool parse_doc_optreturn(ParseContext *c, Expr ***opt_return_ref)
{
Ast **returns = NULL;
Ast *ast = ast_new_curr(c, AST_CONTRACT);
ast->span = c->prev_span;
advance_and_verify(c, TOKEN_QUESTION);
ast->contract_stmt.kind = CONTRACT_OPTIONALS;
while (1)
{
Ast *ret = ast_new_curr(c, AST_CONTRACT_FAULT);
ASSIGN_EXPR_OR_RET(ret->contract_fault.expr, parse_expr(c), false);
vec_add(returns, ret);
ASSIGN_EXPR_OR_RET(Expr *expr, parse_expr(c), false);
vec_add(*opt_return_ref, expr);
if (!try_consume(c, TOKEN_COMMA)) break;
}
RANGE_EXTEND_PREV(ast);
// Just ignore our potential string:
if (!parse_doc_discarded_comment(c)) return false;
ast->contract_stmt.faults = returns;
append_docs(docs_next, docs, ast);
return true;
}
static bool parse_contracts(ParseContext *c, AstId *contracts_ref)
static bool parse_contracts(ParseContext *c, ContractDescription *contracts_ref)
{
*contracts_ref = 0;
if (!tok_is(c, TOKEN_DOCS_START)) return true;
AstId **next = &contracts_ref;
if (c->data.strlen > 0)
{
Ast *ast = ast_new_curr(c, AST_CONTRACT);
ast->contract_stmt.kind = CONTRACT_COMMENT;
ast->contract_stmt.string = symstr(c);
ast->contract_stmt.strlen = c->data.strlen;
ast->span = c->span;
append_docs(next, contracts_ref, ast);
contracts_ref->comment = symstr(c);
contracts_ref->comment_span = c->span;
}
contracts_ref->first = c->span;
advance_and_verify(c, TOKEN_DOCS_START);
bool return_comment = false;
while (!try_consume(c, TOKEN_DOCS_END))
{
if (try_consume(c, TOKEN_DOCS_EOL)) continue;
@@ -3383,44 +3397,103 @@ static bool parse_contracts(ParseContext *c, AstId *contracts_ref)
{
RETURN_PRINT_ERROR_HERE("Expected a directive starting with '@' here, like '@param' or `@require`");
}
if (!contracts_ref->first.a) contracts_ref->first = c->span;
const char *name = symstr(c);
if (name == kw_at_require)
{
if (!contracts_ref->has_contracts)
{
contracts_ref->first_contract = c->span;
contracts_ref->has_contracts = true;
}
if (!parse_doc_contract(c, &contracts_ref->requires, "@require")) return false;
goto END;
}
if (contracts_ref->first_non_require.a == 0)
{
contracts_ref->first_non_require = c->span;
}
if (name == kw_at_param)
{
if (!parse_contract_param(c, contracts_ref, next)) return false;
if (!contracts_ref->has_contracts)
{
contracts_ref->first_contract = c->span;
contracts_ref->has_contracts = true;
}
if (!parse_contract_param(c, &contracts_ref->params)) return false;
}
else if (name == kw_at_return)
{
advance(c);
if (tok_is(c, TOKEN_QUESTION))
{
if (!parse_doc_optreturn(c, contracts_ref, next)) return false;
if (!contracts_ref->has_contracts)
{
contracts_ref->first_contract = c->span;
contracts_ref->has_contracts = true;
}
if (!parse_doc_optreturn(c, &contracts_ref->opt_returns)) return false;
}
else
{
if (return_comment)
{
RETURN_PRINT_ERROR_HERE("Only one `@return` directive is allowed per contract.");
}
return_comment = true;
if (!parse_doc_direct_comment(c)) return false;
}
}
else if (name == kw_at_deprecated)
{
advance(c);
if (!parse_doc_direct_comment(c)) return false;
REMINDER("Implement @deprecated tracking");
}
else if (name == kw_at_require)
{
if (!parse_doc_contract(c, contracts_ref, next, CONTRACT_REQUIRE)) return false;
if (contracts_ref->deprecated)
{
RETURN_PRINT_ERROR_HERE("Only one `@deprecated` directive is allowed per contract.");
}
Attr *attr = CALLOCS(Attr);
attr->name = kw_at_deprecated;
attr->span = c->prev_span;
attr->path = NULL;
attr->attr_kind = ATTRIBUTE_DEPRECATED;
if (tok_is(c, TOKEN_DOCS_EOL) && peek(c) == TOKEN_STRING)
{
advance(c);
}
SourceSpan start = c->span;
if (tok_is(c, TOKEN_STRING))
{
const char *str = NULL;
size_t len;
if (!parse_joined_strings(c, &str, &len)) return false;
Expr *e = expr_new_const_string(extend_span_with_token(start, c->prev_span), str);
vec_add(attr->exprs, e);
}
contracts_ref->deprecated = attr;
}
else if (name == kw_at_ensure)
{
if (!parse_doc_contract(c, contracts_ref, next, CONTRACT_ENSURE)) return false;
if (!contracts_ref->has_contracts)
{
contracts_ref->first_contract = c->span;
contracts_ref->has_contracts = true;
}
if (!parse_doc_contract(c, &contracts_ref->ensures, "@ensure")) return false;
}
else if (name == kw_at_pure)
{
Ast *ast = ast_new_curr(c, AST_CONTRACT);
ast->contract_stmt.kind = CONTRACT_PURE;
if (contracts_ref->pure)
{
RETURN_PRINT_ERROR_HERE("Multiple '@pure' declarations, please remove one.");
}
if (!contracts_ref->has_contracts)
{
contracts_ref->first_contract = c->span;
contracts_ref->has_contracts = true;
}
contracts_ref->pure = true;
advance(c);
if (!parse_doc_direct_comment(c)) return false;
append_docs(next, contracts_ref, ast);
}
else
{
@@ -3429,6 +3502,7 @@ static bool parse_contracts(ParseContext *c, AstId *contracts_ref)
if (parse_doc_to_eol(c)) continue;
RETURN_PRINT_ERROR_HERE("Expected a string description for the custom contract '%s'.", name);
}
END:
if (parse_doc_to_eol(c)) continue;
PRINT_ERROR_HERE("Expected the end of the contract here.");
return false;
@@ -3488,7 +3562,7 @@ END:
*/
Decl *parse_top_level_statement(ParseContext *c, ParseContext **context_out)
{
AstId contracts = 0;
ContractDescription contracts = { .first = c->span };
if (!parse_contracts(c, &contracts)) return poisoned_decl;
Decl *decl;
@@ -3509,7 +3583,7 @@ Decl *parse_top_level_statement(ParseContext *c, ParseContext **context_out)
switch (tok)
{
case TOKEN_FN:
decl = parse_func_definition(c, contracts, FUNC_PARSE_EXTERN);
decl = parse_func_definition(c, &contracts, FUNC_PARSE_EXTERN);
break;
case TOKEN_CONST:
decl = parse_top_level_const_declaration(c, true);
@@ -3551,13 +3625,13 @@ Decl *parse_top_level_statement(ParseContext *c, ParseContext **context_out)
new_context->unit = unit_create(c->unit->file);
*context_out = c = new_context;
}
if (!parse_module(c, contracts)) return poisoned_decl;
if (!parse_module(c, &contracts)) return poisoned_decl;
return NULL;
case TOKEN_DOCS_START:
PRINT_ERROR_HERE("There are more than one doc comment in a row, that is not allowed.");
return poisoned_decl;
case TOKEN_ALIAS:
decl = parse_alias(c, contracts);
decl = parse_alias(c, &contracts);
if (decl->decl_kind == DECL_ALIAS_PATH)
{
if (!context_out)
@@ -3574,11 +3648,11 @@ Decl *parse_top_level_statement(ParseContext *c, ParseContext **context_out)
attach_contracts = true;
break;
case TOKEN_FN:
decl = parse_func_definition(c, contracts, c->unit->is_interface_file ? FUNC_PARSE_C3I : FUNC_PARSE_REGULAR);
decl = parse_func_definition(c, &contracts, c->unit->is_interface_file ? FUNC_PARSE_C3I : FUNC_PARSE_REGULAR);
break;
case TOKEN_CT_ASSERT:
{
if (contracts) goto CONTRACT_NOT_ALLOWED;
if (contracts.has_contracts) goto CONTRACT_NOT_ALLOWED;
ASSIGN_AST_OR_RET(Ast *ast, parse_ct_assert_stmt(c), poisoned_decl);
decl = decl_new_ct(DECL_CT_ASSERT, ast->span);
decl->ct_assert_decl = ast;
@@ -3586,7 +3660,7 @@ Decl *parse_top_level_statement(ParseContext *c, ParseContext **context_out)
}
case TOKEN_CT_ERROR:
{
if (contracts) goto CONTRACT_NOT_ALLOWED;
if (contracts.has_contracts) goto CONTRACT_NOT_ALLOWED;
ASSIGN_AST_OR_RET(Ast *ast, parse_ct_error_stmt(c), poisoned_decl);
decl = decl_new_ct(DECL_CT_ASSERT, ast->span);
decl->ct_assert_decl = ast;
@@ -3594,14 +3668,14 @@ Decl *parse_top_level_statement(ParseContext *c, ParseContext **context_out)
}
case TOKEN_CT_ECHO:
{
if (contracts) goto CONTRACT_NOT_ALLOWED;
if (contracts.has_contracts) goto CONTRACT_NOT_ALLOWED;
ASSIGN_AST_OR_RET(Ast *ast, parse_ct_echo_stmt(c), poisoned_decl);
decl = decl_new_ct(DECL_CT_ECHO, ast->span);
decl->ct_echo_decl = ast;
break;
}
case TOKEN_IMPORT:
if (contracts) goto CONTRACT_NOT_ALLOWED;
if (contracts.has_contracts) goto CONTRACT_NOT_ALLOWED;
if (!context_out)
{
PRINT_ERROR_HERE("'import' may not appear inside a compile time statement.");
@@ -3610,11 +3684,11 @@ Decl *parse_top_level_statement(ParseContext *c, ParseContext **context_out)
if (!parse_import(c)) return poisoned_decl;
return NULL;
case TOKEN_CT_INCLUDE:
if (contracts) goto CONTRACT_NOT_ALLOWED;
if (contracts.has_contracts) goto CONTRACT_NOT_ALLOWED;
decl = parse_ct_include(c);
break;
case TOKEN_CT_EXEC:
if (contracts) goto CONTRACT_NOT_ALLOWED;
if (contracts.has_contracts) goto CONTRACT_NOT_ALLOWED;
decl = parse_exec(c);
break;
case TOKEN_BITSTRUCT:
@@ -3639,11 +3713,11 @@ Decl *parse_top_level_statement(ParseContext *c, ParseContext **context_out)
attach_contracts = true;
break;
case TOKEN_MACRO:
decl = parse_macro_declaration(c, contracts);
decl = parse_macro_declaration(c, &contracts);
break;
case TOKEN_ENUM:
case TOKEN_CENUM:
decl = parse_enum_declaration(c, false);
case TOKEN_CONSTDEF:
decl = parse_enum_declaration(c);
attach_contracts = true;
break;
case TOKEN_FAULTDEF:
@@ -3683,10 +3757,12 @@ Decl *parse_top_level_statement(ParseContext *c, ParseContext **context_out)
return poisoned_decl;
}
if (!decl_ok(decl)) return decl;
if (attach_contracts && contracts && !parse_attach_contracts(decl_template_get_generic(decl), contracts)) return poisoned_decl;
attach_deprecation_from_contract(c, &contracts, decl);
if (attach_contracts && contracts.has_contracts && !parse_attach_contracts(decl_template_get_generic(decl), &contracts)) return poisoned_decl;
ASSERT(decl);
return decl;
CONTRACT_NOT_ALLOWED:
RETURN_PRINT_ERROR_AT(poisoned_decl, astptr(contracts), "Contracts are only used for modules, functions and macros.");
print_error_at(contracts.first, "Contracts are only used for modules, functions and macros.");
return poisoned_decl;
}

View File

@@ -1444,7 +1444,7 @@ Ast *parse_stmt(ParseContext *c)
case TOKEN_BIT_AND_ASSIGN:
case TOKEN_BIT_OR_ASSIGN:
case TOKEN_BIT_XOR_ASSIGN:
case TOKEN_CENUM:
case TOKEN_CONSTDEF:
case TOKEN_COLON:
case TOKEN_COMMA:
case TOKEN_CT_CASE:

View File

@@ -15,6 +15,24 @@ typedef enum
PARAM_PARSE_ATTR,
} ParameterParseKind;
typedef struct
{
const char *comment;
SourceSpan comment_span;
unsigned comment_len;
Expr **requires;
Expr **ensures;
ContractParam *params;
bool pure;
bool has_contracts;
SourceSpan first;
SourceSpan first_non_require;
SourceSpan first_contract;
Expr **opt_returns;
Attr *deprecated;
} ContractDescription;
#define EMPTY_CONTRACT ((ContractDescription){ NULL })
#define EXPECT_IDENT_FOR_OR(_name, _res) do { if (!expect_ident(c, _name)) return _res; } while(0)
#define EXPECT_OR_RET(_tok, _res) do { if (!expect(c, _tok)) return _res; } while(0)
#define CONSUME_OR_RET(_tok, _res) do { if (!expect(c, _tok)) return _res; advance(c); } while(0)
@@ -46,7 +64,7 @@ Ast *parse_short_body(ParseContext *c, TypeInfoId return_type, bool is_regular_f
bool parse_attribute(ParseContext *c, Attr **attribute_ref, bool expect_eos);
bool parse_attributes(ParseContext *c, Attr ***attributes_ref, Visibility *visibility_ref, bool *builtin_ref, bool *cond_ref);
bool parse_attributes(ParseContext *c, Attr ***attributes_ref, Visibility *visibility_ref, bool *builtin_ref, bool *cond_ref, const char *reject_visibility);
Decl *parse_generic_decl(ParseContext *c);
bool parse_switch_body(ParseContext *c, Ast ***cases, TokenType case_type, TokenType default_type);
@@ -74,7 +92,7 @@ INLINE void add_decl_to_list(Decl ***list, Decl *decl)
vec_add(*list, decl);
}
bool parse_module(ParseContext *c, AstId contracts);
bool parse_module(ParseContext *c, ContractDescription *contracts);
bool try_consume(ParseContext *c, TokenType type);
bool consume(ParseContext *c, TokenType type, const char *message, ...);

View File

@@ -1238,7 +1238,7 @@ RETRY:;
inner = decl->strukt.members[0]->type->canonical;
break;
case DECL_ENUM:
case DECL_CONST_ENUM:
case DECL_CONSTDEF:
// Could be made to work.
return false;
default:
@@ -1656,7 +1656,7 @@ static bool rule_enum_to_value(CastContext *cc, bool is_explicit, bool is_silent
return cast_is_allowed(cc, is_explicit, is_silent);
}
ASSERT(enum_decl->decl_kind != DECL_CONST_ENUM);
ASSERT(enum_decl->decl_kind != DECL_CONSTDEF);
Type *inner = enum_decl->enums.type_info->type;
if (!type_is_integer_or_bool_kind(type_flatten(cc->to)))
@@ -2698,7 +2698,7 @@ static ConvGroup group_from_type[TYPE_LAST + 1] = {
[TYPE_TYPEID] = CONV_TYPEID,
[TYPE_POINTER] = CONV_POINTER,
[TYPE_ENUM] = CONV_ENUM,
[TYPE_CONST_ENUM] = CONV_RAW_ENUM,
[TYPE_CONSTDEF] = CONV_RAW_ENUM,
[TYPE_FUNC_PTR] = CONV_FUNC,
[TYPE_STRUCT] = CONV_STRUCT,
[TYPE_UNION] = CONV_UNION,

Some files were not shown because too many files have changed in this diff Show More