bitwise operations

This commit is contained in:
2026-07-19 00:36:31 +02:00
parent f9448363e4
commit c7e3162ecb
21 changed files with 219439 additions and 140077 deletions
+124 -3
View File
@@ -1270,14 +1270,18 @@ ct_eval_expr :: proc(
return ct_add_value(state, Ct_Value{
kind=.Range, type=types.range(store, child_type), start=start, count=2, active=i64(expr.integer),
}), ct_flow(.Normal), true
case .Negate, .Not:
case .Negate, .Not, .Bit_Not:
value, flow, ok := ct_eval_expr(state, expr.left, expected, depth+1)
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, .Eq, .Ne, .Lt, .Le, .Gt, .Ge:
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, flow, ok := ct_eval_expr(state, expr.left, left_expected, depth+1)
if !ok || flow.kind != .Normal {
return INVALID_CT_VALUE, flow, ok
@@ -1287,6 +1291,16 @@ ct_eval_expr :: proc(
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)
if !ok || flow.kind != .Normal {
return INVALID_CT_VALUE, flow, ok
}
right, right_flow, right_ok := ct_eval_expr(state, expr.right, types.U64, 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 .And, .Or:
left, flow, ok := ct_eval_expr(state, expr.left, types.BOOL, depth+1)
if !ok || flow.kind != .Normal {
@@ -2033,6 +2047,19 @@ ct_unwrap_optional :: proc(state: ^Ct_State, id: Ct_Value_Id, span: source.Span)
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, span, "postfix '?' requires an optional")
}
ct_normalize_integer :: proc(state: ^Ct_State, value: i128, type: types.Type) -> i128 {
bits := types.bits(type, state.checker.target)
mask := (i128(1) << u32(bits))-1
raw := value & mask
if types.is_signed(type, state.checker.target) {
sign := i128(1) << u32(bits-1)
if raw & sign != 0 {
return raw-(i128(1) << u32(bits))
}
}
return raw
}
ct_eval_unary :: proc(state: ^Ct_State, op: ast.Expr_Kind, id: Ct_Value_Id, span: source.Span) -> (Ct_Value_Id, Ct_Flow, bool) {
if id == INVALID_CT_VALUE || int(id) >= len(state.values) {
return INVALID_CT_VALUE, ct_flow(.Normal), false
@@ -2044,6 +2071,13 @@ 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) {
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 {
result, overflow := intrinsics.overflow_sub(i128(0), value.integer)
if overflow {
@@ -2125,6 +2159,62 @@ ct_eval_binary :: proc(state: ^Ct_State, op: ast.Expr_Kind, left_id, right_id: C
"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")
}
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
}
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 {
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)
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) {
endpoint = -(i128(1) << u32(bits-1)) if left.integer < 0 else (i128(1) << u32(bits-1))-1
} else {
endpoint = (i128(1) << u32(bits))-1
}
}
return ct_add_value(state, Ct_Value{kind=.Integer, type=left.type, integer=endpoint}), ct_flow(.Normal), true
}
count := u32(right.integer)
if op == .Shift_Right {
value := left.integer >> count
if !types.is_signed(left.type, 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
}
if op == .Shift_Left_Saturating {
factor := i128(1) << count
value := left.integer*factor
if types.is_signed(left.type, state.checker.target) {
minimum := -(i128(1) << u32(bits-1))
maximum := (i128(1) << u32(bits-1))-1
value = max(minimum, min(maximum, value))
} else {
maximum := (i128(1) << u32(bits))-1
value = min(maximum, ct_normalize_integer(state, left.integer, left.type)*factor)
}
return ct_add_value(state, Ct_Value{kind=.Integer, type=left.type, integer=value}), ct_flow(.Normal), true
}
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
}
value: i128
overflow := false
#partial switch op {
@@ -3478,7 +3568,12 @@ ct_exec_assignment :: proc(state: ^Ct_State, statement: ast.Stmt, depth: int) ->
flow = ct_flow(.Normal)
}
} else {
value, flow, ok = ct_eval_expr(state, statement.expr, expected, depth+1)
value_expected := expected
if statement.assignment_op == .Shift_Left || statement.assignment_op == .Shift_Right ||
statement.assignment_op == .Shift_Left_Saturating {
value_expected = types.U64
}
value, flow, ok = ct_eval_expr(state, statement.expr, value_expected, depth+1)
}
if !ok || flow.kind != .Normal {
return flow, ok
@@ -3493,6 +3588,12 @@ ct_exec_assignment :: proc(state: ^Ct_State, statement: ast.Stmt, depth: int) ->
case .Sub: op = .Sub
case .Mul: op = .Mul
case .Div: op = .Div
case .Bit_And: op = .Bit_And
case .Bit_Or: op = .Bit_Or
case .Bit_Xor: op = .Bit_Xor
case .Shift_Left: op = .Shift_Left
case .Shift_Right: op = .Shift_Right
case .Shift_Left_Saturating: op = .Shift_Left_Saturating
case: op = .Add
}
bin_flow: Ct_Flow
@@ -4046,6 +4147,26 @@ eval_integer_constant_in_context :: proc(
return Constant{kind=.Value, value=integer}
}
try_fold_typed_integer_expr :: proc(
checker: ^Checker,
expr_id: ast.Expr_Id,
expected: types.Type,
pkg: ast.Package_Id,
file: ast.File_Id,
) -> (hir.Expr_Id, bool) {
if !is_typed_integer_fold_candidate(checker, expr_id) {
return hir.INVALID_EXPR, false
}
state := ct_state_make(checker, pkg, file, diagnose=false)
defer ct_state_destroy(&state)
value, flow, ok := ct_eval_expr(&state, expr_id, expected)
if !ok || flow.kind != .Normal || value == INVALID_CT_VALUE || int(value) >= len(state.values) ||
state.values[value].kind != .Integer {
return hir.INVALID_EXPR, false
}
return ct_materialize_value(&state, value, checker.ast_module.exprs[expr_id].span, expected), true
}
eval_comptime_statements :: proc(
checker: ^Checker,
statements: []ast.Stmt_Id,