mirror of
https://github.com/odin-lang/Odin.git
synced 2026-09-01 17:53:35 +00:00
Merge branch 'master' of https://github.com/odin-lang/Odin
This commit is contained in:
4
.gitignore
vendored
4
.gitignore
vendored
@@ -277,6 +277,8 @@ odin
|
||||
*.bin
|
||||
demo.bin
|
||||
libLLVM*.so*
|
||||
*.a
|
||||
*.o
|
||||
|
||||
# core:rexcode commits its generated encode/decode tables: each arch's tablegen
|
||||
# emits human-readable Odin, serialized to tables/*.bin, which the library
|
||||
@@ -321,5 +323,3 @@ build/
|
||||
cmake-build*/
|
||||
CMakeLists.txt
|
||||
sandbox/
|
||||
|
||||
vendor/box3d/lib/box3d_wasm.o
|
||||
|
||||
@@ -7008,6 +7008,18 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
|
||||
}
|
||||
}
|
||||
|
||||
// an `asm` template's `$` parameter is encoded as an immediate, so only a constant can reach it
|
||||
if (e && e->kind == Entity_Variable && (e->flags & EntityFlag_PolyConst)) {
|
||||
if (o->mode != Addressing_Constant) {
|
||||
if (show_error) {
|
||||
gbString str = expr_to_string(o->expr);
|
||||
error(o->expr, "Expected a constant value for the '$' immediate '%.*s', got %s", LIT(e->token.string), str);
|
||||
gb_string_free(str);
|
||||
}
|
||||
err = CallArgumentError_NoneConstantParameter;
|
||||
}
|
||||
}
|
||||
|
||||
if (e && e->kind == Entity_Constant && is_type_proc(e->type)) {
|
||||
bool ok = false;
|
||||
if (o->mode == Addressing_Constant) {
|
||||
|
||||
@@ -3872,6 +3872,24 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
|
||||
rhs = lb_emit_byte_swap(p, {rhs, pt}, pt).value;
|
||||
}
|
||||
|
||||
if (is_type_boolean(a) && is_type_boolean(b) && (op_kind == Token_CmpEq || op_kind == Token_NotEq)) {
|
||||
// anything not 0 is true, which is what control flow already tests for
|
||||
bool lhs_is_const = LLVMIsAConstantInt(lhs) != nullptr;
|
||||
bool rhs_is_const = LLVMIsAConstantInt(rhs) != nullptr;
|
||||
if (lhs_is_const != rhs_is_const) {
|
||||
// against a literal, the truthiness test is the whole comparison
|
||||
LLVMValueRef v = rhs_is_const ? lhs : rhs;
|
||||
LLVMValueRef c = rhs_is_const ? rhs : lhs;
|
||||
bool is_true = LLVMConstIntGetZExtValue(c) != 0;
|
||||
pred = ((op_kind == Token_CmpEq) == is_true) ? LLVMIntNE : LLVMIntEQ;
|
||||
lhs = v;
|
||||
rhs = LLVMConstNull(LLVMTypeOf(v));
|
||||
} else {
|
||||
lhs = LLVMBuildICmp(p->builder, LLVMIntNE, lhs, LLVMConstNull(LLVMTypeOf(lhs)), "");
|
||||
rhs = LLVMBuildICmp(p->builder, LLVMIntNE, rhs, LLVMConstNull(LLVMTypeOf(rhs)), "");
|
||||
}
|
||||
}
|
||||
|
||||
res.value = LLVMBuildICmp(p->builder, pred, lhs, rhs, "");
|
||||
} else if (is_type_float(a)) {
|
||||
LLVMRealPredicate pred = {};
|
||||
|
||||
@@ -43,6 +43,7 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused
|
||||
..\..\..\odin test ..\test_issue_7008.odin %COMMON% || exit /b
|
||||
..\..\..\odin check ..\test_issue_7012.odin -no-entry-point %COMMON% || exit /b
|
||||
..\..\..\odin check ..\test_issue_7260.odin -no-entry-point %COMMON% || exit /b
|
||||
..\..\..\odin test ..\test_issue_bool_comparison_truthiness.odin %COMMON% || exit /b
|
||||
..\..\..\odin check ..\test_issue_ellipsis_type_call.odin -no-entry-point %COMMON% 2>&1 | find /c "Error:" | findstr /x "10" || exit /b
|
||||
..\..\..\odin check ..\test_issue_foreign_redeclaration.odin -no-entry-point %COMMON% || exit /b
|
||||
..\..\..\odin check ..\test_issue_foreign_redeclaration_mismatch.odin -no-entry-point %COMMON% 2>&1 | find /c "Error:" | findstr /x "1" || exit /b
|
||||
|
||||
57
tests/issues/test_issue_bool_comparison_truthiness.odin
Normal file
57
tests/issues/test_issue_bool_comparison_truthiness.odin
Normal file
@@ -0,0 +1,57 @@
|
||||
package test_issues
|
||||
|
||||
import "core:testing"
|
||||
|
||||
// A boolean is true when its payload is non-zero, which is what `if`, `!` and `&&` test for.
|
||||
// `==`, `!=` and `switch` compared the payload against 1 instead, so a boolean decoded from
|
||||
// bytes -- transmuted, read through a pointer, or returned by a foreign procedure -- was true
|
||||
// under `if` yet matched neither arm of its own switch.
|
||||
|
||||
@(test)
|
||||
bool_comparison_uses_truthiness :: proc(t: ^testing.T) {
|
||||
two: u8 = 2
|
||||
one: u8 = 1
|
||||
nil_: u8 = 0
|
||||
|
||||
b := transmute(bool)two
|
||||
c := transmute(bool)one
|
||||
f := transmute(bool)nil_
|
||||
|
||||
testing.expect(t, b, "a non-zero payload is true")
|
||||
|
||||
testing.expect_value(t, b == true, true)
|
||||
testing.expect_value(t, b != true, false)
|
||||
testing.expect_value(t, b == false, false)
|
||||
testing.expect_value(t, b != false, true)
|
||||
|
||||
// both operands are normalised, not just the literal one
|
||||
testing.expect_value(t, b == c, true)
|
||||
testing.expect_value(t, b != c, false)
|
||||
testing.expect_value(t, b == f, false)
|
||||
testing.expect_value(t, b != f, true)
|
||||
|
||||
arm := 2
|
||||
switch b {
|
||||
case true: arm = 1
|
||||
case false: arm = 0
|
||||
}
|
||||
testing.expect_value(t, arm, 1)
|
||||
|
||||
// the wider boolean types share the path
|
||||
two16: u16 = 2
|
||||
two32: u32 = 2
|
||||
two64: u64 = 2
|
||||
testing.expect_value(t, transmute(b8)two == true, true)
|
||||
testing.expect_value(t, transmute(b16)two16 == true, true)
|
||||
testing.expect_value(t, transmute(b32)two32 == true, true)
|
||||
testing.expect_value(t, transmute(b64)two64 == true, true)
|
||||
|
||||
// the normal 0/1 payloads keep behaving
|
||||
yes := true
|
||||
no := false
|
||||
testing.expect_value(t, yes == true, true)
|
||||
testing.expect_value(t, yes == false, false)
|
||||
testing.expect_value(t, no == false, true)
|
||||
testing.expect_value(t, yes == no, false)
|
||||
testing.expect_value(t, yes != no, true)
|
||||
}
|
||||
Reference in New Issue
Block a user