Browse Source

Fix issue #1592 "LLVM code gen error when using a constant in an if"
Changes lb_build_if_stmt() to return null lbValue if condition is
cmpAnd, cmpOr or non-const neg and check in lb_build_if_stmt()
to avoid short circuiting if that's the case
Adds test to "tests/issues" and adds step in CI to check this dir

gitlost 3 years ago
parent
commit
fdbbf24271

+ 12 - 0
.github/workflows/ci.yml

@@ -38,6 +38,9 @@ jobs:
           cd tests/vendor
           make
         timeout-minutes: 10
+      - name: Odin issues tests
+        run: ./odin run tests/issues -collection:tests=tests
+        timeout-minutes: 10
       - name: Odin check examples/all for Linux i386
         run: ./odin check examples/all -vet -strict-style -target:linux_i386
         timeout-minutes: 10
@@ -87,6 +90,9 @@ jobs:
           cd tests/vendor
           make
         timeout-minutes: 10
+      - name: Odin issues tests
+        run: ./odin run tests/issues -collection:tests=tests
+        timeout-minutes: 10
       - name: Odin check examples/all for Darwin arm64
         run: ./odin check examples/all -vet -strict-style -target:darwin_arm64
         timeout-minutes: 10
@@ -153,6 +159,12 @@ jobs:
           cd tests\core\math\big
           call build.bat
         timeout-minutes: 10
+      - name: Odin issues tests
+        shell: cmd
+        run: |
+          call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat
+          odin run tests\issues -collection:tests=tests
+        timeout-minutes: 10
       - name: Odin check examples/all for Windows 32bits
         shell: cmd
         run: |

+ 2 - 2
src/llvm_backend_expr.cpp

@@ -3028,7 +3028,7 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
 		lbBlock *done  = lb_create_block(p, "if.done"); // NOTE(bill): Append later
 		lbBlock *else_ = lb_create_block(p, "if.else");
 
-		lbValue cond = lb_build_cond(p, te->cond, then, else_);
+		lb_build_cond(p, te->cond, then, else_);
 		lb_start_block(p, then);
 
 		Type *type = default_type(type_of_expr(expr));
