disambiguate enum blocks and complete distinct type operations

This commit is contained in:
2026-08-01 23:57:35 +02:00
parent 91aa601464
commit b9526b5f06
34 changed files with 1128 additions and 1963359 deletions
+263 -93
View File
@@ -10,7 +10,6 @@ import "base:intrinsics"
import "core:fmt"
import "core:hash"
import "core:math"
import "core:mem"
import "core:strings"
COMPTIME_EVAL_QUOTA :: 100_000
@@ -79,6 +78,13 @@ current_comptime_type :: proc(checker: ^Checker, name: symbol.Id) -> (types.Type
if value, ok := current_comptime_value(checker, name); ok && value.kind == .Type {
return value.type, true
}
if binding, ok := current_static_binding(checker, name);
ok && binding.value != INVALID_CT_VALUE && int(binding.value) < len(checker.static_state.values) {
value := checker.static_state.values[binding.value]
if value.kind == .Type {
return types.Type(value.index), true
}
}
return types.INVALID, false
}
@@ -1089,16 +1095,21 @@ ct_materialize_value :: proc(
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
return build_constant_expr(checker, ast.Expr{span=span}, Constant{kind=.Value, value=value.integer}, value.type)
_, distinct_ok := types.distinct_scalar_backing(value.type, &checker.module.types)
return build_constant_expr(
checker, ast.Expr{span=span}, Constant{kind=.Value, value=value.integer},
value.type, distinct_ok,
)
case .Bool:
return add_hir_expr(checker, hir.Expr{
kind=.Bool, span=span, type=types.BOOL, integer=i64(value.integer),
kind=.Bool, span=span, type=value.type, integer=i64(value.integer),
target=hir.INVALID_REF, left=hir.INVALID_EXPR, right=hir.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Float:
bits := transmute(i64)value.float
if types.bits(value.type, checker.target) == 32 {
representation := types.runtime_representation(value.type, &checker.module.types)
if types.bits(representation, checker.target) == 32 {
bits = i64(transmute(u32)f32(value.float))
}
return add_hir_expr(checker, hir.Expr{
@@ -1251,6 +1262,7 @@ ct_eval_expr :: proc(
expr_id: ast.Expr_Id,
expected := types.INVALID,
depth := 0,
numeric_operation := false,
) -> (Ct_Value_Id, Ct_Flow, bool) {
checker := state.checker
if depth > 128 || expr_id == ast.INVALID_EXPR || int(expr_id) >= len(checker.ast_module.exprs) {
@@ -1263,6 +1275,22 @@ ct_eval_expr :: proc(
store := &checker.module.types
#partial switch expr.kind {
case .Integer:
if numeric_operation {
if representation, distinct_ok := types.distinct_scalar_backing(expected, store); distinct_ok {
if types.is_float(representation, checker.target) {
return ct_add_value(state, Ct_Value{
kind=.Float, type=expected, float=f64(expr.integer),
}), ct_flow(.Normal), true
}
value := i128(expr.integer)
if types.is_concrete_integer(representation) &&
fits_integer_type(value, representation, checker.target) {
return ct_add_value(state, Ct_Value{
kind=.Integer, type=expected, integer=value,
}), ct_flow(.Normal), true
}
}
}
value_type := expected if types.is_concrete_integer(expected) || types.is_enum(expected, store) else ct_default_integer_type(i128(expr.integer))
id := ct_add_value(state, Ct_Value{kind=.Integer, type=value_type, integer=i128(expr.integer)})
if types.is_valid(expected) {
@@ -1272,6 +1300,14 @@ ct_eval_expr :: proc(
case .Bool:
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=i128(expr.integer)}), ct_flow(.Normal), true
case .Float:
if numeric_operation {
if representation, distinct_ok := types.distinct_scalar_backing(expected, store);
distinct_ok && types.is_float(representation, checker.target) {
return ct_add_value(state, Ct_Value{
kind=.Float, type=expected, float=transmute(f64)expr.integer,
}), ct_flow(.Normal), true
}
}
value_type := expected if types.is_float(expected, checker.target) else types.F64
return ct_add_value(state, Ct_Value{kind=.Float, type=value_type, float=transmute(f64)expr.integer}), ct_flow(.Normal), true
case .String:
@@ -1416,10 +1452,17 @@ ct_eval_expr :: proc(
}
return ct_eval_field_value(state, base_id, expr.name, expr.span)
case .Index:
index_id, index_flow, index_ok := ct_eval_expr(state, expr.right, types.USIZE, depth+1)
index_id, index_flow, index_ok := ct_eval_expr(state, expr.right, types.INVALID, depth+1)
if !index_ok || index_flow.kind != .Normal {
return INVALID_CT_VALUE, index_flow, index_ok
}
index_type := state.values[index_id].type
index_representation := types.runtime_representation(index_type, store)
index_literal := is_numeric_constant_expr(checker, expr.right)
if !types.is_concrete_integer(index_representation) ||
!index_literal && !can_implicitly_convert_type(checker, index_representation, types.USIZE) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "comptime index must be coercible to usize")
}
index_value, index_is_int := ct_integer_value(state, index_id)
if !index_is_int || index_value < 0 || index_value > i128(0x7fff_ffff) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "comptime index must be a non-negative integer")
@@ -1541,17 +1584,12 @@ ct_eval_expr :: proc(
kind=.Range, type=types.range(store, child_type), start=start, count=2, active=i64(expr.integer),
}), ct_flow(.Normal), true
case .Negate, .Not, .Bit_Not:
value, flow, ok := ct_eval_expr(state, expr.left, expected, depth+1)
value, flow, ok := ct_eval_expr(state, expr.left, expected, depth+1, numeric_operation)
if !ok || flow.kind != .Normal {
return INVALID_CT_VALUE, flow, ok
}
return ct_eval_unary(state, expr.kind, value, expr.span)
case .Add, .Sub, .Mul, .Div, .Bit_And, .Bit_Or, .Bit_Xor, .Eq, .Ne, .Lt, .Le, .Gt, .Ge:
left_expected := expected if expr.kind == .Div && types.is_float(expected, checker.target) else types.INVALID
if (expr.kind == .Bit_And || expr.kind == .Bit_Or || expr.kind == .Bit_Xor) &&
types.is_concrete_integer(expected) {
left_expected = expected
}
left_expr := checker.ast_module.exprs[expr.left]
right_expr := checker.ast_module.exprs[expr.right]
if left_expr.kind == .Null && right_expr.kind != .Null &&
@@ -1566,14 +1604,39 @@ ct_eval_expr :: proc(
}
return ct_eval_binary(state, expr.kind, left, right, expr.span)
}
left, flow, ok := ct_eval_expr(state, expr.left, left_expected, depth+1)
left_const := is_numeric_constant_expr(checker, expr.left)
right_const := is_numeric_constant_expr(checker, expr.right)
left, right := INVALID_CT_VALUE, INVALID_CT_VALUE
flow := ct_flow(.Normal)
ok := false
if left_const && !right_const {
right, flow, ok = ct_eval_expr(state, expr.right, types.INVALID, depth+1)
if !ok || flow.kind != .Normal {
return INVALID_CT_VALUE, flow, ok
}
_, distinct_ok := types.distinct_scalar_backing(state.values[right].type, store)
left, flow, ok = ct_eval_expr(
state, expr.left, state.values[right].type, depth+1, distinct_ok,
)
} else {
left_expected := types.INVALID
if types.is_concrete_integer(expected) ||
expr.kind == .Div && types.is_float(expected, checker.target) {
left_expected = expected
}
left, flow, ok = ct_eval_expr(state, expr.left, left_expected, depth+1)
if !ok || flow.kind != .Normal {
return INVALID_CT_VALUE, flow, ok
}
right_expected := state.values[left].type
_, distinct_ok := types.distinct_scalar_backing(right_expected, store)
right, flow, ok = ct_eval_expr(
state, expr.right, right_expected, depth+1, distinct_ok && right_const,
)
}
if !ok || flow.kind != .Normal {
return INVALID_CT_VALUE, flow, ok
}
right, right_flow, right_ok := ct_eval_expr(state, expr.right, state.values[left].type, depth+1)
if !right_ok || right_flow.kind != .Normal {
return INVALID_CT_VALUE, right_flow, right_ok
}
return ct_eval_binary(state, expr.kind, left, right, expr.span)
case .Shift_Left, .Shift_Right, .Shift_Left_Saturating:
left, flow, ok := ct_eval_expr(state, expr.left, expected, depth+1)
@@ -1899,10 +1962,17 @@ ct_eval_slice_expr :: proc(state: ^Ct_State, expr: ast.Expr, depth: int) -> (Ct_
if bound == ast.INVALID_EXPR {
continue
}
value, bound_flow, bound_ok := ct_eval_expr(state, bound, types.USIZE, depth+1)
value, bound_flow, bound_ok := ct_eval_expr(state, bound, types.INVALID, depth+1)
if !bound_ok || bound_flow.kind != .Normal {
return INVALID_CT_VALUE, bound_flow, bound_ok
}
bound_type := state.values[value].type
bound_representation := types.runtime_representation(bound_type, store)
bound_literal := is_numeric_constant_expr(checker, bound)
if !types.is_concrete_integer(bound_representation) ||
!bound_literal && !can_implicitly_convert_type(checker, bound_representation, types.USIZE) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "slice bounds must be coercible to usize")
}
integer, integer_ok := ct_integer_value(state, value)
if !integer_ok || integer < 0 || integer > i128(0x7fff_ffff) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "slice bounds must be non-negative integers")
@@ -2356,10 +2426,11 @@ ct_unwrap_optional :: proc(state: ^Ct_State, id: Ct_Value_Id, span: source.Span)
}
ct_normalize_integer :: proc(state: ^Ct_State, value: i128, type: types.Type) -> i128 {
bits := types.bits(type, state.checker.target)
representation := types.runtime_representation(type, &state.checker.module.types)
bits := types.bits(representation, state.checker.target)
mask := (i128(1) << u32(bits))-1
raw := value & mask
if types.is_signed(type, state.checker.target) {
if types.is_signed(representation, state.checker.target) {
sign := i128(1) << u32(bits-1)
if raw & sign != 0 {
return raw-(i128(1) << u32(bits))
@@ -2380,13 +2451,15 @@ ct_eval_unary :: proc(state: ^Ct_State, op: ast.Expr_Kind, id: Ct_Value_Id, span
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if value.integer == 0 else 0}), ct_flow(.Normal), true
}
if op == .Bit_Not {
if value.kind != .Integer || !types.is_concrete_integer(value.type) {
representation := types.runtime_representation(value.type, &state.checker.module.types)
if value.kind != .Integer || !types.is_concrete_integer(representation) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "'~' requires a concrete integer operand")
}
value.integer = ct_normalize_integer(state, ~value.integer, value.type)
return ct_add_value(state, value), ct_flow(.Normal), true
}
if value.kind == .Integer {
representation := types.runtime_representation(value.type, &state.checker.module.types)
if value.kind == .Integer && types.is_signed(representation, state.checker.target) {
result, overflow := intrinsics.overflow_sub(i128(0), value.integer)
if overflow {
state.error = .Overflow
@@ -2395,7 +2468,7 @@ ct_eval_unary :: proc(state: ^Ct_State, op: ast.Expr_Kind, id: Ct_Value_Id, span
value.integer = result
return ct_add_value(state, value), ct_flow(.Normal), true
}
if value.kind == .Float {
if value.kind == .Float && types.is_float(representation, state.checker.target) {
value.float = -value.float
return ct_add_value(state, value), ct_flow(.Normal), true
}
@@ -2409,10 +2482,11 @@ ct_eval_binary :: proc(state: ^Ct_State, op: ast.Expr_Kind, left_id, right_id: C
}
left := state.values[left_id]
right := state.values[right_id]
store := &state.checker.module.types
is_compare := op == .Eq || op == .Ne || op == .Lt || op == .Le || op == .Gt || op == .Ge
if left.kind == .Null || right.kind == .Null {
if (op != .Eq && op != .Ne) ||
!types.is_optional(left.type, &state.checker.module.types) ||
!types.is_optional(left.type, store) ||
!types.equal(left.type, right.type) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "'null' only supports '==' and '!=' with an optional value")
}
@@ -2426,36 +2500,43 @@ ct_eval_binary :: proc(state: ^Ct_State, op: ast.Expr_Kind, left_id, right_id: C
if left.kind != .Type || right.kind != .Type || (op != .Eq && op != .Ne) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "type values only support '==' and '!=' with another type")
}
ok := types.equal(types.Type(left.index), types.Type(right.index))
equal := types.equal(types.Type(left.index), types.Type(right.index))
if op == .Ne {
ok = !ok
equal = !equal
}
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if ok else 0}), ct_flow(.Normal), true
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if equal else 0}), ct_flow(.Normal), true
}
if left.kind == .Bool && right.kind == .Bool {
if op != .Eq && op != .Ne {
if left.kind == .Bool || right.kind == .Bool {
operation, compatible := numeric_operation_type(state.checker, left.type, right.type)
if left.kind != .Bool || right.kind != .Bool || !compatible ||
!types.is_bool(operation.representation) || (op != .Eq && op != .Ne) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "bool values only support '==' and '!='")
}
ok := left.integer == right.integer
equal := left.integer == right.integer
if op == .Ne {
ok = !ok
equal = !equal
}
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if ok else 0}), ct_flow(.Normal), true
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if equal else 0}), ct_flow(.Normal), true
}
if left.kind == .Float || right.kind == .Float {
operation, compatible := numeric_operation_type(state.checker, left.type, right.type)
distinct_operation := types.is_distinct(left.type, store) || types.is_distinct(right.type, store)
if distinct_operation && (!compatible || !types.is_float(operation.representation, state.checker.target)) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "comptime binary expression requires compatible operands")
}
lf := left.float if left.kind == .Float else f64(left.integer)
rf := right.float if right.kind == .Float else f64(right.integer)
if is_compare {
ok := false
result := false
#partial switch op {
case .Eq: ok = lf == rf
case .Ne: ok = lf != rf
case .Lt: ok = lf < rf
case .Le: ok = lf <= rf
case .Gt: ok = lf > rf
case .Ge: ok = lf >= rf
case .Eq: result = lf == rf
case .Ne: result = lf != rf
case .Lt: result = lf < rf
case .Le: result = lf <= rf
case .Gt: result = lf > rf
case .Ge: result = lf >= rf
}
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if ok else 0}), ct_flow(.Normal), true
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if result else 0}), ct_flow(.Normal), true
}
result := lf
#partial switch op {
@@ -2463,57 +2544,46 @@ ct_eval_binary :: proc(state: ^Ct_State, op: ast.Expr_Kind, left_id, right_id: C
case .Sub: result = lf - rf
case .Mul: result = lf * rf
case .Div: result = lf / rf
case:
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "comptime binary expression requires compatible operands")
}
result_type := types.widest(left.type, right.type)
if !types.is_float(result_type, state.checker.target) {
result_type = types.F64
result_type := operation.result
if !distinct_operation {
result_type = types.widest(left.type, right.type)
if !types.is_float(result_type, state.checker.target) {
result_type = types.F64
}
}
if types.bits(result_type, state.checker.target) == 32 {
result = f64(f32(result))
}
return ct_add_value(state, Ct_Value{kind=.Float, type=result_type, float=result}), ct_flow(.Normal), true
}
if ct_is_integer_like(left) && ct_is_integer_like(right) {
if is_compare {
ok := false
#partial switch op {
case .Eq: ok = left.integer == right.integer
case .Ne: ok = left.integer != right.integer
case .Lt: ok = left.integer < right.integer
case .Le: ok = left.integer <= right.integer
case .Gt: ok = left.integer > right.integer
case .Ge: ok = left.integer >= right.integer
if types.is_enum(left.type, store) || types.is_enum(right.type, store) {
if !types.equal(left.type, right.type) || (op != .Eq && op != .Ne) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "enum values only support '==' and '!=' with the same enum type")
}
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if ok else 0}), ct_flow(.Normal), true
}
if op == .Div {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(
state, .Integer_Division, span,
"integer '/' is not allowed; use divtrunc!, divfloor!, divexact!, or divceil!",
)
}
if op == .Bit_And || op == .Bit_Or || op == .Bit_Xor {
result_type := types.widest(left.type, right.type)
if !types.is_concrete_integer(result_type) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "bitwise operation requires compatible concrete integer operands")
equal := left.integer == right.integer
if op == .Ne {
equal = !equal
}
value := left.integer & right.integer
#partial switch op {
case .Bit_Or: value = left.integer | right.integer
case .Bit_Xor: value = left.integer ~ right.integer
}
value = ct_normalize_integer(state, value, result_type)
return ct_add_value(state, Ct_Value{kind=.Integer, type=result_type, integer=value}), ct_flow(.Normal), true
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if equal else 0}), ct_flow(.Normal), true
}
if op == .Shift_Left || op == .Shift_Right || op == .Shift_Left_Saturating {
if !types.is_concrete_integer(left.type) || !types.is_unsigned(right.type, state.checker.target) || right.integer < 0 {
left_representation := types.runtime_representation(left.type, store)
if !types.is_concrete_integer(left_representation) ||
!types.is_unsigned(right.type, state.checker.target) || right.integer < 0 {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "shift requires a concrete integer value and unsigned integer count")
}
bits := types.bits(left.type, state.checker.target)
bits := types.bits(left_representation, state.checker.target)
if right.integer >= i128(bits) {
if op != .Shift_Left_Saturating {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "shift count exceeds integer width")
}
endpoint := i128(0)
if left.integer != 0 {
if types.is_signed(left.type, state.checker.target) {
if types.is_signed(left_representation, state.checker.target) {
endpoint = -(i128(1) << u32(bits-1)) if left.integer < 0 else (i128(1) << u32(bits-1))-1
} else {
endpoint = (i128(1) << u32(bits))-1
@@ -2524,7 +2594,7 @@ ct_eval_binary :: proc(state: ^Ct_State, op: ast.Expr_Kind, left_id, right_id: C
count := u32(right.integer)
if op == .Shift_Right {
value := left.integer >> count
if !types.is_signed(left.type, state.checker.target) {
if !types.is_signed(left_representation, state.checker.target) {
value = ct_normalize_integer(state, left.integer, left.type) >> count
}
return ct_add_value(state, Ct_Value{kind=.Integer, type=left.type, integer=value}), ct_flow(.Normal), true
@@ -2532,7 +2602,7 @@ ct_eval_binary :: proc(state: ^Ct_State, op: ast.Expr_Kind, left_id, right_id: C
if op == .Shift_Left_Saturating {
factor := i128(1) << count
value := left.integer*factor
if types.is_signed(left.type, state.checker.target) {
if types.is_signed(left_representation, state.checker.target) {
minimum := -(i128(1) << u32(bits-1))
maximum := (i128(1) << u32(bits-1))-1
value = max(minimum, min(maximum, value))
@@ -2545,6 +2615,42 @@ ct_eval_binary :: proc(state: ^Ct_State, op: ast.Expr_Kind, left_id, right_id: C
value := ct_normalize_integer(state, ct_normalize_integer(state, left.integer, left.type) << count, left.type)
return ct_add_value(state, Ct_Value{kind=.Integer, type=left.type, integer=value}), ct_flow(.Normal), true
}
operation, compatible := numeric_operation_type(state.checker, left.type, right.type)
distinct_operation := types.is_distinct(left.type, store) || types.is_distinct(right.type, store)
if distinct_operation && (!compatible || !types.is_concrete_integer(operation.representation)) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "comptime binary expression requires compatible operands")
}
if is_compare {
result := false
#partial switch op {
case .Eq: result = left.integer == right.integer
case .Ne: result = left.integer != right.integer
case .Lt: result = left.integer < right.integer
case .Le: result = left.integer <= right.integer
case .Gt: result = left.integer > right.integer
case .Ge: result = left.integer >= right.integer
}
return ct_add_value(state, Ct_Value{kind=.Bool, type=types.BOOL, integer=1 if result else 0}), ct_flow(.Normal), true
}
if op == .Div {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(
state, .Integer_Division, span,
"integer '/' is not allowed; use divtrunc!, divfloor!, divexact!, or divceil!",
)
}
if op == .Bit_And || op == .Bit_Or || op == .Bit_Xor {
result_type := operation.result if distinct_operation else types.widest(left.type, right.type)
if !types.is_concrete_integer(types.runtime_representation(result_type, store)) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "bitwise operation requires compatible concrete integer operands")
}
value := left.integer & right.integer
#partial switch op {
case .Bit_Or: value = left.integer | right.integer
case .Bit_Xor: value = left.integer ~ right.integer
}
value = ct_normalize_integer(state, value, result_type)
return ct_add_value(state, Ct_Value{kind=.Integer, type=result_type, integer=value}), ct_flow(.Normal), true
}
value: i128
overflow := false
#partial switch op {
@@ -2559,9 +2665,12 @@ ct_eval_binary :: proc(state: ^Ct_State, op: ast.Expr_Kind, left_id, right_id: C
state.error = .Overflow
return INVALID_CT_VALUE, ct_flow(.Normal), false
}
result_type := types.widest(left.type, right.type)
if !types.is_concrete_integer(result_type) && !types.is_enum(result_type, &state.checker.module.types) {
result_type = ct_default_integer_type(value)
result_type := operation.result
if !distinct_operation {
result_type = types.widest(left.type, right.type)
if !types.is_concrete_integer(result_type) {
result_type = ct_default_integer_type(value)
}
}
return ct_add_value(state, Ct_Value{kind=.Integer, type=result_type, integer=value}), ct_flow(.Normal), true
}
@@ -2580,14 +2689,14 @@ ct_eval_division_builtin :: proc(
}
left := state.values[left_id]
right := state.values[right_id]
result_type := types.widest(left.type, right.type)
if !types.is_concrete_scalar(result_type) || types.is_bool(result_type) {
operation, compatible := numeric_operation_type(state.checker, left.type, right.type)
if !compatible || types.is_bool(operation.representation) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(
state, .Not_Comptime, span, "division builtins require compatible numeric operands",
)
}
left_id, left_ok := ct_coerce_value(state, left_id, result_type, span)
right_id, right_ok := ct_coerce_value(state, right_id, result_type, span)
left_id, left_ok := ct_coerce_value(state, left_id, operation.result, span)
right_id, right_ok := ct_coerce_value(state, right_id, operation.result, span)
if !left_ok || !right_ok {
return INVALID_CT_VALUE, ct_flow(.Normal), false
}
@@ -2614,10 +2723,10 @@ ct_eval_division_builtin :: proc(
result += right.float
}
}
if types.bits(result_type, state.checker.target) == 32 {
if types.bits(operation.representation, state.checker.target) == 32 {
result = f64(f32(result))
}
return ct_add_value(state, Ct_Value{kind=.Float, type=result_type, float=result}), ct_flow(.Normal), true
return ct_add_value(state, Ct_Value{kind=.Float, type=operation.result, float=result}), ct_flow(.Normal), true
}
if left.kind != .Integer || right.kind != .Integer {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "division builtins require compatible numeric operands")
@@ -2626,8 +2735,8 @@ ct_eval_division_builtin :: proc(
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Div_By_Zero, span, "division builtin denominator is zero")
}
is_quotient := kind == .Trunc || kind == .Floor || kind == .Exact || kind == .Ceil
if is_quotient && types.is_signed(result_type, state.checker.target) {
minimum := -(i128(1) << u32(types.bits(result_type, state.checker.target)-1))
if is_quotient && types.is_signed(operation.representation, state.checker.target) {
minimum := -(i128(1) << u32(types.bits(operation.representation, state.checker.target)-1))
if left.integer == minimum && right.integer == -1 {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Overflow, span, "signed integer division overflow")
}
@@ -2656,7 +2765,7 @@ ct_eval_division_builtin :: proc(
}
case:
}
return ct_add_value(state, Ct_Value{kind=.Integer, type=result_type, integer=result}), ct_flow(.Normal), true
return ct_add_value(state, Ct_Value{kind=.Integer, type=operation.result, integer=result}), ct_flow(.Normal), true
}
ct_eval_division_call :: proc(
@@ -2684,14 +2793,20 @@ ct_eval_division_call :: proc(
if !ok || flow.kind != .Normal {
return INVALID_CT_VALUE, flow, ok
}
left, flow, ok = ct_eval_expr(state, expr.args[0], state.values[right].type, depth+1)
_, distinct_ok := types.distinct_scalar_backing(state.values[right].type, &checker.module.types)
left, flow, ok = ct_eval_expr(
state, expr.args[0], state.values[right].type, depth+1, distinct_ok,
)
} else {
left, flow, ok = ct_eval_expr(state, expr.args[0], hint, depth+1)
if !ok || flow.kind != .Normal {
return INVALID_CT_VALUE, flow, ok
}
right_hint := hint if types.is_valid(hint) else state.values[left].type
right, flow, ok = ct_eval_expr(state, expr.args[1], right_hint, depth+1)
_, distinct_ok := types.distinct_scalar_backing(right_hint, &checker.module.types)
right, flow, ok = ct_eval_expr(
state, expr.args[1], right_hint, depth+1, distinct_ok && right_const,
)
}
if !ok || flow.kind != .Normal {
return INVALID_CT_VALUE, flow, ok
@@ -2864,6 +2979,10 @@ ct_scalar_cast :: proc(state: ^Ct_State, id: Ct_Value_Id, target: types.Type, sp
return INVALID_CT_VALUE, ct_flow(.Normal), false
}
value := state.values[id]
if types.can_retype_distinct(value.type, target, &state.checker.module.types) {
value.type = target
return ct_add_value(state, value), ct_flow(.Normal), true
}
if !types.is_concrete_scalar(target) || types.is_bool(target) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "scalar cast requires numeric scalar types")
}
@@ -3039,6 +3158,16 @@ ct_typeinfo_value :: proc(state: ^Ct_State, target: types.Type, span: source.Spa
kind=.Struct, type=typeinfo_type, start=payload_start, count=1, active=i64(variant_index),
}), ct_flow(.Normal), true
}
if tag == "distinct" {
child_value := ct_add_value(state, Ct_Value{
kind=.Type, type=types.INVALID, index=u64(item.child),
})
payload_start := u32(len(state.children))
append(&state.children, child_value)
return ct_add_value(state, Ct_Value{
kind=.Struct, type=typeinfo_type, start=payload_start, count=1, active=i64(variant_index),
}), ct_flow(.Normal), true
}
if tag != "record" {
start := u32(len(state.children))
return ct_add_value(state, Ct_Value{
@@ -3522,7 +3651,11 @@ ct_eval_call_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Type
if (builtin == .Size_Of || builtin == .Align_Of) && !valid_layout_type(checker, target) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, checker.ast_module.exprs[expr.args[0]].span, "layout target must be a sized runtime value type, got %s", type_label(checker, target))
}
if (builtin == .Min_Value || builtin == .Max_Value) && !types.is_concrete_integer(target) {
bound_representation := target
if backing, ok := types.distinct_scalar_backing(target, &checker.module.types); ok {
bound_representation = backing
}
if (builtin == .Min_Value || builtin == .Max_Value) && !types.is_concrete_integer(bound_representation) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(state, .Not_Comptime, checker.ast_module.exprs[expr.args[0]].span, "integer bound target must be a concrete integer type, got %s", type_label(checker, target))
}
result_type := types.USIZE if builtin == .Size_Of || builtin == .Align_Of else target
@@ -3593,8 +3726,39 @@ ct_eval_call_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Type
)
named_item, named_ok := types.node(&checker.module.types, named_type)
target := types.resolve_alias(named_type, &checker.module.types)
if named_ok && named_item.kind == .Alias &&
types.is_concrete_scalar(target) && !types.is_bool(target) {
target_item, target_ok := types.node(&checker.module.types, target)
if target_ok && target_item.kind == .Distinct {
if len(expr.args) != 1 {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(
state,
.Not_Comptime,
expr.span,
"distinct type '%s' expects 1 argument, got %d",
symbol_text(checker, expr.name),
len(expr.args),
)
}
value, flow, ok := ct_eval_expr(state, expr.args[0], types.INVALID, depth+1)
if !ok || flow.kind != .Normal {
return INVALID_CT_VALUE, flow, ok
}
actual := state.values[value].type
if !types.can_retype_distinct(actual, target, &checker.module.types) {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(
state,
.Not_Comptime,
expr.span,
"distinct type '%s' requires an exact %s value, got %s",
symbol_text(checker, expr.name),
types.name(target_item.child),
types.name(actual),
)
}
result := state.values[value]
result.type = target
return ct_add_value(state, result), ct_flow(.Normal), true
}
if named_ok && named_item.kind == .Alias && types.is_concrete_scalar(target) {
if len(expr.args) != 1 {
return INVALID_CT_VALUE, ct_flow(.Normal), ct_failf(
state,
@@ -5027,11 +5191,17 @@ ct_exec_assignment :: proc(state: ^Ct_State, statement: ast.Stmt, depth: int) ->
}
} else {
value_expected := expected
numeric_operation := false
if statement.assignment_op == .Shift_Left || statement.assignment_op == .Shift_Right ||
statement.assignment_op == .Shift_Left_Saturating {
value_expected = types.U64
} else if statement.assignment_op != .Set &&
is_numeric_constant_expr(checker, statement.expr) {
_, numeric_operation = types.distinct_scalar_backing(expected, &checker.module.types)
}
value, flow, ok = ct_eval_expr(state, statement.expr, value_expected, depth+1)
value, flow, ok = ct_eval_expr(
state, statement.expr, value_expected, depth+1, numeric_operation,
)
}
if !ok || flow.kind != .Normal {
return flow, ok