mirror of
https://github.com/odin-lang/Odin.git
synced 2026-10-31 08:49:09 -04:00
Unify the types of constant arguments to min, max and clamp
When every argument was constant, the result took the value and type of the selected argument without checking the arguments against each other, so mismatched constants like `min(42, "string_literal")`, `min(i32(1), u8(2))` and `clamp(1.5, 42, "string_test")` were accepted, and `min(1, 2.5)` was an `int`. Unify the argument types before the constant and non-constant paths, as the non-constant path already did. Fixes #7975 Fixes #7976
This commit is contained in:
6 files changed
+143
-116
No files matched your search
+99
-116
@@ -4572,61 +4572,56 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
}
|
||||
}
|
||||
|
||||
for_array(i, operands) {
|
||||
Operand *a = &operands[i];
|
||||
for_array(j, operands) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
Operand *b = &operands[j];
|
||||
|
||||
convert_to_typed(c, a, b->type);
|
||||
if (a->mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
convert_to_typed(c, b, a->type);
|
||||
if (b->mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (isize i = 0; i < operands.count-1; i++) {
|
||||
Operand *a = &operands[i];
|
||||
Operand *b = &operands[i+1];
|
||||
|
||||
if (!are_types_identical(a->type, b->type)) {
|
||||
gbString type_a = type_to_string(a->type);
|
||||
gbString type_b = type_to_string(b->type);
|
||||
error(a->expr,
|
||||
"Mismatched types to 'min', '%s' vs '%s'",
|
||||
type_a, type_b);
|
||||
gb_string_free(type_b);
|
||||
gb_string_free(type_a);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_constant) {
|
||||
ExactValue value = operands[0].value;
|
||||
Type *type = operands[0].type;
|
||||
for (isize i = 1; i < operands.count; i++) {
|
||||
Operand y = operands[i];
|
||||
if (compare_exact_values(Token_Lt, value, y.value)) {
|
||||
// okay
|
||||
} else {
|
||||
value = y.value;
|
||||
type = y.type;
|
||||
}
|
||||
}
|
||||
operand->value = value;
|
||||
operand->type = type;
|
||||
} else {
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = original_type;
|
||||
|
||||
for_array(i, operands) {
|
||||
Operand *a = &operands[i];
|
||||
for_array(j, operands) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
Operand *b = &operands[j];
|
||||
|
||||
convert_to_typed(c, a, b->type);
|
||||
if (a->mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
convert_to_typed(c, b, a->type);
|
||||
if (b->mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (isize i = 0; i < operands.count-1; i++) {
|
||||
Operand *a = &operands[i];
|
||||
Operand *b = &operands[i+1];
|
||||
|
||||
if (!are_types_identical(a->type, b->type)) {
|
||||
gbString type_a = type_to_string(a->type);
|
||||
gbString type_b = type_to_string(b->type);
|
||||
error(a->expr,
|
||||
"Mismatched types to 'min', '%s' vs '%s'",
|
||||
type_a, type_b);
|
||||
gb_string_free(type_b);
|
||||
gb_string_free(type_a);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
operand->type = operands[0].type;
|
||||
}
|
||||
operand->type = operands[0].type;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4769,61 +4764,56 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
}
|
||||
}
|
||||
|
||||
for_array(i, operands) {
|
||||
Operand *a = &operands[i];
|
||||
for_array(j, operands) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
Operand *b = &operands[j];
|
||||
|
||||
convert_to_typed(c, a, b->type);
|
||||
if (a->mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
convert_to_typed(c, b, a->type);
|
||||
if (b->mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (isize i = 0; i < operands.count-1; i++) {
|
||||
Operand *a = &operands[i];
|
||||
Operand *b = &operands[i+1];
|
||||
|
||||
if (!are_types_identical(a->type, b->type)) {
|
||||
gbString type_a = type_to_string(a->type);
|
||||
gbString type_b = type_to_string(b->type);
|
||||
error(a->expr,
|
||||
"Mismatched types to 'max', '%s' vs '%s'",
|
||||
type_a, type_b);
|
||||
gb_string_free(type_b);
|
||||
gb_string_free(type_a);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (all_constant) {
|
||||
ExactValue value = operands[0].value;
|
||||
Type *type = operands[0].type;
|
||||
for (isize i = 1; i < operands.count; i++) {
|
||||
Operand y = operands[i];
|
||||
if (compare_exact_values(Token_Gt, value, y.value)) {
|
||||
// okay
|
||||
} else {
|
||||
type = y.type;
|
||||
value = y.value;
|
||||
}
|
||||
}
|
||||
operand->value = value;
|
||||
operand->type = type;
|
||||
} else {
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = original_type;
|
||||
|
||||
for_array(i, operands) {
|
||||
Operand *a = &operands[i];
|
||||
for_array(j, operands) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
Operand *b = &operands[j];
|
||||
|
||||
convert_to_typed(c, a, b->type);
|
||||
if (a->mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
convert_to_typed(c, b, a->type);
|
||||
if (b->mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (isize i = 0; i < operands.count-1; i++) {
|
||||
Operand *a = &operands[i];
|
||||
Operand *b = &operands[i+1];
|
||||
|
||||
if (!are_types_identical(a->type, b->type)) {
|
||||
gbString type_a = type_to_string(a->type);
|
||||
gbString type_b = type_to_string(b->type);
|
||||
error(a->expr,
|
||||
"Mismatched types to 'max', '%s' vs '%s'",
|
||||
type_a, type_b);
|
||||
gb_string_free(type_b);
|
||||
gb_string_free(type_a);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
operand->type = operands[0].type;
|
||||
}
|
||||
operand->type = operands[0].type;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -4932,7 +4922,32 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
}
|
||||
}
|
||||
|
||||
Type *type = x.type;
|
||||
for (isize i = 0; i < 3; i++) {
|
||||
Operand *a = ops[i];
|
||||
for (isize j = 0; j < 3; j++) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
Operand *b = ops[j];
|
||||
convert_to_typed(c, a, b->type);
|
||||
if (a->mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!are_types_identical(x.type, y.type) || !are_types_identical(x.type, z.type)) {
|
||||
gbString type_x = type_to_string(x.type);
|
||||
gbString type_y = type_to_string(y.type);
|
||||
gbString type_z = type_to_string(z.type);
|
||||
error(call,
|
||||
"Mismatched types to 'clamp', '%s', '%s', '%s'",
|
||||
type_x, type_y, type_z);
|
||||
gb_string_free(type_z);
|
||||
gb_string_free(type_y);
|
||||
gb_string_free(type_x);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (x.mode == Addressing_Constant &&
|
||||
y.mode == Addressing_Constant &&
|
||||
@@ -4944,47 +4959,15 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
operand->mode = Addressing_Constant;
|
||||
if (compare_exact_values(Token_Lt, a, b)) {
|
||||
operand->value = b;
|
||||
operand->type = y.type;
|
||||
} else if (compare_exact_values(Token_Gt, a, c)) {
|
||||
operand->value = c;
|
||||
operand->type = z.type;
|
||||
} else {
|
||||
operand->value = a;
|
||||
operand->type = x.type;
|
||||
}
|
||||
} else {
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = type;
|
||||
|
||||
for (isize i = 0; i < 3; i++) {
|
||||
Operand *a = ops[i];
|
||||
for (isize j = 0; j < 3; j++) {
|
||||
if (i == j) {
|
||||
continue;
|
||||
}
|
||||
Operand *b = ops[j];
|
||||
convert_to_typed(c, a, b->type);
|
||||
if (a->mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!are_types_identical(x.type, y.type) || !are_types_identical(x.type, z.type)) {
|
||||
gbString type_x = type_to_string(x.type);
|
||||
gbString type_y = type_to_string(y.type);
|
||||
gbString type_z = type_to_string(z.type);
|
||||
error(call,
|
||||
"Mismatched types to 'clamp', '%s', '%s', '%s'",
|
||||
type_x, type_y, type_z);
|
||||
gb_string_free(type_z);
|
||||
gb_string_free(type_y);
|
||||
gb_string_free(type_x);
|
||||
return false;
|
||||
}
|
||||
|
||||
operand->type = ops[0]->type;
|
||||
}
|
||||
operand->type = x.type;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ set ODIN=..\..\..\odin
|
||||
%ODIN% check "..\test_issue_7429" %COMMON% || exit /b
|
||||
%ODIN% check "..\test_issue_7708_mismatch" %COMMON% 2>&1 | find /c "Error:" | findstr /x "2" || exit /b
|
||||
%ODIN% check "..\test_issue_7938" -no-entry-point %COMMON% 2>&1 | find /c "Error: Missing return statement" | findstr /x "3" || exit /b
|
||||
%ODIN% check "..\test_issue_7975_7976_mismatch" %COMMON% 2>&1 | find /c "Error:" | findstr /x "8" || exit /b
|
||||
%ODIN% check "..\test_issue_ambiguous_union_literal" %COMMON% 2>&1 | find /c "Error:" | findstr /x "1" || exit /b
|
||||
%ODIN% check "..\test_issue_global_when_cycle" -no-entry-point %COMMON% 2>&1 | find /c "Contradictory global" | findstr /x "4" || exit /b
|
||||
%ODIN% check "..\test_issue_global_when_cycle_ambiguous" -no-entry-point %COMMON% 2>&1 | find /c "Ambiguous global" | findstr /x "1" || exit /b
|
||||
|
||||
@@ -144,6 +144,13 @@ else
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $($ODIN check ../test_issue_7975_7976_mismatch $COMMON_CHECK 2>&1 >/dev/null | grep -c "Error:") -eq 8 ]]; then
|
||||
echo "SUCCESSFUL 1/1"
|
||||
else
|
||||
echo "SUCCESSFUL 0/1"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $($ODIN check ../test_issue_poly_proc_value $COMMON_CHECK 2>&1 >/dev/null | grep -c "Error:") -eq 4 ]]; then
|
||||
echo "SUCCESSFUL 1/1"
|
||||
else
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
// Tests issues #7975 https://github.com/odin-lang/Odin/issues/7975 and #7976 https://github.com/odin-lang/Odin/issues/7976
|
||||
// Constant arguments to `min`, `max` and `clamp` are unified into a single type, as non-constant ones are
|
||||
package test_issue_7975_7976
|
||||
|
||||
import "core:testing"
|
||||
|
||||
@(test)
|
||||
test_constant_min_max_clamp :: proc(t: ^testing.T) {
|
||||
a := min(1, 2.5)
|
||||
b := max(3, 1.5)
|
||||
c := clamp(3, 0.5, 2)
|
||||
d := min("b", "string_literal")
|
||||
|
||||
testing.expect_value(t, typeid_of(type_of(a)), f64)
|
||||
testing.expect_value(t, typeid_of(type_of(b)), f64)
|
||||
testing.expect_value(t, typeid_of(type_of(c)), f64)
|
||||
testing.expect_value(t, a, 1.0)
|
||||
testing.expect_value(t, b, 3.0)
|
||||
testing.expect_value(t, c, 2.0)
|
||||
testing.expect_value(t, d, "b")
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Tests issues #7975 https://github.com/odin-lang/Odin/issues/7975 and #7976 https://github.com/odin-lang/Odin/issues/7976
|
||||
// Constant arguments to `min`, `max` and `clamp` which cannot be unified into a single type must be rejected
|
||||
package test_issue_7975_7976_mismatch
|
||||
|
||||
main :: proc() {
|
||||
_ = min(3.14, "string_literal")
|
||||
_ = max(42, "string_literal")
|
||||
_ = min('a', "b")
|
||||
_ = clamp(1.5, "string_test", 5.5)
|
||||
_ = clamp(1.5, 42, "string_test")
|
||||
_ = min(i32(1), u8(2))
|
||||
_ = max(3.5, i32(2))
|
||||
_ = clamp(i32(1), 2, u8(3))
|
||||
}
|
||||
@@ -52,6 +52,7 @@ package tests_issues
|
||||
@(require) import "test_issue_7853"
|
||||
@(require) import "test_issue_7875_7471"
|
||||
@(require) import "test_issue_7889"
|
||||
@(require) import "test_issue_7975_7976"
|
||||
@(require) import "test_issue_fast_isel_lowering"
|
||||
@(require) import "test_issue_omitted_field_union"
|
||||
@(require) import "test_issue_equal_proc_dependencies"
|
||||
|
||||
Reference in new issue
Block a user