@@ -4646,7 +4646,7 @@ lbAddr lb_build_addr(lbProcedure *p, Ast *expr) {
 		lbBlock *done  = lb_create_block(p, "if.done"); // NOTE(bill): Append later
 		lbBlock *else_ = lb_create_block(p, "if.else");
 
-		lbValue cond = lb_build_cond(p, te->cond, then, else_);
+		lb_build_cond(p, te->cond, then, else_);
 		lb_start_block(p, then);
 
 		Type *ptr_type = alloc_type_pointer(default_type(type_of_expr(expr)));

+ 12 - 3
src/llvm_backend_general.cpp

@@ -2602,6 +2602,9 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
 	GB_ASSERT(true_block  != nullptr);
 	GB_ASSERT(false_block != nullptr);
 
+	// Use to signal not to do compile time short circuit for consts
+	lbValue no_comptime_short_circuit = {};
+
 	switch (cond->kind) {
 	case_ast_node(pe, ParenExpr, cond);
 		return lb_build_cond(p, pe->expr, true_block, false_block);
@@ -2609,7 +2612,11 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
 
 	case_ast_node(ue, UnaryExpr, cond);
 		if (ue->op.kind == Token_Not) {
-			return lb_build_cond(p, ue->expr, false_block, true_block);
+			lbValue cond_val = lb_build_cond(p, ue->expr, false_block, true_block);
+			if (cond_val.value && LLVMIsConstant(cond_val.value)) {
+				return lb_const_bool(p->module, cond_val.type, LLVMConstIntGetZExtValue(cond_val.value) == 0);
+			}
+			return no_comptime_short_circuit;
 		}
 	case_end;
 
@@ -2618,12 +2625,14 @@ lbValue lb_build_cond(lbProcedure *p, Ast *cond, lbBlock *true_block, lbBlock *f
 			lbBlock *block = lb_create_block(p, "cmp.and");
 			lb_build_cond(p, be->left, block, false_block);
 			lb_start_block(p, block);
-			return lb_build_cond(p, be->right, true_block, false_block);
+			lb_build_cond(p, be->right, true_block, false_block);
+			return no_comptime_short_circuit;
 		} else if (be->op.kind == Token_CmpOr) {
 			lbBlock *block = lb_create_block(p, "cmp.or");
 			lb_build_cond(p, be->left, true_block, block);
 			lb_start_block(p, block);
-			return lb_build_cond(p, be->right, true_block, false_block);
+			lb_build_cond(p, be->right, true_block, false_block);
+			return no_comptime_short_circuit;
 		}
 	case_end;
 	}

+ 4 - 1
src/llvm_backend_stmt.cpp

@@ -1652,13 +1652,16 @@ void lb_build_if_stmt(lbProcedure *p, Ast *node) {
 	}
 
 	lbValue cond = lb_build_cond(p, is->cond, then, else_);
+	// Note `cond.value` only set for non-and/or conditions and const negs so that the `LLVMIsConstant()`
+	// and `LLVMConstIntGetZExtValue()` calls below will be valid and `LLVMInstructionEraseFromParent()`
+	// will target the correct (& only) branch statement
 
 	if (is->label != nullptr) {
 		lbTargetList *tl = lb_push_target_list(p, is->label, done, nullptr, nullptr);
 		tl->is_block = true;
 	}
 
-	if (LLVMIsConstant(cond.value)) {
+	if (cond.value && LLVMIsConstant(cond.value)) {
 		// NOTE(bill): Do a compile time short circuit for when the condition is constantly known.
 		// This done manually rather than relying on the SSA passes because sometimes the SSA passes
 		// miss some even if they are constantly known, especially with few optimization passes.

+ 489 - 0
tests/issues/test_issue_1592.odin

@@ -0,0 +1,489 @@
+// Tests issue #1592 https://github.com/odin-lang/Odin/issues/1592
+package test_issues
+
+import "core:fmt"
+import "core:testing"
+import tc "tests:common"
+
+main :: proc() {
+	t := testing.T{}
+
+	/* This won't short-circuit */
+	test_orig()
+
+	/* These will short-circuit */
+	test_simple_const_false(&t)
+	test_simple_const_true(&t)
+
+	/* These won't short-circuit */
+	test_simple_proc_false(&t)
+	test_simple_proc_true(&t)
+
+	/* These won't short-circuit */
+	test_const_false_const_false(&t)
+	test_const_false_const_true(&t)
+	test_const_true_const_false(&t)
+	test_const_true_const_true(&t)
+
+	/* These won't short-circuit */
+	test_proc_false_const_false(&t)
+	test_proc_false_const_true(&t)
+	test_proc_true_const_false(&t)
+	test_proc_true_const_true(&t)
+
+	tc.report(&t)
+}
+
+/* Original issue #1592 example */
+
+// I get a LLVM code gen error when this constant is false, but it works when it is true
+CONSTANT_BOOL :: false
+
+bool_result :: proc() -> bool {
+	return false
+}
+
+@test
+test_orig :: proc() {
+	if bool_result() || CONSTANT_BOOL {
+	}
+}
+
+CONSTANT_FALSE :: false
+CONSTANT_TRUE :: true
+
+false_result :: proc() -> bool {
+	return false
+}
+true_result :: proc() -> bool {
+	return true
+}
+
+@test
+test_simple_const_false :: proc(t: ^testing.T) {
+	if CONSTANT_FALSE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if (CONSTANT_FALSE) {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if !CONSTANT_FALSE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if (!CONSTANT_FALSE) {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if !(CONSTANT_FALSE) {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if !!CONSTANT_FALSE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if CONSTANT_FALSE == true {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if CONSTANT_FALSE == false {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if !(CONSTANT_FALSE == true) {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if !(CONSTANT_FALSE == false) {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+}
+
+@test
+test_simple_const_true :: proc(t: ^testing.T) {
+	if CONSTANT_TRUE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if (CONSTANT_TRUE) {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if !CONSTANT_TRUE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if (!CONSTANT_TRUE) {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if (!CONSTANT_TRUE) {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if !(CONSTANT_TRUE) {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if !!CONSTANT_TRUE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if CONSTANT_TRUE == true {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if CONSTANT_TRUE == false {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if !(CONSTANT_TRUE == true) {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if !(CONSTANT_TRUE == false) {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+}
+
+@test
+test_simple_proc_false :: proc(t: ^testing.T) {
+	if false_result() {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if !false_result() {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+}
+
+@test
+test_simple_proc_true :: proc(t: ^testing.T) {
+	if true_result() {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if !true_result() {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+}
+
+@test
+test_const_false_const_false :: proc(t: ^testing.T) {
+	if CONSTANT_FALSE || CONSTANT_FALSE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if CONSTANT_FALSE && CONSTANT_FALSE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+
+	if !CONSTANT_FALSE || CONSTANT_FALSE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if !CONSTANT_FALSE && CONSTANT_FALSE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+
+	if CONSTANT_FALSE || !CONSTANT_FALSE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if CONSTANT_FALSE && !CONSTANT_FALSE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+
+	if !(CONSTANT_FALSE || CONSTANT_FALSE) {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if !(CONSTANT_FALSE && CONSTANT_FALSE) {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+}
+
+@test
+test_const_false_const_true :: proc(t: ^testing.T) {
+	if CONSTANT_FALSE || CONSTANT_TRUE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if CONSTANT_FALSE && CONSTANT_TRUE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+
+	if !CONSTANT_FALSE || CONSTANT_TRUE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if !CONSTANT_FALSE && CONSTANT_TRUE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+
+	if CONSTANT_FALSE || !CONSTANT_TRUE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if CONSTANT_FALSE && !CONSTANT_TRUE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+
+	if !(CONSTANT_FALSE || CONSTANT_TRUE) {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if !(CONSTANT_FALSE && CONSTANT_TRUE) {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+}
+
+@test
+test_const_true_const_false :: proc(t: ^testing.T) {
+	if CONSTANT_TRUE || CONSTANT_FALSE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if CONSTANT_TRUE && CONSTANT_FALSE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+
+	if !CONSTANT_TRUE || CONSTANT_FALSE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if !CONSTANT_TRUE && CONSTANT_FALSE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+
+	if CONSTANT_TRUE || !CONSTANT_FALSE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if CONSTANT_TRUE && !CONSTANT_FALSE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+
+	if !(CONSTANT_TRUE || CONSTANT_FALSE) {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if !(CONSTANT_TRUE && CONSTANT_FALSE) {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+}
+
+@test
+test_const_true_const_true :: proc(t: ^testing.T) {
+	if CONSTANT_TRUE || CONSTANT_TRUE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if CONSTANT_TRUE && CONSTANT_TRUE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+
+	if !CONSTANT_TRUE || CONSTANT_TRUE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if !CONSTANT_TRUE && CONSTANT_TRUE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+
+	if CONSTANT_TRUE || !CONSTANT_TRUE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if CONSTANT_TRUE && !CONSTANT_TRUE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+
+	if !(CONSTANT_TRUE || CONSTANT_TRUE) {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if !(CONSTANT_TRUE && CONSTANT_TRUE) {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+}
+
+@test
+test_proc_false_const_false :: proc(t: ^testing.T) {
+	if false_result() || CONSTANT_FALSE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if false_result() && CONSTANT_FALSE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+
+	if !(false_result() || CONSTANT_FALSE) {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if !(false_result() && CONSTANT_FALSE) {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+}
+
+@test
+test_proc_false_const_true :: proc(t: ^testing.T) {
+	if false_result() || CONSTANT_TRUE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if false_result() && CONSTANT_TRUE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+
+	if !(false_result() || CONSTANT_TRUE) {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if !(false_result() && CONSTANT_TRUE) {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+}
+
+@test
+test_proc_true_const_false :: proc(t: ^testing.T) {
+	if true_result() || CONSTANT_FALSE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if true_result() && CONSTANT_FALSE {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+
+	if !(true_result() || CONSTANT_FALSE) {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if !(true_result() && CONSTANT_FALSE) {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+}
+
+@test
+test_proc_true_const_true :: proc(t: ^testing.T) {
+	if true_result() || CONSTANT_TRUE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+	if true_result() && CONSTANT_TRUE {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	} else {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	}
+
+	if !(true_result() || CONSTANT_TRUE) {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+	if !(true_result() && CONSTANT_TRUE) {
+		tc.expect(t, false, fmt.tprintf("%s: !false\n", #procedure))
+	} else {
+		tc.expect(t, true, fmt.tprintf("%s: !true\n", #procedure))
+	}
+}