Merge branch 'master' into pr/7345

This commit is contained in:
Jeroen van Rijn
2026-08-16 11:19:48 +02:00
14 changed files with 2099 additions and 33 deletions

View File

@@ -125,7 +125,7 @@ NetBSD)
;;
Linux)
CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
LDFLAGS="$LDFLAGS -lstdc++ -ldl $($LLVM_CONFIG --libs core native --system-libs --libfiles)"
LDFLAGS="$LDFLAGS -lstdc++ -ldl $($LLVM_CONFIG --libs core native passes arm aarch64 x86 webassembly riscv --system-libs --libfiles)"
# Copy libLLVM*.so into current directory for linking
# NOTE: This is needed by the Linux release pipeline!
# cp $(readlink -f $($LLVM_CONFIG --libfiles)) ./

View File

@@ -95,12 +95,12 @@ Decodes a hex sequence into a byte slice
*Allocates Using Provided Allocator*
Inputs:
- dst: The hex sequence decoded into bytes
- src: The `[]byte` to be hex-decoded
- allocator: (default: context.allocator)
- loc: The caller location for debugging purposes (default: #caller_location)
Returns:
- dst: The hex sequence decoded into bytes
- ok: A bool, `true` if decoding succeeded, `false` otherwise
*/
decode :: proc(src: []byte, allocator := context.allocator, loc := #caller_location) -> (dst: []byte, ok: bool) {
@@ -123,6 +123,40 @@ decode :: proc(src: []byte, allocator := context.allocator, loc := #caller_locat
return dst, true
}
/*
Decodes a hex sequence into a byte slice
Inputs:
- src: The `[]byte` to be hex-decoded
- buf: A buffer large enough to hold the decoded sequence
Returns:
- dst: The hex sequence decoded into bytes
- ok: A bool, `true` if decoding succeeded, `false` otherwise
*/
decode_into_buffer :: proc(src: []byte, buf: []byte) -> (dst: []byte, ok: bool) #optional_ok {
if len(src) % 2 == 1 {
return
}
dst_len := len(src) / 2
if len(buf) < dst_len {
return
}
#no_bounds_check for i, j := 0, 1; j < len(src); j += 2 {
p := src[j-1]
q := src[j]
a := hex_digit(p) or_return
b := hex_digit(q) or_return
buf[i] = (a << 4) | b
i += 1
}
return buf[:dst_len], true
}
/*
Decodes the first byte in a hex sequence to a byte
@@ -173,4 +207,4 @@ hex_digit :: proc(char: byte) -> (u8, bool) {
case 'A' ..= 'F': return char - 'A' + 10, true
case: return 0, false
}
}
}

View File

@@ -148,7 +148,7 @@ write :: proc(dst: []byte, x: $T/Fixed($Backing, $Fraction_Width)) -> string {
}
}
n := copy(dst, buf[:i])
copy(dst, buf[:i])
return string(dst[:i])
}

File diff suppressed because it is too large Load Diff

View File

@@ -502,6 +502,134 @@ vtbl4_p8 :: #force_inline proc "c" (t: poly8x8x4_t, idx: poly8x8_t) -> poly8x8_t
}
}
// Extended Table Lookup.
//
// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vtbx1_p8)
@(require_results, enable_target_feature = "neon")
vtbx1_p8 :: #force_inline proc "c" (v: poly8x8_t, t: poly8x8_t, idx: uint8x8_t) -> poly8x8_t {
when ODIN_ARCH == .arm64 {
return simd.select(
simd.lanes_lt(idx, uint8x8_t(8)),
vqtbx1_p8(v, vcombine_p8(t, poly8x8_t{}), idx),
v,
)
} else {
return transmute(poly8x8_t)_vtbx1(
transmute(int8x8_t)v,
transmute(int8x8_t)t,
transmute(int8x8_t)idx,
)
}
}
// Extended Table Lookup.
//
// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vtbx2_p8)
@(require_results, enable_target_feature = "neon")
vtbx2_p8 :: #force_inline proc "c" (v: poly8x8_t, t: poly8x8x2_t, idx: uint8x8_t) -> poly8x8_t {
when ODIN_ARCH == .arm64 {
return simd.select(
simd.lanes_lt(idx, uint8x8_t(16)),
vqtbx1_p8(v, vcombine_p8(t.x, t.y), idx),
v,
)
} else {
return transmute(poly8x8_t)_vtbx2(
transmute(int8x8_t)v,
transmute(int8x8_t)t.x,
transmute(int8x8_t)t.y,
transmute(int8x8_t)idx,
)
}
}
// Extended Table Lookup.
//
// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vtbx3_p8)
@(require_results, enable_target_feature = "neon")
vtbx3_p8 :: #force_inline proc "c" (v: poly8x8_t, t: poly8x8x3_t, idx: uint8x8_t) -> poly8x8_t {
when ODIN_ARCH == .arm64 {
x := poly8x16x2_t {
vcombine_p8(t.x, t.y),
vcombine_p8(t.z, poly8x8_t{}),
}
return simd.select(
simd.lanes_lt(idx, uint8x8_t(24)),
vqtbx2_p8(v, x, idx),
v,
)
} else {
return transmute(poly8x8_t)_vtbx3(
transmute(int8x8_t)v,
transmute(int8x8_t)t.x,
transmute(int8x8_t)t.y,
transmute(int8x8_t)t.z,
transmute(int8x8_t)idx,
)
}
}
// Extended Table Lookup.
//
// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vtbx4_p8)
@(require_results, enable_target_feature = "neon")
vtbx4_p8 :: #force_inline proc "c" (v: poly8x8_t, t: poly8x8x4_t, idx: uint8x8_t) -> poly8x8_t {
when ODIN_ARCH == .arm64 {
x := poly8x16x2_t {
vcombine_p8(t.x, t.y),
vcombine_p8(t.z, t.w),
}
return simd.select(
simd.lanes_lt(idx, uint8x8_t(32)),
vqtbx2_p8(v, x, idx),
v,
)
} else {
return transmute(poly8x8_t)_vtbx4(
transmute(int8x8_t)v,
transmute(int8x8_t)t.x,
transmute(int8x8_t)t.y,
transmute(int8x8_t)t.z,
transmute(int8x8_t)t.w,
transmute(int8x8_t)idx,
)
}
}
// Population count per byte.
//
// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vcnt_p8)
@(require_results, enable_target_feature = "neon")
vcnt_p8 :: #force_inline proc "c" (a: poly8x8_t) -> poly8x8_t {
return transmute(poly8x8_t)vcnt_s8(transmute(int8x8_t)a)
}
// Population count per byte.
//
// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vcntq_p8)
@(require_results, enable_target_feature = "neon")
vcntq_p8 :: #force_inline proc "c" (a: poly8x16_t) -> poly8x16_t {
return transmute(poly8x16_t)vcntq_s8(transmute(int8x16_t)a)
}
// Bitwise Not.
//
// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vmvn_p8)
@(require_results, enable_target_feature = "neon")
vmvn_p8 :: #force_inline proc "c" (a: poly8x8_t) -> poly8x8_t {
b := poly8x8_t(max(poly8_t))
return simd.bit_xor(a, b)
}
// Bitwise Not.
//
// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vmvnq_p8)
@(require_results, enable_target_feature = "neon")
vmvnq_p8 :: #force_inline proc "c" (a: poly8x16_t) -> poly8x16_t {
b := poly8x16_t(max(poly8_t))
return simd.bit_xor(a, b)
}
when ODIN_ARCH == .arm64 {
// Polynomial multiply long
//
@@ -732,6 +860,246 @@ when ODIN_ARCH == .arm64 {
return simd.shuffle(c, c, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
}
}
// Extended Table Lookup.
//
// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqtbx1_p8)
@(require_results, enable_target_feature = "neon")
vqtbx1_p8 :: #force_inline proc "c" (v: poly8x8_t, t: poly8x16_t, idx: uint8x8_t) -> poly8x8_t {
when ODIN_ENDIAN == .Little {
return transmute(poly8x8_t)_vqtbx1(
transmute(int8x8_t)v,
transmute(int8x16_t)t,
idx,
)
} else {
v := simd.shuffle(v, v, 7, 6, 5, 4, 3, 2, 1, 0)
t := simd.shuffle(t, t, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
idx := simd.shuffle(idx, idx, 7, 6, 5, 4, 3, 2, 1, 0)
c := transmute(poly8x8_t)_vqtbx1(
transmute(int8x8_t)v,
transmute(int8x16_t)t,
idx,
)
return simd.shuffle(c, c, 7, 6, 5, 4, 3, 2, 1, 0)
}
}
// Extended Table Lookup.
//
// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqtbx1q_p8)
@(require_results, enable_target_feature = "neon")
vqtbx1q_p8 :: #force_inline proc "c" (v: poly8x16_t, t: poly8x16_t, idx: uint8x16_t) -> poly8x16_t {
when ODIN_ENDIAN == .Little {
return transmute(poly8x16_t)_vqtbx1q(
transmute(int8x16_t)v,
transmute(int8x16_t)t,
idx,
)
} else {
v := simd.shuffle(v, v, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
t := simd.shuffle(t, t, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
idx := simd.shuffle(idx, idx, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
c := transmute(poly8x16_t)_vqtbx1q(
transmute(int8x16_t)v,
transmute(int8x16_t)t,
idx,
)
return simd.shuffle(c, c, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
}
}
// Extended Table Lookup.
//
// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqtbx2_p8)
@(require_results, enable_target_feature = "neon")
vqtbx2_p8 :: #force_inline proc "c" (v: poly8x8_t, t: poly8x16x2_t, idx: uint8x8_t) -> poly8x8_t {
when ODIN_ENDIAN == .Little {
return transmute(poly8x8_t)_vqtbx2(
transmute(int8x8_t)v,
transmute(int8x16_t)t.x,
transmute(int8x16_t)t.y,
idx,
)
} else {
v := simd.shuffle(v, v, 7, 6, 5, 4, 3, 2, 1, 0)
t := poly8x16x2_t {
simd.shuffle(t.x, t.x, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
simd.shuffle(t.y, t.y, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
}
idx := simd.shuffle(idx, idx, 7, 6, 5, 4, 3, 2, 1, 0)
c := transmute(poly8x8_t)_vqtbx2(
transmute(int8x8_t)v,
transmute(int8x16_t)t.x,
transmute(int8x16_t)t.y,
idx,
)
return simd.shuffle(c, c, 7, 6, 5, 4, 3, 2, 1, 0)
}
}
// Extended Table Lookup.
//
// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqtbx2q_p8)
@(require_results, enable_target_feature = "neon")
vqtbx2q_p8 :: #force_inline proc "c" (v: poly8x16_t, t: poly8x16x2_t, idx: uint8x16_t) -> poly8x16_t {
when ODIN_ENDIAN == .Little {
return transmute(poly8x16_t)_vqtbx2q(
transmute(int8x16_t)v,
transmute(int8x16_t)t.x,
transmute(int8x16_t)t.y,
idx,
)
} else {
v := simd.shuffle(v, v, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
t := poly8x16x2_t {
simd.shuffle(t.x, t.x, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
simd.shuffle(t.y, t.y, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
}
idx := simd.shuffle(idx, idx, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
c := transmute(poly8x16_t)_vqtbx2q(
transmute(int8x16_t)v,
transmute(int8x16_t)t.x,
transmute(int8x16_t)t.y,
idx,
)
return simd.shuffle(c, c, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
}
}
// Extended Table Lookup.
//
// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqtbx3_p8)
@(require_results, enable_target_feature = "neon")
vqtbx3_p8 :: #force_inline proc "c" (v: poly8x8_t, t: poly8x16x3_t, idx: uint8x8_t) -> poly8x8_t {
when ODIN_ENDIAN == .Little {
return transmute(poly8x8_t)_vqtbx3(
transmute(int8x8_t)v,
transmute(int8x16_t)t.x,
transmute(int8x16_t)t.y,
transmute(int8x16_t)t.z,
idx,
)
} else {
v := simd.shuffle(v, v, 7, 6, 5, 4, 3, 2, 1, 0)
t := poly8x16x3_t {
simd.shuffle(t.x, t.x, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
simd.shuffle(t.y, t.y, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
simd.shuffle(t.z, t.z, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
}
idx := simd.shuffle(idx, idx, 7, 6, 5, 4, 3, 2, 1, 0)
c := transmute(poly8x8_t)_vqtbx3(
transmute(int8x8_t)v,
transmute(int8x16_t)t.x,
transmute(int8x16_t)t.y,
transmute(int8x16_t)t.z,
idx,
)
return simd.shuffle(c, c, 7, 6, 5, 4, 3, 2, 1, 0)
}
}
// Extended Table Lookup.
//
// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqtbx3q_p8)
@(require_results, enable_target_feature = "neon")
vqtbx3q_p8 :: #force_inline proc "c" (v: poly8x16_t, t: poly8x16x3_t, idx: uint8x16_t) -> poly8x16_t {
when ODIN_ENDIAN == .Little {
return transmute(poly8x16_t)_vqtbx3q(
transmute(int8x16_t)v,
transmute(int8x16_t)t.x,
transmute(int8x16_t)t.y,
transmute(int8x16_t)t.z,
idx,
)
} else {
v := simd.shuffle(v, v, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
t := poly8x16x3_t {
simd.shuffle(t.x, t.x, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
simd.shuffle(t.y, t.y, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
simd.shuffle(t.z, t.z, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
}
idx := simd.shuffle(idx, idx, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
c := transmute(poly8x16_t)_vqtbx3q(
transmute(int8x16_t)v,
transmute(int8x16_t)t.x,
transmute(int8x16_t)t.y,
transmute(int8x16_t)t.z,
idx,
)
return simd.shuffle(c, c, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
}
}
// Extended Table Lookup.
//
// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqtbx4_p8)
@(require_results, enable_target_feature = "neon")
vqtbx4_p8 :: #force_inline proc "c" (v: poly8x8_t, t: poly8x16x4_t, idx: uint8x8_t) -> poly8x8_t {
when ODIN_ENDIAN == .Little {
return transmute(poly8x8_t)_vqtbx4(
transmute(int8x8_t)v,
transmute(int8x16_t)t.x,
transmute(int8x16_t)t.y,
transmute(int8x16_t)t.z,
transmute(int8x16_t)t.w,
idx,
)
} else {
v := simd.shuffle(v, v, 7, 6, 5, 4, 3, 2, 1, 0)
t := poly8x16x4_t {
simd.shuffle(t.x, t.x, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
simd.shuffle(t.y, t.y, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
simd.shuffle(t.z, t.z, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
simd.shuffle(t.w, t.w, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
}
idx := simd.shuffle(idx, idx, 7, 6, 5, 4, 3, 2, 1, 0)
c := transmute(poly8x8_t)_vqtbx4(
transmute(int8x8_t)v,
transmute(int8x16_t)t.x,
transmute(int8x16_t)t.y,
transmute(int8x16_t)t.z,
transmute(int8x16_t)t.w,
idx,
)
return simd.shuffle(c, c, 7, 6, 5, 4, 3, 2, 1, 0)
}
}
// Extended Table Lookup.
//
// [Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/vqtbx4q_p8)
@(require_results, enable_target_feature = "neon")
vqtbx4q_p8 :: #force_inline proc "c" (v: poly8x16_t, t: poly8x16x4_t, idx: uint8x16_t) -> poly8x16_t {
when ODIN_ENDIAN == .Little {
return transmute(poly8x16_t)_vqtbx4q(
transmute(int8x16_t)v,
transmute(int8x16_t)t.x,
transmute(int8x16_t)t.y,
transmute(int8x16_t)t.z,
transmute(int8x16_t)t.w,
idx,
)
} else {
v := simd.shuffle(v, v, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
t := poly8x16x4_t {
simd.shuffle(t.x, t.x, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
simd.shuffle(t.y, t.y, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
simd.shuffle(t.z, t.z, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
simd.shuffle(t.w, t.w, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
}
idx := simd.shuffle(idx, idx, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
c := transmute(poly8x16_t)_vqtbx4q(
transmute(int8x16_t)v,
transmute(int8x16_t)t.x,
transmute(int8x16_t)t.y,
transmute(int8x16_t)t.z,
transmute(int8x16_t)t.w,
idx,
)
return simd.shuffle(c, c, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
}
}
}
@(private, default_calling_convention = "none")

View File

@@ -2244,7 +2244,10 @@ gb_internal LoadDirectiveResult check_load_directory_directive(CheckerContext *c
String name = bd->name.string;
GB_ASSERT(name == "load_directory");
if (ce->args.count != 1) {
if (ce->args.count == 0) {
error(ce->close, "'#%.*s' expects 1 argument, got 0", LIT(name));
return LoadDirective_Error;
} else if (ce->args.count != 1) {
error(ce->args[0], "'#%.*s' expects 1 argument, got %td", LIT(name), ce->args.count);
return LoadDirective_Error;
}

View File

@@ -3343,27 +3343,29 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper
case Token_Lt:
case Token_LtEq:
{
// subset: (lhs & rhs) == lhs. a proper subset also requires lhs != rhs
ExactValue lhs = x->value;
ExactValue rhs = y->value;
ExactValue res = exact_binary_operator_value(Token_And, lhs, rhs);
res = exact_value_bool(compare_exact_values(op, res, lhs));
ExactValue both = exact_binary_operator_value(Token_And, lhs, rhs);
bool res = compare_exact_values(Token_CmpEq, both, lhs);
if (op == Token_Lt) {
res = exact_binary_operator_value(Token_And, res, exact_value_bool(compare_exact_values(op, lhs, rhs)));
res = res && compare_exact_values(Token_NotEq, lhs, rhs);
}
x->value = res;
x->value = exact_value_bool(res);
break;
}
case Token_Gt:
case Token_GtEq:
{
// superset: (lhs & rhs) == rhs
ExactValue lhs = x->value;
ExactValue rhs = y->value;
ExactValue res = exact_binary_operator_value(Token_And, lhs, rhs);
res = exact_value_bool(compare_exact_values(op, res, rhs));
ExactValue both = exact_binary_operator_value(Token_And, lhs, rhs);
bool res = compare_exact_values(Token_CmpEq, both, rhs);
if (op == Token_Gt) {
res = exact_binary_operator_value(Token_And, res, exact_value_bool(compare_exact_values(op, lhs, rhs)));
res = res && compare_exact_values(Token_NotEq, lhs, rhs);
}
x->value = res;
x->value = exact_value_bool(res);
break;
}
}

View File

@@ -1368,7 +1368,7 @@ gb_internal void check_bit_set_type(CheckerContext *c, Type *type, Type *named_t
gb_free(a, s.text);
return;
}
if (!check_representable_as_constant(c, iv, t, nullptr)) {
if (!check_representable_as_constant(c, jv, t, nullptr)) {
gbAllocator a = heap_allocator();
String s = big_int_to_string(a, &j);
gbString ts = type_to_string(t);

View File

@@ -1740,7 +1740,13 @@ gb_internal void lb_build_unroll_range_stmt(lbProcedure *p, AstUnrollRangeStmt *
slice = lb_emit_load(p, slice);
} else {
count_ptr = lb_add_local_generated(p, t_int, false).addr;
lb_emit_store(p, count_ptr, lb_slice_len(p, slice));
if (t->kind == Type_Slice) {
lb_emit_store(p, count_ptr, lb_slice_len(p, slice));
} else if (t->kind == Type_DynamicArray) {
lb_emit_store(p, count_ptr, lb_dynamic_array_len(p, slice));
} else {
GB_ASSERT_MSG(false, "Need to add support for this type.");
}
}
data_ptr = lb_emit_struct_ev(p, slice, 0);
break;

View File

@@ -1446,11 +1446,13 @@ gb_internal bool is_type_ordered(Type *t) {
return false;
}
gb_internal bool is_type_ordered_numeric(Type *t) {
t = core_type(t);
t = base_type(t);
if (t == nullptr) { return false; }
switch (t->kind) {
case Type_Basic:
return (t->Basic.flags & BasicFlag_OrderedNumeric) != 0;
case Type_Enum:
return is_type_ordered_numeric(t->Enum.base_type);
}
return false;
}

View File

@@ -47,6 +47,60 @@ hex_decode :: proc(t: ^testing.T) {
}
}
@(test)
hex_decode_into_buffer :: proc(t: ^testing.T) {
for test in CASES {
buffer := make([]u8, len(test[1]) / 2)
defer delete(buffer)
decoded, ok := hex.decode_into_buffer(transmute([]byte)test[1], buffer)
testing.expect(t, ok, "decode_into_buffer: not ok")
testing.expectf(
t,
ok,
"decode: %q not ok",
test[1],
)
testing.expectf(
t,
string(decoded) == test[0],
"decode: %q -> %q (should be: %q)",
test[1],
string(decoded),
test[0],
)
}
// destination buffer is larger
for test in CASES {
buffer := make([]u8, len(test[1]) / 2 + 1)
defer delete(buffer)
decoded, ok := hex.decode_into_buffer(transmute([]byte)test[1], buffer)
testing.expect(t, ok, "decode_into_buffer: not ok")
testing.expectf(
t,
ok,
"decode: %q not ok",
test[1],
)
testing.expectf(
t,
string(decoded) == test[0],
"decode: %q -> %q (should be: %q)",
test[1],
string(decoded),
test[0],
)
}
// destination buffer is too small
for test in CASES {
buffer := make([]u8, len(test[1]) / 2 - 1)
defer delete(buffer)
_, ok := hex.decode_into_buffer(transmute([]byte)test[1], buffer)
testing.expect(t, !ok, "decode_into_buffer: should not be ok")
}
}
@(test)
hex_decode_sequence :: proc(t: ^testing.T) {
b, ok := hex.decode_sequence("0x23")
@@ -83,4 +137,4 @@ hex_decode_sequence :: proc(t: ^testing.T) {
_, ok = hex.decode_sequence("123")
testing.expect(t, !ok, "decode_sequence: 123 should be too long")
}
}

View File

@@ -136,4 +136,78 @@ bitwise_constant_folding_matches_runtime :: proc(t: ^testing.T) {
testing.expect_value(t, 0 | -3, a | b)
testing.expect_value(t, 0 ~ -3, a ~ b)
}
// `<=` and `<` on a `bit_set` are subset and proper subset, `>=` and `>` superset. The folder
// asked `(lhs & rhs) <= lhs` where the definition is `(lhs & rhs) == lhs`, which is true for
// any operands, so `<=` folded true unconditionally; `<` compounded it by requiring `lhs < rhs`
// where it needs `lhs != rhs`. Under `when` this decides which declarations exist.
}
@(test)
bit_set_subset_folding_matches_runtime :: proc(t: ^testing.T) {
B :: bit_set[0..<4]
{ // disjoint: neither a subset nor a superset
a, b := B{0, 3}, B{0, 1}
testing.expect_value(t, B{0, 3} <= B{0, 1}, a <= b)
testing.expect_value(t, B{0, 3} <= B{0, 1}, false)
testing.expect_value(t, B{0, 3} >= B{0, 1}, a >= b)
testing.expect_value(t, B{0, 3} >= B{0, 1}, false)
}
{ // proper subset
a, b := B{0}, B{0, 1}
testing.expect_value(t, B{0} <= B{0, 1}, a <= b)
testing.expect_value(t, B{0} <= B{0, 1}, true)
testing.expect_value(t, B{0} < B{0, 1}, a < b)
testing.expect_value(t, B{0} < B{0, 1}, true)
}
{ // equal: a subset but not a proper one
a, b := B{0, 1}, B{0, 1}
testing.expect_value(t, B{0, 1} <= B{0, 1}, a <= b)
testing.expect_value(t, B{0, 1} <= B{0, 1}, true)
testing.expect_value(t, B{0, 1} < B{0, 1}, a < b)
testing.expect_value(t, B{0, 1} < B{0, 1}, false)
}
{ // superset
a, b := B{0, 1}, B{0}
testing.expect_value(t, B{0, 1} <= B{0}, a <= b)
testing.expect_value(t, B{0, 1} <= B{0}, false)
testing.expect_value(t, B{0, 1} > B{0}, a > b)
testing.expect_value(t, B{0, 1} > B{0}, true)
}
{ // the empty set is a subset of everything, and a proper one unless both are empty
a, b := B{}, B{0}
testing.expect_value(t, B{} < B{0}, a < b)
testing.expect_value(t, B{} < B{0}, true)
}
{
a, b := B{}, B{}
testing.expect_value(t, B{} <= B{}, a <= b)
testing.expect_value(t, B{} <= B{}, true)
testing.expect_value(t, B{} < B{}, a < b)
testing.expect_value(t, B{} < B{}, false)
}
// equality was never affected, so a fix here must not disturb it
{
a, b := B{0, 1}, B{1, 0}
testing.expect_value(t, B{0, 1} == B{1, 0}, a == b)
testing.expect_value(t, B{0, 1} == B{1, 0}, true)
testing.expect_value(t, B{0, 1} != B{0}, a != B{0})
}
}
// a mis-folded subset test selects the wrong `when` arm, which changes which declarations exist
@(test)
bit_set_subset_folding_selects_the_right_when_arm :: proc(t: ^testing.T) {
B :: bit_set[0..<4]
when (B{0} < B{0, 1}) { W1 :: 1 } else { W1 :: 0 }
when (B{0, 3} <= B{0, 1}) { W2 :: 0 } else { W2 :: 1 }
when (B{0, 1} <= B{0, 1}) { W3 :: 1 } else { W3 :: 0 }
when (B{0, 1} > B{0}) { W4 :: 1 } else { W4 :: 0 }
testing.expect_value(t, W1, 1)
testing.expect_value(t, W2, 1)
testing.expect_value(t, W3, 1)
testing.expect_value(t, W4, 1)
}

View File

@@ -107,6 +107,13 @@ else
exit 1
fi
if [[ $($ODIN check ../test_issue_7304.odin -no-entry-point $COMMON_CHECK 2>&1 >/dev/null | grep -c "9223372036854775808 is not representable by int") -eq 1 ]]; then
echo "SUCCESSFUL 1/1"
else
echo "SUCCESSFUL 0/1"
exit 1
fi
clang -c ../test_issue_7010.c -o test_issue_7010_c.o
$ODIN test ../test_issue_7010.odin $COMMON

View File

@@ -0,0 +1,4 @@
// Tests issue #7304 https://github.com/odin-lang/Odin/issues/7304
package test_issues
Bad_Bit_Set :: bit_set[-1 ..< 9223372036854775808]