feat: support compound updates and bitwise operators
This commit is contained in:
+105
-7
@@ -438,7 +438,7 @@ check_elaborated proc(
|
||||
match ast.expr_kind(tree, id) {
|
||||
.unary: {
|
||||
expression :: ast.unary_expr(tree, id)
|
||||
if (expression.operator == .negate) try require_numeric(&state, id, expression.operand)
|
||||
if (expression.operator == .negate or expression.operator == .bit_not) try require_numeric(&state, id, expression.operand)
|
||||
}
|
||||
.binary: {
|
||||
expression :: ast.binary_expr(tree, id)
|
||||
@@ -464,7 +464,7 @@ check_elaborated proc(
|
||||
match ast.expr_kind(tree, id) {
|
||||
.unary: {
|
||||
expression :: ast.unary_expr(tree, id)
|
||||
if (expression.operator == .negate) try numeric_operand(&state, id, expression.operand)
|
||||
if (expression.operator == .negate or expression.operator == .bit_not) try numeric_operand(&state, id, expression.operand)
|
||||
}
|
||||
.binary: {
|
||||
expression :: ast.binary_expr(tree, id)
|
||||
@@ -473,7 +473,11 @@ check_elaborated proc(
|
||||
if (!bool_equality(&state, id)) try comparison_operands(&state, id)
|
||||
} else {
|
||||
try numeric_operand(&state, id, expression.left)
|
||||
try numeric_operand(&state, id, expression.right)
|
||||
if (shift(expression.operator)) {
|
||||
count_group :: root(&state, usize(expression.right))
|
||||
if (state.cells[count_group].type.kind == .none and state.cells[count_group].demand == .none)
|
||||
try impose(&state, expression.right, .usize, .invalid_operand, expression.span)
|
||||
} else try numeric_operand(&state, id, expression.right)
|
||||
if (expression.operator == .divide) {
|
||||
try impose(&state, id, .float, .integer_division, expression.span)
|
||||
}
|
||||
@@ -1370,10 +1374,10 @@ collect_expression proc(state @mut State, id ast.NodeId) void ! (mem.AllocError
|
||||
}
|
||||
.unary: {
|
||||
expression :: ast.unary_expr(tree, id)
|
||||
if (expression.operator == .negate) state.cells[usize(id)].producer = id
|
||||
if (expression.operator == .negate or expression.operator == .bit_not) state.cells[usize(id)].producer = id
|
||||
if (expression.operator == .logical_not) {
|
||||
state.cells[usize(id)].type = Type{ kind = .bool }
|
||||
} else if (expression.operator == .negate) {
|
||||
} else if (expression.operator == .negate or expression.operator == .bit_not) {
|
||||
state.cells[usize(id)].numeric = true
|
||||
}
|
||||
operand :: expression.operand
|
||||
@@ -1570,6 +1574,27 @@ logical proc(operator ast.BinaryOperator) bool {
|
||||
return operator == .logical_and or operator == .logical_or
|
||||
}
|
||||
|
||||
@hide:file
|
||||
shift proc(operator ast.BinaryOperator) bool {
|
||||
return operator == .shift_left or operator == .shift_right or operator == .shift_left_saturating
|
||||
}
|
||||
|
||||
@hide:file
|
||||
bitwise proc(operator ast.BinaryOperator) bool {
|
||||
return operator == .bit_and or operator == .bit_or or operator == .bit_xor or shift(operator)
|
||||
}
|
||||
|
||||
@hide:file
|
||||
integer_width proc(kind TypeKind) usize {
|
||||
return match kind {
|
||||
.i8, .u8: 8
|
||||
.i16, .u16: 16
|
||||
.i32, .u32: 32
|
||||
.int, .isize, .u64, .usize: 64
|
||||
else: 0
|
||||
}
|
||||
}
|
||||
|
||||
@hide:file
|
||||
pointer_addition proc(state @mut State, id ast.NodeId) bool {
|
||||
expression :: ast.binary_expr(state.tree, id)
|
||||
@@ -2153,7 +2178,9 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
|
||||
return
|
||||
}
|
||||
operand :: state.types[usize(expression.operand)]
|
||||
if (operand.kind != .invalid and (!numeric_kind(operand.kind) or unsigned_kind(operand.kind))) try fail(state, id, .invalid_operand, ast.node_span(tree, id))
|
||||
if (expression.operator == .bit_not) {
|
||||
if (operand.kind != .invalid and !integer_kind(operand.kind)) try fail(state, id, .invalid_operand, expression.span)
|
||||
} else if (operand.kind != .invalid and (!numeric_kind(operand.kind) or unsigned_kind(operand.kind))) try fail(state, id, .invalid_operand, ast.node_span(tree, id))
|
||||
}
|
||||
.binary: {
|
||||
expression :: ast.binary_expr(tree, id)
|
||||
@@ -2165,6 +2192,21 @@ validate proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | diagnost
|
||||
left :: state.types[usize(expression.left)]
|
||||
right :: state.types[usize(expression.right)]
|
||||
if (left.kind == .invalid or right.kind == .invalid) return
|
||||
if (shift(expression.operator)) {
|
||||
if (!integer_kind(left.kind) or !unsigned_kind(right.kind)) {
|
||||
try fail(state, id, .invalid_operand, expression.span)
|
||||
} else if (expression.operator != .shift_left_saturating) {
|
||||
if (evaluate_integer(state, expression.right, true)) |count| {
|
||||
if (count.negative or count.magnitude >= u64(integer_width(left.kind)))
|
||||
try fail(state, id, .invalid_shift_count, expression.span)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if (bitwise(expression.operator) and (!integer_kind(left.kind) or !integer_kind(right.kind))) {
|
||||
try fail(state, id, .invalid_operand, expression.span)
|
||||
return
|
||||
}
|
||||
if (pointer_addition(state, id)) {
|
||||
if (right.kind != .usize or right.nominal != ast.NodeId(maxval!(u32))) try fail(state, id, .invalid_operand, expression.span)
|
||||
return
|
||||
@@ -2378,6 +2420,53 @@ constant_integer proc(state @mut State, id ast.NodeId) ?ConstantInteger {
|
||||
return evaluate_integer(state, id, false)
|
||||
}
|
||||
|
||||
@hide:file
|
||||
constant_bits proc(value ConstantInteger, mask u64) u64 {
|
||||
if (value.negative) return (value.magnitude - 1) xor mask
|
||||
return value.magnitude & mask
|
||||
}
|
||||
|
||||
@hide:file
|
||||
constant_from_bits proc(bits u64, kind TypeKind) ConstantInteger {
|
||||
mask :: maxval!(u64) >> u64(64 - integer_width(kind))
|
||||
negative :: !unsigned_kind(kind) and bits > (mask >> 1)
|
||||
magnitude :: if (negative) 1 + (bits xor mask) else bits
|
||||
return ConstantInteger{
|
||||
magnitude = magnitude,
|
||||
negative = negative,
|
||||
}
|
||||
}
|
||||
|
||||
@hide:file
|
||||
constant_bitwise proc(state @mut State, id ast.NodeId, left, right ConstantInteger) ?ConstantInteger {
|
||||
view :: ast.binary_expr(state.tree, id)
|
||||
family :: bound_family(state, id)
|
||||
kind :: if (family == .none) TypeKind.int else family
|
||||
width :: integer_width(kind)
|
||||
if (width == 0 or !integer_fits(left, kind)) return null
|
||||
mask :: maxval!(u64) >> u64(64 - width)
|
||||
a :: constant_bits(left, mask)
|
||||
b :: constant_bits(right, mask)
|
||||
if (view.operator == .bit_and) return constant_from_bits(a & b, kind)
|
||||
if (view.operator == .bit_or) return constant_from_bits(a | b, kind)
|
||||
if (view.operator == .bit_xor) return constant_from_bits(a xor b, kind)
|
||||
if (right.negative) return null
|
||||
count :: right.magnitude
|
||||
if (view.operator == .shift_left_saturating) {
|
||||
signed_limit :: if (left.negative) 1 + (mask >> 1) else mask >> 1
|
||||
limit :: if (unsigned_kind(kind)) mask else signed_limit
|
||||
if (left.magnitude == 0) return left
|
||||
if (count >= u64(width) or left.magnitude > (limit >> count))
|
||||
return ConstantInteger{ magnitude = limit, negative = left.negative }
|
||||
} else if (count >= u64(width)) return null
|
||||
if (view.operator == .shift_right) {
|
||||
shifted :: a >> count
|
||||
bits :: if (left.negative) shifted | (mask xor (mask >> count)) else shifted
|
||||
return constant_from_bits(bits, kind)
|
||||
}
|
||||
return constant_from_bits((a << count) & mask, kind)
|
||||
}
|
||||
|
||||
@hide:file
|
||||
evaluate_integer proc(state @mut State, id ast.NodeId, allow_local bool) ?ConstantInteger {
|
||||
if (state.cells[usize(id)].constant_visiting) return null
|
||||
@@ -2431,8 +2520,15 @@ evaluate_integer proc(state @mut State, id ast.NodeId, allow_local bool) ?Consta
|
||||
}
|
||||
.unary: {
|
||||
view :: ast.unary_expr(state.tree, id)
|
||||
if (view.operator != .negate) return null
|
||||
if (view.operator != .negate and view.operator != .bit_not) return null
|
||||
if (evaluate_integer(state, view.operand, allow_local)) |value| {
|
||||
if (view.operator == .bit_not) {
|
||||
family :: bound_family(state, id)
|
||||
kind :: if (family == .none) TypeKind.int else family
|
||||
if (!integer_kind(kind) or !integer_fits(value, kind)) return null
|
||||
mask :: maxval!(u64) >> u64(64 - integer_width(kind))
|
||||
return constant_from_bits(constant_bits(value, mask) xor mask, kind)
|
||||
}
|
||||
return ConstantInteger{ magnitude = value.magnitude, negative = value.magnitude != 0 and !value.negative }
|
||||
}
|
||||
return null
|
||||
@@ -2443,6 +2539,7 @@ evaluate_integer proc(state @mut State, id ast.NodeId, allow_local bool) ?Consta
|
||||
right := ConstantInteger{ magnitude = 0 }
|
||||
if (evaluate_integer(state, view.left, allow_local)) |value| left = value else return null
|
||||
if (evaluate_integer(state, view.right, allow_local)) |value| right = value else return null
|
||||
if (bitwise(view.operator)) return constant_bitwise(state, id, left, right)
|
||||
if (view.operator == .multiply) {
|
||||
if (right.magnitude != 0 and left.magnitude > divtrunc!(maxval!(u64), right.magnitude)) return null
|
||||
magnitude :: left.magnitude * right.magnitude
|
||||
@@ -3422,6 +3519,7 @@ bound_family proc(state @mut State, id ast.NodeId) TypeKind {
|
||||
if (ast.expr_kind(state.tree, producer) == .unary) return bound_family(state, ast.unary_expr(state.tree, producer).operand)
|
||||
view :: ast.binary_expr(state.tree, producer)
|
||||
left :: bound_family(state, view.left)
|
||||
if (shift(view.operator)) return left
|
||||
right :: bound_family(state, view.right)
|
||||
if (left == .float or right == .float) return .float
|
||||
if (integer_kind(left)) return left
|
||||
|
||||
@@ -718,7 +718,7 @@ resolves_every_array_child_error_and_preserves_lexer_diagnostics test {
|
||||
` return
|
||||
` data [missing_count]MissingElement :: [absent, missing_element]
|
||||
` (missing_base[missing_index])[missing_outer] = missing_rhs
|
||||
` malformed [~]i64 :: [~]
|
||||
` malformed [%]i64 :: [%]
|
||||
`}
|
||||
scan_state := lexer.init(mem.c_allocator)
|
||||
defer lexer.deinit(&scan_state)
|
||||
|
||||
@@ -125,6 +125,8 @@ elaborate proc(
|
||||
if (class == .expression
|
||||
and (ast.expr_kind(&original, id) == .comptime_expr or ast.expr_kind(&original, id) == .block_expr))
|
||||
needed = true
|
||||
if (class == .expression and ast.expr_kind(&original, id) == .binary
|
||||
and ast.binary_expr(&original, id).operator == .bit_or) needed = true
|
||||
if (class == .statement and ast.stmt_kind(&original, id) == .for_stmt
|
||||
and ast.for_stmt(&original, id).is_inline) needed = true
|
||||
if (class == .match_arm and ast.match_arm_view(&original, id).is_inline) needed = true
|
||||
@@ -451,6 +453,14 @@ expand proc(state @mut State, id ast.NodeId) void ! Error {
|
||||
}
|
||||
if (class == .expression and ast.expr_kind(&state.tree, id) == .binary) {
|
||||
view :: ast.binary_expr(&state.tree, id)
|
||||
if (view.operator == .bit_or
|
||||
and (is_type(state, try expression_type(state, view.left, 0))
|
||||
or is_type(state, try expression_type(state, view.right, 0)))) {
|
||||
composed :: try ast.add_binary_type(state.builder, view.span, view.left, view.right, false)
|
||||
try replace(state, id, composed)
|
||||
try refresh(state)
|
||||
return
|
||||
}
|
||||
if (
|
||||
(view.operator == .equal or view.operator == .not_equal)
|
||||
and branch_constant(state, view.left, 0) and branch_constant(state, view.right, 0)
|
||||
@@ -1502,7 +1512,7 @@ branch_constant proc(state @State, id ast.NodeId, depth usize) bool {
|
||||
.unary: {
|
||||
view :: ast.unary_expr(&state.tree, id)
|
||||
return (
|
||||
(view.operator == .negate or view.operator == .logical_not)
|
||||
(view.operator == .negate or view.operator == .logical_not or view.operator == .bit_not)
|
||||
and branch_constant(state, view.operand, depth + 1)
|
||||
)
|
||||
}
|
||||
@@ -1955,6 +1965,15 @@ contextual_literal proc(state @State, id ast.NodeId) bool {
|
||||
kind :: ast.expr_kind(&state.tree, id)
|
||||
if (kind == .builtin_call)
|
||||
return mem.eql(ast.literal_text(&state.tree, ast.builtin_call(&state.tree, id).name), "some")
|
||||
if (kind == .unary) return ast.unary_expr(&state.tree, id).operator == .bit_not and flexible(state, id)
|
||||
if (kind == .binary) {
|
||||
operator :: ast.binary_expr(&state.tree, id).operator
|
||||
return (
|
||||
(operator == .bit_and or operator == .bit_or or operator == .bit_xor
|
||||
or operator == .shift_left or operator == .shift_right or operator == .shift_left_saturating)
|
||||
and flexible(state, id)
|
||||
)
|
||||
}
|
||||
return kind == .record_literal or kind == .array_literal or kind == .enum_literal or kind == .block_expr
|
||||
}
|
||||
|
||||
@@ -3963,6 +3982,7 @@ execute_type_statement_body proc(state @mut State, execution @mut TypeExecution,
|
||||
if (target != assignment.target) {
|
||||
normalized :: try ast.add_assignment_stmt(state.builder,
|
||||
ast.node_span(&state.tree, statement), target, assignment.value)
|
||||
if (assignment.compound) ast.set_assignment_compound(state.builder, normalized)
|
||||
try refresh(state)
|
||||
# synthesized places still need local substitution in snapshots.
|
||||
previous :: execution.mapping.len
|
||||
@@ -4442,6 +4462,8 @@ evaluate proc(state @mut State, id ast.NodeId) Value ! Error {
|
||||
.alias_expr: return try evaluate(state, ast.type_operand(&state.tree, id))
|
||||
.unary: {
|
||||
view :: ast.unary_expr(&state.tree, id)
|
||||
if (view.operator == .bit_not)
|
||||
return try evaluate_ir(state, id, try expression_type(state, id, 0))
|
||||
value := try evaluate(state, view.operand)
|
||||
if (view.operator == .negate and value.kind == .integer) value.negative = value.magnitude != 0 and !value.negative
|
||||
else if (view.operator == .negate and value.kind == .real) value.magnitude = value.magnitude xor (u64(1) << 63)
|
||||
@@ -4484,6 +4506,9 @@ evaluate proc(state @mut State, id ast.NodeId) Value ! Error {
|
||||
evaluate_binary proc(state @mut State, id ast.NodeId) Value ! Error {
|
||||
view :: ast.binary_expr(&state.tree, id)
|
||||
if (view.operator == .divide) return try evaluate_ir(state, id, try builtin(state, id, .float))
|
||||
if (view.operator == .bit_and or view.operator == .bit_or or view.operator == .bit_xor
|
||||
or view.operator == .shift_left or view.operator == .shift_right or view.operator == .shift_left_saturating)
|
||||
return try evaluate_ir(state, id, try expression_type(state, id, 0))
|
||||
left :: try evaluate(state, view.left)
|
||||
if (left.kind == .invalid) return Value{}
|
||||
if (left.kind == .boolean) {
|
||||
@@ -4978,6 +5003,8 @@ expression_type proc(state @mut State, id ast.NodeId, depth usize) ast.NodeId !
|
||||
or view.operator == .less_equal or view.operator == .greater or view.operator == .greater_equal
|
||||
or view.operator == .logical_and or view.operator == .logical_or
|
||||
) return try builtin(state, id, .bool)
|
||||
if ((view.operator == .bit_and or view.operator == .bit_or or view.operator == .bit_xor)
|
||||
and flexible(state, view.left)) return try expression_type(state, view.right, depth + 1)
|
||||
return try expression_type(state, view.left, depth + 1)
|
||||
}
|
||||
.selection: {
|
||||
@@ -5326,12 +5353,14 @@ flexible proc(state @State, id ast.NodeId) bool {
|
||||
if (kind == .literal_int or kind == .literal_float) return true
|
||||
if (kind == .unary) {
|
||||
view :: ast.unary_expr(&state.tree, id)
|
||||
return view.operator == .negate and flexible(state, view.operand)
|
||||
return (view.operator == .negate or view.operator == .bit_not) and flexible(state, view.operand)
|
||||
}
|
||||
if (kind == .binary) {
|
||||
view :: ast.binary_expr(&state.tree, id)
|
||||
return (
|
||||
(view.operator == .add or view.operator == .subtract or view.operator == .multiply or view.operator == .divide)
|
||||
(view.operator == .add or view.operator == .subtract or view.operator == .multiply or view.operator == .divide
|
||||
or view.operator == .bit_and or view.operator == .bit_or or view.operator == .bit_xor
|
||||
or view.operator == .shift_left or view.operator == .shift_right or view.operator == .shift_left_saturating)
|
||||
and flexible(state, view.left) and flexible(state, view.right)
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user