Browse Source

Add `cstring` specific comparison procedures to fix comparisons like `cstring("") != cstring(nil)`

gingerBill 1 year ago
parent
commit
c08bf1204f
3 changed files with 77 additions and 4 deletions
  1. 42 0
      core/runtime/internal.odin
  2. 13 3
      src/check_expr.cpp
  3. 22 1
      src/llvm_backend_expr.cpp

+ 42 - 0
core/runtime/internal.odin

@@ -410,6 +410,48 @@ cstring_to_string :: proc "contextless" (s: cstring) -> string {
 }
 
 
+cstring_eq :: proc "contextless" (lhs, rhs: cstring) -> bool {
+	x := ([^]byte)(lhs)
+	y := ([^]byte)(rhs)
+	if x == y {
+		return true
+	}
+	if (x == nil) ~ (y == nil) {
+		return false
+	}
+	xn := cstring_len(lhs)
+	yn := cstring_len(rhs)
+	if xn != yn {
+		return false
+	}
+	return #force_inline memory_equal(x, y, xn)
+}
+
+cstring_cmp :: proc "contextless" (lhs, rhs: cstring) -> int {
+	x := ([^]byte)(lhs)
+	y := ([^]byte)(rhs)
+	if x == y {
+		return 0
+	}
+	if (x == nil) ~ (y == nil) {
+		return -1 if x == nil else +1
+	}
+	xn := cstring_len(lhs)
+	yn := cstring_len(rhs)
+	ret := memory_compare(x, y, min(xn, yn))
+	if ret == 0 && xn != yn {
+		return -1 if xn < yn else +1
+	}
+	return ret
+}
+
+cstring_ne :: #force_inline proc "contextless" (a, b: cstring) -> bool { return !cstring_eq(a, b) }
+cstring_lt :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) < 0 }
+cstring_gt :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) > 0 }
+cstring_le :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) <= 0 }
+cstring_ge :: #force_inline proc "contextless" (a, b: cstring) -> bool { return cstring_cmp(a, b) >= 0 }
+
+
 complex32_eq :: #force_inline proc "contextless"  (a, b: complex32)  -> bool { return real(a) == real(b) && imag(a) == imag(b) }
 complex32_ne :: #force_inline proc "contextless"  (a, b: complex32)  -> bool { return real(a) != real(b) || imag(a) != imag(b) }
 

+ 13 - 3
src/check_expr.cpp

@@ -2462,8 +2462,9 @@ gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t
 			add_package_dependency(c, "runtime", "quaternion256_ne");
 			break;
 		case Basic_cstring:
-			add_package_dependency(c, "runtime", "cstring_to_string");
-			/*fallthrough*/
+			add_package_dependency(c, "runtime", "cstring_eq");
+			add_package_dependency(c, "runtime", "cstring_ne");
+			break;
 		case Basic_string:
 			add_package_dependency(c, "runtime", "string_eq");
 			add_package_dependency(c, "runtime", "string_ne");
@@ -2621,7 +2622,16 @@ gb_internal void check_comparison(CheckerContext *c, Ast *node, Operand *x, Oper
 			if (!is_type_untyped(x->type)) size = gb_max(size, type_size_of(x->type));
 			if (!is_type_untyped(y->type)) size = gb_max(size, type_size_of(y->type));
 
-			if (is_type_string(x->type) || is_type_string(y->type)) {
+			if (is_type_cstring(x->type) && is_type_cstring(y->type)) {
+				switch (op) {
+				case Token_CmpEq: add_package_dependency(c, "runtime", "cstring_eq"); break;
+				case Token_NotEq: add_package_dependency(c, "runtime", "cstring_ne"); break;
+				case Token_Lt:    add_package_dependency(c, "runtime", "cstring_lt"); break;
+				case Token_Gt:    add_package_dependency(c, "runtime", "cstring_gt"); break;
+				case Token_LtEq:  add_package_dependency(c, "runtime", "cstring_le"); break;
+				case Token_GtEq:  add_package_dependency(c, "runtime", "cstring_gt"); break;
+				}
+			} else if (is_type_string(x->type) || is_type_string(y->type)) {
 				switch (op) {
 				case Token_CmpEq: add_package_dependency(c, "runtime", "string_eq"); break;
 				case Token_NotEq: add_package_dependency(c, "runtime", "string_ne"); break;

+ 22 - 1
src/llvm_backend_expr.cpp

@@ -2414,7 +2414,28 @@ gb_internal lbValue lb_emit_comp(lbProcedure *p, TokenKind op_kind, lbValue left
 	}
 
 	if (is_type_string(a)) {
-		if (is_type_cstring(a)) {
+		if (is_type_cstring(a) && is_type_cstring(b)) {
+			left  = lb_emit_conv(p, left, t_cstring);
+			right = lb_emit_conv(p, right, t_cstring);
+			char const *runtime_procedure = nullptr;
+			switch (op_kind) {
+			case Token_CmpEq: runtime_procedure = "cstring_eq"; break;
+			case Token_NotEq: runtime_procedure = "cstring_ne"; break;
+			case Token_Lt:    runtime_procedure = "cstring_lt"; break;
+			case Token_Gt:    runtime_procedure = "cstring_gt"; break;
+			case Token_LtEq:  runtime_procedure = "cstring_le"; break;
+			case Token_GtEq:  runtime_procedure = "cstring_gt"; break;
+			}
+			GB_ASSERT(runtime_procedure != nullptr);
+
+			auto args = array_make<lbValue>(permanent_allocator(), 2);
+			args[0] = left;
+			args[1] = right;
+			return lb_emit_runtime_call(p, runtime_procedure, args);
+		}
+
+
+		if (is_type_cstring(a) ^ is_type_cstring(b)) {
 			left  = lb_emit_conv(p, left, t_string);
 			right = lb_emit_conv(p, right, t_string);
 		}