refactor: remove enum-map lookup wrappers
This commit is contained in:
@@ -241,9 +241,11 @@ AST `NodeId` values are compilation-wide, so declarations in different files can
|
||||
|
||||
AST nodes retain a compact 16-byte layout on the bootstrap target: a kind, three 32-bit payload words and one packed flag byte in existing padding. file-local, foreign-callable, variadic and opaque metadata therefore need neither per-node boolean fields nor another allocation. spans and variable-length payloads remain in parallel storage; cloning and replacement copy the flags with the node.
|
||||
|
||||
the AST renderer displays nested blocks, cleanup bodies/captures and compound operators alongside ordinary statements.
|
||||
|
||||
identifier interning is compilation-owned: initialize a `strpool.StringPool`, pass it to `lexer.scan` and `sema.resolve` before the diagnostic store, and release it after lowering. scanning interns identifier spellings once per pool; parsing preserves their IDs in compact AST storage, exposed through `ast.name_id`, while retaining source spans for diagnostics. name resolution keys scopes by those IDs and interns builtin names into the same pool, so identifier hashing and equality do not scan spelling bytes. IDs are meaningful only within their originating pool; independent compilations may assign the same numeric ID to different names. interning failures propagate as infrastructure errors rather than producing an invalid identifier ID. IR retains no pool dependency.
|
||||
|
||||
compiler token/operator mappings, scalar type metadata and backend spellings use compile-time `@std/enummap` tables; state-dependent layout and control-flow dispatch remain ordinary code. `enummap.init_filled(E, V, storage_default, fields)` initializes unused value slots so sparse maps can become owned compile-time constants without changing which keys are present. `enummap.get` still performs runtime linear lookup; static construction is not a constant-time lookup guarantee.
|
||||
compiler token/operator mappings, scalar type metadata and backend spellings use compile-time `@std/enummap` tables directly; state-dependent layout and control-flow dispatch remain ordinary code. IR exports `integer_minima` and `integer_maxima` maps for shared integer bounds, with absent entries for noninteger types. `enummap.init_default(E, V, storage_default, fields)` initializes unused value slots so sparse maps can become owned compile-time constants without changing which keys are present. `enummap.get` still performs runtime linear lookup; static construction is not a constant-time lookup guarantee.
|
||||
|
||||
resolver scopes use `@std/hashmap.AutoHashMap(K, V)`, which shares `init`, `put`, `get` and `deinit` with custom `HashMap` and `StringHashMap`. automatic keys support integers, booleans, enums, distinct types, fixed arrays and records recursively; record padding is ignored. slices, floating-point values, pointers, optionals and unions require explicit hashing and equality through `HashMap` (`StringHashMap` supplies content-based byte-string operations). keys and values are stored by value; referenced resources remain caller-owned. the builtin declaration count is inferred, and builtin ID storage derives its fixed stack-array length from that declaration array rather than repeating a count or initializer.
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ render_function proc(
|
||||
render_block proc(renderer @mut Renderer, tree @ast.Tree, block ast.NodeId, depth usize) void ! mem.AllocError {
|
||||
for 0..ast.block_statement_count(tree, block) |index| {
|
||||
statement :: ast.block_statement(tree, block, index)
|
||||
for 0..depth |_| try append_text(renderer, " ")
|
||||
for (0..depth) |_| try append_text(renderer, " ")
|
||||
match ast.stmt_kind(tree, statement) {
|
||||
.declaration: try render_statement(renderer, tree, statement, "")
|
||||
.expression: {
|
||||
@@ -65,6 +65,22 @@ render_block proc(renderer @mut Renderer, tree @ast.Tree, block ast.NodeId, dept
|
||||
}
|
||||
.break_stmt: try append_text(renderer, "break\n")
|
||||
.continue_stmt: try append_text(renderer, "continue\n")
|
||||
.block_stmt: {
|
||||
try append_text(renderer, "block\n")
|
||||
try render_block(renderer, tree, ast.statement_block(tree, statement), depth + 1)
|
||||
}
|
||||
.defer_stmt, .errdefer_stmt: {
|
||||
cleanup :: ast.cleanup_stmt(tree, statement)
|
||||
keyword :: if (ast.stmt_kind(tree, statement) == .errdefer_stmt) "errdefer" else "defer"
|
||||
try append_text(renderer, keyword)
|
||||
if (cleanup.capture) |capture| {
|
||||
try append_text(renderer, " |")
|
||||
try append_text(renderer, ast.source_text(tree, ast.node_span(tree, capture)))
|
||||
try append_text(renderer, "|")
|
||||
}
|
||||
try append_text(renderer, "\n")
|
||||
try render_block(renderer, tree, cleanup.body, depth + 1)
|
||||
}
|
||||
.return_stmt: {
|
||||
try append_text(renderer, "return")
|
||||
if (ast.return_stmt(tree, statement).value) |value| {
|
||||
@@ -86,7 +102,7 @@ render_block proc(renderer @mut Renderer, tree @ast.Tree, block ast.NodeId, dept
|
||||
try append_text(renderer, "\n")
|
||||
try render_block(renderer, tree, conditional.then_block, depth + 1)
|
||||
if (conditional.else_block) |alternative| {
|
||||
for 0..depth |_| try append_text(renderer, " ")
|
||||
for (0..depth) |_| try append_text(renderer, " ")
|
||||
try append_text(renderer, "else\n")
|
||||
try render_block(renderer, tree, alternative, depth + 1)
|
||||
}
|
||||
@@ -99,7 +115,7 @@ render_block proc(renderer @mut Renderer, tree @ast.Tree, block ast.NodeId, dept
|
||||
if (assignment.compound) {
|
||||
binary :: ast.binary_expr(tree, value)
|
||||
try append_text(renderer, " ")
|
||||
try append_text(renderer, assignment_operator_text(binary.operator))
|
||||
try append_text(renderer, enummap.get(&assignment_operator_texts, binary.operator)?)
|
||||
try append_text(renderer, "= ")
|
||||
value = binary.right
|
||||
} else {
|
||||
@@ -126,7 +142,7 @@ render_block proc(renderer @mut Renderer, tree @ast.Tree, block ast.NodeId, dept
|
||||
else try append_text(renderer, "_")
|
||||
if (loop.index_capture) {
|
||||
try append_text(renderer, ", ")
|
||||
if (loop.index) |index| try append_text(renderer, ast.source_text(tree, ast.node_span(tree, index)))
|
||||
if (loop.index) |index_capture| try append_text(renderer, ast.source_text(tree, ast.node_span(tree, index_capture)))
|
||||
else try append_text(renderer, "_")
|
||||
}
|
||||
try append_text(renderer, "|\n")
|
||||
@@ -152,7 +168,8 @@ render_statement proc(
|
||||
try append_text(renderer, " ")
|
||||
try append_text(renderer, ast.source_text(tree, ast.node_span(tree, type)))
|
||||
}
|
||||
try append_text(renderer, if (statement.mutability == .constant) " :: " else " := ")
|
||||
binding :: if (statement.mutability == .constant) " :: " else " := "
|
||||
try append_text(renderer, binding)
|
||||
try append_text(renderer, ast.source_text(tree, ast.node_span(tree, statement.initializer)))
|
||||
try append_text(renderer, "\n")
|
||||
}
|
||||
@@ -163,7 +180,7 @@ append_text proc(renderer @mut Renderer, text []u8) void ! mem.AllocError {
|
||||
}
|
||||
|
||||
@hide:file
|
||||
assignment_operator_texts :: $enummap.init_filled(
|
||||
assignment_operator_texts :: $enummap.init_default(
|
||||
ast.BinaryOperator, []u8, "", {
|
||||
add = "+",
|
||||
subtract = "-",
|
||||
@@ -178,7 +195,3 @@ assignment_operator_texts :: $enummap.init_filled(
|
||||
},
|
||||
)
|
||||
|
||||
@hide:file
|
||||
assignment_operator_text proc(operator ast.BinaryOperator) []u8 {
|
||||
return enummap.get(&assignment_operator_texts, operator)?
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import "@std/mem"
|
||||
import "@std/arraylist"
|
||||
import "@std/enummap"
|
||||
import "@source/ir"
|
||||
import "@source/diagnostics"
|
||||
|
||||
@@ -579,7 +580,7 @@ execute proc(
|
||||
if instruction.type == .float {
|
||||
value = ir.Value{ type = .float, data = ir.ValueData{ real = -operand.data.real } }
|
||||
} else {
|
||||
if (operand.data.integer == ir.integer_min(instruction.type)) return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
|
||||
if (operand.data.integer == enummap.get(&ir.integer_minima, instruction.type)?) return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
|
||||
value = ir.Value{ type = instruction.type, data = ir.ValueData{ integer = -operand.data.integer } }
|
||||
}
|
||||
}
|
||||
@@ -628,7 +629,7 @@ execute proc(
|
||||
} else if ir.unsigned_integer(instruction.type) {
|
||||
result :: checked_unsigned(instruction.op, left.data.unsigned, right.data.unsigned)
|
||||
if result |unsigned| {
|
||||
if (unsigned > ir.integer_max(instruction.type)) return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
|
||||
if (unsigned > enummap.get(&ir.integer_maxima, instruction.type)?) return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
|
||||
value = ir.Value{ type = instruction.type, data = ir.ValueData{ unsigned = unsigned } }
|
||||
} else {
|
||||
return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
|
||||
@@ -636,7 +637,7 @@ execute proc(
|
||||
} else {
|
||||
result :: checked_integer(instruction.op, left.data.integer, right.data.integer)
|
||||
if result |result_integer| {
|
||||
if (result_integer < ir.integer_min(instruction.type) or result_integer > i64(ir.integer_max(instruction.type))) return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
|
||||
if (result_integer < enummap.get(&ir.integer_minima, instruction.type)? or result_integer > i64(enummap.get(&ir.integer_maxima, instruction.type)?)) return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
|
||||
value = ir.Value{ type = instruction.type, data = ir.ValueData{ integer = result_integer } }
|
||||
} else {
|
||||
return try runtime_trap(diagnostic_store, .integer_overflow, instruction.span)
|
||||
@@ -950,7 +951,7 @@ shift_integer proc(op ir.Op, left ir.Value, count u64) ir.Value {
|
||||
negative :: signed and left.data.integer < 0
|
||||
if (op == .shift_left_saturating) {
|
||||
if (bits == 0) return left
|
||||
limit :: if (negative) 1 + (mask >> 1) else ir.integer_max(left.type)
|
||||
limit :: if (negative) 1 + (mask >> 1) else enummap.get(&ir.integer_maxima, left.type)?
|
||||
magnitude :: if (negative) 1 + (bits xor mask) else bits
|
||||
if (count >= width) return integer_from_bits(left.type, limit)
|
||||
if (magnitude > (limit >> count)) return integer_from_bits(left.type, limit)
|
||||
@@ -1054,28 +1055,28 @@ convert proc(source ir.Value, target ir.Type) ?ir.Value {
|
||||
if (ir.unsigned_integer(target)) {
|
||||
if (!(real > -1.0 and real < 18446744073709551616.0)) return null
|
||||
integer :: u64(real)
|
||||
if (integer > ir.integer_max(target)) return null
|
||||
if (integer > enummap.get(&ir.integer_maxima, target)?) return null
|
||||
result.data = ir.ValueData{ unsigned = integer }
|
||||
} else {
|
||||
if (!(real >= -9223372036854775808.0 and real < 9223372036854775808.0)) return null
|
||||
integer :: i64(real)
|
||||
if (integer < ir.integer_min(target) or integer > i64(ir.integer_max(target))) return null
|
||||
if (integer < enummap.get(&ir.integer_minima, target)? or integer > i64(enummap.get(&ir.integer_maxima, target)?)) return null
|
||||
result.data = ir.ValueData{ integer = integer }
|
||||
}
|
||||
return result
|
||||
}
|
||||
if (ir.unsigned_integer(source.type)) {
|
||||
integer :: source.data.unsigned
|
||||
if (integer > ir.integer_max(target)) return null
|
||||
if (integer > enummap.get(&ir.integer_maxima, target)?) return null
|
||||
if (ir.unsigned_integer(target)) result.data = ir.ValueData{ unsigned = integer }
|
||||
else result.data = ir.ValueData{ integer = i64(integer) }
|
||||
} else {
|
||||
integer :: source.data.integer
|
||||
if (ir.unsigned_integer(target)) {
|
||||
if (integer < 0 or u64(integer) > ir.integer_max(target)) return null
|
||||
if (integer < 0 or u64(integer) > enummap.get(&ir.integer_maxima, target)?) return null
|
||||
result.data = ir.ValueData{ unsigned = u64(integer) }
|
||||
} else {
|
||||
if (integer < ir.integer_min(target) or integer > i64(ir.integer_max(target))) return null
|
||||
if (integer < enummap.get(&ir.integer_minima, target)? or integer > i64(enummap.get(&ir.integer_maxima, target)?)) return null
|
||||
result.data = ir.ValueData{ integer = integer }
|
||||
}
|
||||
}
|
||||
|
||||
+7
-15
@@ -35,7 +35,7 @@ aggregate proc(type Type) bool {
|
||||
}
|
||||
|
||||
@hide:file
|
||||
integer_widths :: $enummap.init_filled(Type, usize, usize(0), {
|
||||
integer_widths :: $enummap.init_default(Type, usize, usize(0), {
|
||||
i8 = 8, u8 = 8,
|
||||
i16 = 16, u16 = 16,
|
||||
i32 = 32, u32 = 32,
|
||||
@@ -52,8 +52,8 @@ unsigned_integer proc(type Type) bool {
|
||||
return type == .u8 or type == .u16 or type == .u32 or type == .u64 or type == .usize
|
||||
}
|
||||
|
||||
@hide:file
|
||||
integer_maxima :: $enummap.init_filled(Type, u64, u64(0), {
|
||||
#! maxima for supported integer storage types; noninteger keys are absent.
|
||||
integer_maxima :: $enummap.init_default(Type, u64, u64(0), {
|
||||
i8 = 127,
|
||||
i16 = 32767,
|
||||
i32 = 2147483647,
|
||||
@@ -64,12 +64,8 @@ integer_maxima :: $enummap.init_filled(Type, u64, u64(0), {
|
||||
u64 = maxval!(u64), usize = maxval!(u64),
|
||||
})
|
||||
|
||||
integer_max proc(type Type) u64 {
|
||||
return enummap.get(&integer_maxima, type)?
|
||||
}
|
||||
|
||||
@hide:file
|
||||
integer_minima :: $enummap.init_filled(Type, i64, i64(0), {
|
||||
#! minima for supported integer storage types; noninteger keys are absent.
|
||||
integer_minima :: $enummap.init_default(Type, i64, i64(0), {
|
||||
i8 = i64(-128),
|
||||
i16 = i64(-32768),
|
||||
i32 = i64(-2147483648),
|
||||
@@ -77,10 +73,6 @@ integer_minima :: $enummap.init_filled(Type, i64, i64(0), {
|
||||
u8 = 0, u16 = 0, u32 = 0, u64 = 0, usize = 0,
|
||||
})
|
||||
|
||||
integer_min proc(type Type) i64 {
|
||||
return enummap.get(&integer_minima, type)?
|
||||
}
|
||||
|
||||
ValueData :: union {
|
||||
integer i64
|
||||
# source pointers use run-wide handles; internal addresses use frame-local IDs below.
|
||||
@@ -421,12 +413,12 @@ aligned_size proc(size, alignment usize) usize {
|
||||
}
|
||||
|
||||
@hide:file
|
||||
scalar_alignments :: $enummap.init_filled(Type, usize, usize(1), {
|
||||
scalar_alignments :: $enummap.init_default(Type, usize, usize(1), {
|
||||
float = 8, string = 8, function = 8, pointer = 8,
|
||||
})
|
||||
|
||||
@hide:file
|
||||
scalar_sizes :: $enummap.init_filled(Type, usize, usize(0), {
|
||||
scalar_sizes :: $enummap.init_default(Type, usize, usize(0), {
|
||||
float = 8, function = 8, pointer = 8,
|
||||
string = 16,
|
||||
bool = 1,
|
||||
|
||||
@@ -4,14 +4,14 @@ import "@source/ir"
|
||||
import "@source/diagnostics"
|
||||
|
||||
@hide:file
|
||||
bitwise_opcodes :: $enummap.init_filled(ir.Op, []u8, "", {
|
||||
bitwise_opcodes :: $enummap.init_default(ir.Op, []u8, "", {
|
||||
bit_and = "and",
|
||||
bit_or = "or",
|
||||
bit_xor = "xor",
|
||||
})
|
||||
|
||||
@hide:file
|
||||
float_opcodes :: $enummap.init_filled(ir.Op, []u8, "", {
|
||||
float_opcodes :: $enummap.init_default(ir.Op, []u8, "", {
|
||||
add = "fadd",
|
||||
subtract = "fsub",
|
||||
multiply = "fmul",
|
||||
@@ -19,14 +19,14 @@ float_opcodes :: $enummap.init_filled(ir.Op, []u8, "", {
|
||||
})
|
||||
|
||||
@hide:file
|
||||
integer_opcodes :: $enummap.init_filled(ir.Op, []u8, "", {
|
||||
integer_opcodes :: $enummap.init_default(ir.Op, []u8, "", {
|
||||
add = "add",
|
||||
subtract = "sub",
|
||||
multiply = "mul",
|
||||
})
|
||||
|
||||
@hide:file
|
||||
float_predicates :: $enummap.init_filled(ir.Op, []u8, "", {
|
||||
float_predicates :: $enummap.init_default(ir.Op, []u8, "", {
|
||||
equal = "oeq",
|
||||
not_equal = "une",
|
||||
less = "olt",
|
||||
@@ -36,7 +36,7 @@ float_predicates :: $enummap.init_filled(ir.Op, []u8, "", {
|
||||
})
|
||||
|
||||
@hide:file
|
||||
integer_predicates :: $enummap.init_filled(ir.Op, []u8, "", {
|
||||
integer_predicates :: $enummap.init_default(ir.Op, []u8, "", {
|
||||
equal = "eq",
|
||||
not_equal = "ne",
|
||||
less = "lt",
|
||||
@@ -46,7 +46,7 @@ integer_predicates :: $enummap.init_filled(ir.Op, []u8, "", {
|
||||
})
|
||||
|
||||
@hide:file
|
||||
conversion_upper_bounds :: $enummap.init_filled(ir.Type, []u8, "", {
|
||||
conversion_upper_bounds :: $enummap.init_default(ir.Type, []u8, "", {
|
||||
i8 = "1.280000e+02",
|
||||
i16 = "3.276800e+04",
|
||||
i32 = "2.147483648e+09",
|
||||
@@ -387,7 +387,7 @@ emit_instruction proc(state @mut State, block @ir.Block, instruction ir.Instruct
|
||||
minimum :: fresh(state)
|
||||
negative_one :: fresh(state)
|
||||
overflow :: fresh(state)
|
||||
try io.print(" %r{} = icmp eq i{} %r{}, {}\n", output, {minimum, bits, left, ir.integer_min(instruction.type)})
|
||||
try io.print(" %r{} = icmp eq i{} %r{}, {}\n", output, {minimum, bits, left, enummap.get(&ir.integer_minima, instruction.type)?})
|
||||
try io.print(" %r{} = icmp eq i{} %r{}, -1\n", output, {negative_one, bits, right})
|
||||
try io.print(" %r{} = and i1 %r{}, %r{}\n", output, {overflow, minimum, negative_one})
|
||||
try check_failure(state, overflow, .integer_overflow, instruction.span)
|
||||
@@ -472,9 +472,9 @@ emit_shift proc(state @mut State, instruction ir.Instruction) usize ! io.WriteEr
|
||||
if (signed) {
|
||||
negative :: fresh(state)
|
||||
try io.print(" %r{} = icmp slt i{} %r{}, 0\n", output, {negative, bits, left})
|
||||
try io.print(" %r{} = select i1 %r{}, i{} {}, i{} {}\n", output, {limit, negative, bits, ir.integer_min(instruction.type), bits, ir.integer_max(instruction.type)})
|
||||
try io.print(" %r{} = select i1 %r{}, i{} {}, i{} {}\n", output, {limit, negative, bits, enummap.get(&ir.integer_minima, instruction.type)?, bits, enummap.get(&ir.integer_maxima, instruction.type)?})
|
||||
} else {
|
||||
try io.print(" %r{} = or i{} 0, {}\n", output, {limit, bits, ir.integer_max(instruction.type)})
|
||||
try io.print(" %r{} = or i{} 0, {}\n", output, {limit, bits, enummap.get(&ir.integer_maxima, instruction.type)?})
|
||||
}
|
||||
result :: fresh(state)
|
||||
try io.print(" %r{} = select i1 %r{}, i{} %r{}, i{} %r{}\n", output, {result, saturate, bits, limit, bits, shifted})
|
||||
@@ -684,13 +684,13 @@ emit_conversion proc(state @mut State, instruction ir.Instruction) usize ! io.Wr
|
||||
}
|
||||
if (!source_unsigned and (destination_unsigned or destination_bits < source_bits)) {
|
||||
failed :: fresh(state)
|
||||
try io.print(" %r{} = icmp slt i{} %r{}, {}\n", output, {failed, source_bits, operand, ir.integer_min(destination)})
|
||||
try io.print(" %r{} = icmp slt i{} %r{}, {}\n", output, {failed, source_bits, operand, enummap.get(&ir.integer_minima, destination)?})
|
||||
try check_failure(state, failed, .integer_overflow, instruction.span)
|
||||
}
|
||||
if (destination_bits < source_bits or (source_unsigned and !destination_unsigned and destination_bits == source_bits)) {
|
||||
failed :: fresh(state)
|
||||
predicate []u8 :: if (source_unsigned) "ugt" else "sgt"
|
||||
try io.print(" %r{} = icmp {s} i{} %r{}, {}\n", output, {failed, predicate, source_bits, operand, ir.integer_max(destination)})
|
||||
try io.print(" %r{} = icmp {s} i{} %r{}, {}\n", output, {failed, predicate, source_bits, operand, enummap.get(&ir.integer_maxima, destination)?})
|
||||
try check_failure(state, failed, .integer_overflow, instruction.span)
|
||||
}
|
||||
if (source_bits == destination_bits) return operand
|
||||
|
||||
+5
-10
@@ -71,7 +71,7 @@ emit proc(
|
||||
.invalid_shift_count,
|
||||
]
|
||||
for (memory_codes) |code| {
|
||||
try io.print("@memory_{s} = private constant ", output, {memory_symbol(code)})
|
||||
try io.print("@memory_{s} = private constant ", output, {enummap.get(&memory_symbols, code)?})
|
||||
try bytes_constant(output, enummap.get(diagnostics.Code, []u8, &diagnostics.messages, code)?, false)
|
||||
try io.write_all(output, "\n")
|
||||
}
|
||||
@@ -212,7 +212,7 @@ write_type proc(output io.Writer, module @ir.Module, type Type) void ! io.WriteE
|
||||
}
|
||||
|
||||
@hide:file
|
||||
abi_extensions :: $enummap.init_filled(ir.Type, []u8, "", {
|
||||
abi_extensions :: $enummap.init_default(ir.Type, []u8, "", {
|
||||
i8 = "signext",
|
||||
i16 = "signext",
|
||||
u8 = "zeroext",
|
||||
@@ -502,7 +502,7 @@ check_local proc(state @State, slot u32, read bool, span diagnostics.Span) void
|
||||
}
|
||||
|
||||
@hide:file
|
||||
memory_symbols :: $enummap.init_filled(diagnostics.Code, []u8, "", {
|
||||
memory_symbols :: $enummap.init_default(diagnostics.Code, []u8, "", {
|
||||
null_unwrap = "null_unwrap",
|
||||
invalid_slice_bounds = "invalid_slice_bounds",
|
||||
sentinel_mismatch = "sentinel_mismatch",
|
||||
@@ -514,17 +514,12 @@ memory_symbols :: $enummap.init_filled(diagnostics.Code, []u8, "", {
|
||||
})
|
||||
|
||||
@hide:file
|
||||
trap_symbols :: $enummap.init_filled(diagnostics.Code, []u8, "", {
|
||||
trap_symbols :: $enummap.init_default(diagnostics.Code, []u8, "", {
|
||||
integer_overflow = "overflow",
|
||||
index_out_of_bounds = "bounds",
|
||||
inactive_union_field = "inactive_union",
|
||||
})
|
||||
|
||||
@hide:file
|
||||
memory_symbol proc(code diagnostics.Code) []u8 {
|
||||
return enummap.get(&memory_symbols, code)?
|
||||
}
|
||||
|
||||
@hide:file
|
||||
trap proc(state @State, code diagnostics.Code, span diagnostics.Span) void ! io.WriteError {
|
||||
position :: location(state.line_starts, span)
|
||||
@@ -533,7 +528,7 @@ trap proc(state @State, code diagnostics.Code, span diagnostics.Span) void ! io.
|
||||
length :: message.len
|
||||
try io.print(" call void @trap(i64 -1, i64 {}, i64 {}, i64 {}, ptr @", state.output, {usize(span.source), position.line, position.column})
|
||||
if (symbol) |name| try io.write_all(state.output, name)
|
||||
else try io.print("memory_{s}", state.output, {memory_symbol(code)})
|
||||
else try io.print("memory_{s}", state.output, {enummap.get(&memory_symbols, code)?})
|
||||
try io.print(", i64 {})\n unreachable\n", state.output, {length})
|
||||
}
|
||||
|
||||
|
||||
+11
-16
@@ -183,7 +183,7 @@ lower_module proc(
|
||||
}
|
||||
|
||||
@hide:file
|
||||
base_types :: $enummap.init_filled(
|
||||
base_types :: $enummap.init_default(
|
||||
typecheck.TypeKind, ir.Type, ir.Type.invalid, {
|
||||
int = .int,
|
||||
usize = .usize,
|
||||
@@ -957,7 +957,7 @@ lower_statement proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | d
|
||||
array_type = try array_shape(state, pointer.element), span = ast.node_span(tree, id),
|
||||
})
|
||||
} else try emit(state, ir.Instruction{
|
||||
op = binary_op(expression.operator), type = type, left = old, right = right,
|
||||
op = enummap.get(&binary_ops, expression.operator)?, type = type, left = old, right = right,
|
||||
span = ast.node_span(tree, id),
|
||||
})
|
||||
_ = try emit(state, ir.Instruction{
|
||||
@@ -1056,7 +1056,7 @@ lower_statement proc(state @mut State, id ast.NodeId) void ! (mem.AllocError | d
|
||||
}
|
||||
|
||||
@hide:file
|
||||
binary_ops :: $enummap.init_filled(
|
||||
binary_ops :: $enummap.init_default(
|
||||
ast.BinaryOperator, ir.Op, ir.Op.constant, {
|
||||
add = .add,
|
||||
subtract = .subtract,
|
||||
@@ -1078,7 +1078,7 @@ binary_ops :: $enummap.init_filled(
|
||||
)
|
||||
|
||||
@hide:file
|
||||
unary_ops :: $enummap.init_filled(
|
||||
unary_ops :: $enummap.init_default(
|
||||
ast.UnaryOperator, ir.Op, ir.Op.constant, {
|
||||
negate = .negate,
|
||||
logical_not = .logical_not,
|
||||
@@ -1086,11 +1086,6 @@ unary_ops :: $enummap.init_filled(
|
||||
},
|
||||
)
|
||||
|
||||
@hide:file
|
||||
binary_op proc(operator ast.BinaryOperator) ir.Op {
|
||||
return enummap.get(&binary_ops, operator)?
|
||||
}
|
||||
|
||||
@hide:file
|
||||
lower_logical proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocError | diagnostics.CapacityError) {
|
||||
expression :: ast.binary_expr(state.tree, id)
|
||||
@@ -1892,8 +1887,8 @@ lower_builtin proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocError
|
||||
if (mem.eql(name, "maxval") or mem.eql(name, "minval")) {
|
||||
type :: lower_type(state, typecheck.type_of(state.checked, id))
|
||||
value := ir.Value{ type = type }
|
||||
if (mem.eql(name, "maxval")) value.data = ir.ValueData{ unsigned = ir.integer_max(type) }
|
||||
else value.data = ir.ValueData{ integer = ir.integer_min(type) }
|
||||
if (mem.eql(name, "maxval")) value.data = ir.ValueData{ unsigned = enummap.get(&ir.integer_maxima, type)? }
|
||||
else value.data = ir.ValueData{ integer = enummap.get(&ir.integer_minima, type)? }
|
||||
return try constant(state, value, span)
|
||||
}
|
||||
if (mem.eql(name, "divtrunc")) {
|
||||
@@ -2264,9 +2259,9 @@ lower_expression proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocEr
|
||||
}
|
||||
if (expression.operator == .negate and ir.integer_bits(type) != 0 and !ir.unsigned_integer(type) and ast.expr_kind(tree, expression.operand) == .literal_int) {
|
||||
text :: ast.literal_text(tree, expression.operand)
|
||||
magnitude_limit :: ir.integer_max(type) + 1
|
||||
magnitude_limit :: enummap.get(&ir.integer_maxima, type)? + 1
|
||||
if (integer_magnitude(text, magnitude_limit)) |magnitude| {
|
||||
if (magnitude == magnitude_limit) return try constant(state, ir.Value{ type = type, data = ir.ValueData{ integer = ir.integer_min(type) } }, span)
|
||||
if (magnitude == magnitude_limit) return try constant(state, ir.Value{ type = type, data = ir.ValueData{ integer = enummap.get(&ir.integer_minima, type)? } }, span)
|
||||
}
|
||||
}
|
||||
operand :: try lower_expression(state, expression.operand)
|
||||
@@ -2302,7 +2297,7 @@ lower_expression proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocEr
|
||||
array_type = try array_shape(state, pointer.element), span = span,
|
||||
})
|
||||
}
|
||||
op :: binary_op(expression.operator)
|
||||
op :: enummap.get(&binary_ops, expression.operator)?
|
||||
return try emit(state, ir.Instruction{ op = op, type = type, left = left, right = right, span = span })
|
||||
}
|
||||
.call: {
|
||||
@@ -2392,9 +2387,9 @@ lower_number proc(state @mut State, id ast.NodeId) ir.ValueId ! (mem.AllocError
|
||||
text :: ast.literal_text(state.tree, id)
|
||||
type :: lower_type(state, typecheck.type_of(state.checked, id))
|
||||
if (ir.integer_bits(type) != 0 and !ir.unsigned_integer(type)) {
|
||||
if (integer_magnitude(text, ir.integer_max(type))) |magnitude| return try constant(state, ir.Value{ type = type, data = ir.ValueData{ integer = i64(magnitude) } }, span)
|
||||
if (integer_magnitude(text, enummap.get(&ir.integer_maxima, type)?)) |magnitude| return try constant(state, ir.Value{ type = type, data = ir.ValueData{ integer = i64(magnitude) } }, span)
|
||||
} else if (ir.unsigned_integer(type)) {
|
||||
if (integer_magnitude(text, ir.integer_max(type))) |magnitude| return try constant(state, ir.Value{ type = type, data = ir.ValueData{ unsigned = magnitude } }, span)
|
||||
if (integer_magnitude(text, enummap.get(&ir.integer_maxima, type)?)) |magnitude| return try constant(state, ir.Value{ type = type, data = ir.ValueData{ unsigned = magnitude } }, span)
|
||||
} else {
|
||||
value :: strconv.parse_float(state.module.allocator, text) catch |err| {
|
||||
match err {
|
||||
|
||||
+12
-31
@@ -432,7 +432,7 @@ parse_loop_control proc(state @mut State, allow_else bool) ast.NodeId ! ParseErr
|
||||
|
||||
@hide:file
|
||||
parse_assignment proc(state @mut State, target ast.NodeId, allow_else bool) ast.NodeId ! ParseError {
|
||||
operator :: assignment_operator(current(state).kind)
|
||||
operator :: enummap.get(&assignment_operators, current(state).kind)
|
||||
state.next_token += 1
|
||||
value := try parse_expr(state)
|
||||
if (
|
||||
@@ -939,7 +939,7 @@ parse_function_type proc(state @mut State, start ast.Span, invalid bool) ast.Nod
|
||||
}
|
||||
|
||||
@hide:file
|
||||
type_starters :: $enummap.init_filled(lexer.TokenKind, bool, false, {
|
||||
type_starters :: $enummap.init_default(lexer.TokenKind, bool, false, {
|
||||
ident = true, open_bracket = true, struct = true, c_struct = true, opaque = true,
|
||||
enum = true, union = true, distinct = true, alias = true, match = true, if = true,
|
||||
at = true, star = true, question = true, open_paren = true, proc = true, c_func = true,
|
||||
@@ -951,7 +951,7 @@ starts_type proc(kind lexer.TokenKind) bool {
|
||||
}
|
||||
|
||||
@hide:file
|
||||
member_names :: $enummap.init_filled(lexer.TokenKind, bool, false, {
|
||||
member_names :: $enummap.init_default(lexer.TokenKind, bool, false, {
|
||||
ident = true, if = true, else = true, for = true, inline = true, while = true,
|
||||
proc = true, return = true, yield = true, break = true, continue = true,
|
||||
true = true, false = true, and = true, or = true, xor = true, mut = true,
|
||||
@@ -1388,7 +1388,7 @@ parse_expr_bp proc(state @mut State, min_bp BindingPowerScalar) ast.NodeId ! Par
|
||||
skip_operand_newlines(state)
|
||||
token := current(state)
|
||||
expression_start :: state.next_token
|
||||
left := if (prefix_binding_power(token.kind)) |right_bp| {
|
||||
left := if (enummap.get(&prefix_binding_powers, token.kind)) |right_bp| {
|
||||
state.next_token += 1
|
||||
operand :: try parse_expr_bp(state, right_bp)
|
||||
operand_span :: ast.builder_span(state.builder, operand)
|
||||
@@ -1518,7 +1518,7 @@ parse_expr_bp proc(state @mut State, min_bp BindingPowerScalar) ast.NodeId ! Par
|
||||
continue
|
||||
}
|
||||
if (token.kind == .pipe and !state.allow_bit_or) break
|
||||
if (infix_binding_power(token.kind)) |power| {
|
||||
if (enummap.get(&infix_binding_powers, token.kind)) |power| {
|
||||
if (power.left < min_bp) break
|
||||
state.next_token += 1
|
||||
right :: try parse_expr_bp(state, power.right)
|
||||
@@ -1531,7 +1531,7 @@ parse_expr_bp proc(state @mut State, min_bp BindingPowerScalar) ast.NodeId ! Par
|
||||
end = right_span.end,
|
||||
source = left_span.source,
|
||||
},
|
||||
binary_operator(token.kind),
|
||||
enummap.get(&binary_operators, token.kind)?,
|
||||
left,
|
||||
right,
|
||||
)
|
||||
@@ -1975,7 +1975,7 @@ skip_continuation_newlines proc(state @mut State) void {
|
||||
and kind != .close_bracket and kind != .open_bracket
|
||||
and kind != .range and kind != .range_inclusive
|
||||
and kind != .pipe and kind != .bang and kind != .catch
|
||||
and infix_binding_power(kind) == null
|
||||
and enummap.get(&infix_binding_powers, kind) == null
|
||||
) state.next_token = start
|
||||
}
|
||||
|
||||
@@ -2122,19 +2122,14 @@ consume_either proc(state @mut State, kinds []lexer.TokenKind) lexer.Token ! Syn
|
||||
}
|
||||
|
||||
@hide:file
|
||||
prefix_binding_powers :: $enummap.init_filled(
|
||||
prefix_binding_powers :: $enummap.init_default(
|
||||
lexer.TokenKind, BindingPowerScalar, BindingPowerScalar(0), {
|
||||
minus = BindingPowerScalar(50), bang = BindingPowerScalar(50), tilde = BindingPowerScalar(50),
|
||||
ampersand = BindingPowerScalar(50), try = BindingPowerScalar(50),
|
||||
})
|
||||
|
||||
@hide:file
|
||||
prefix_binding_power proc(kind lexer.TokenKind) ?BindingPowerScalar {
|
||||
return enummap.get(&prefix_binding_powers, kind)
|
||||
}
|
||||
|
||||
@hide:file
|
||||
infix_binding_powers :: $enummap.init_filled(lexer.TokenKind, BindingPower,
|
||||
infix_binding_powers :: $enummap.init_default(lexer.TokenKind, BindingPower,
|
||||
BindingPower{ left = BindingPowerScalar(0), right = BindingPowerScalar(0) },
|
||||
{
|
||||
or = BindingPower{ left = BindingPowerScalar(2), right = BindingPowerScalar(3) },
|
||||
@@ -2159,12 +2154,7 @@ infix_binding_powers :: $enummap.init_filled(lexer.TokenKind, BindingPower,
|
||||
)
|
||||
|
||||
@hide:file
|
||||
infix_binding_power proc(kind lexer.TokenKind) ?BindingPower {
|
||||
return enummap.get(&infix_binding_powers, kind)
|
||||
}
|
||||
|
||||
@hide:file
|
||||
binary_operators :: $enummap.init_filled(
|
||||
binary_operators :: $enummap.init_default(
|
||||
lexer.TokenKind,
|
||||
ast.BinaryOperator,
|
||||
ast.BinaryOperator.add, {
|
||||
@@ -2189,18 +2179,13 @@ binary_operators :: $enummap.init_filled(
|
||||
},
|
||||
)
|
||||
|
||||
@hide:file
|
||||
binary_operator proc(kind lexer.TokenKind) ast.BinaryOperator {
|
||||
return enummap.get(&binary_operators, kind)?
|
||||
}
|
||||
|
||||
@hide:file
|
||||
is_assignment_operator proc(kind lexer.TokenKind) bool {
|
||||
return kind == .equal or assignment_operator(kind) != null
|
||||
return kind == .equal or enummap.get(&assignment_operators, kind) != null
|
||||
}
|
||||
|
||||
@hide:file
|
||||
assignment_operators :: $enummap.init_filled(lexer.TokenKind, ast.BinaryOperator, ast.BinaryOperator.add, {
|
||||
assignment_operators :: $enummap.init_default(lexer.TokenKind, ast.BinaryOperator, ast.BinaryOperator.add, {
|
||||
plus_equal = .add,
|
||||
minus_equal = .subtract,
|
||||
star_equal = .multiply,
|
||||
@@ -2213,7 +2198,3 @@ assignment_operators :: $enummap.init_filled(lexer.TokenKind, ast.BinaryOperator
|
||||
shift_left_saturating_equal = .shift_left_saturating,
|
||||
})
|
||||
|
||||
@hide:file
|
||||
assignment_operator proc(kind lexer.TokenKind) ?ast.BinaryOperator {
|
||||
return enummap.get(&assignment_operators, kind)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import "@source/diagnostics"
|
||||
import "@source/strpool"
|
||||
import "@source/lexer"
|
||||
import "@source/ast"
|
||||
import "@source/ast/renderer"
|
||||
|
||||
parses_expression_precedence_through_tree_views test {
|
||||
strings := strpool.init(mem.c_allocator)
|
||||
@@ -2375,3 +2376,29 @@ keeps_type_composition_and_capture_pipes_with_bitwise_or test {
|
||||
try testing.expect(ast.while_stmt(&tree, ast.block_statement(&tree, body, 4)).capture != null)
|
||||
try testing.expect(ast.for_stmt(&tree, ast.block_statement(&tree, body, 5)).capture != null)
|
||||
}
|
||||
|
||||
renders_compound_updates_in_scoped_statement_bodies test {
|
||||
strings := strpool.init(mem.c_allocator)
|
||||
defer strpool.deinit(&strings)
|
||||
diagnostic_store := diagnostics.init(mem.c_allocator)
|
||||
defer diagnostics.deinit(&diagnostic_store)
|
||||
source ::
|
||||
`run proc() void {
|
||||
` value := 1
|
||||
` { value += 2 }
|
||||
` defer { value -= 3 }
|
||||
` errdefer |err| { value xor= 4 }
|
||||
` for (0..2) |item, index| value |= 5
|
||||
`}
|
||||
scan_state := lexer.init(mem.c_allocator)
|
||||
defer lexer.deinit(&scan_state)
|
||||
try lexer.scan(&scan_state, source, &strings, &diagnostic_store)
|
||||
tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store)
|
||||
defer ast.deinit(&tree)
|
||||
try testing.expect_equal(0, diagnostics.count(&diagnostic_store))
|
||||
output := renderer.init(mem.c_allocator)
|
||||
defer renderer.deinit(&output)
|
||||
try renderer.render(&output, &tree)
|
||||
expected :: "package\n function run\n declaration value := 1\n block\n assignment value += 2\n defer\n assignment value -= 3\n errdefer |err|\n assignment value xor= 4\n for 0..2 |item, index|\n assignment value |= 5\n"
|
||||
try testing.expect(mem.eql(output.bytes.items, expected))
|
||||
}
|
||||
|
||||
@@ -931,7 +931,7 @@ check_annotation proc(state @mut State, id ast.NodeId, allow_void bool) Type ! (
|
||||
match binding.kind {
|
||||
.invalid: type = invalid_type(binding.data.diagnostic)
|
||||
.builtin: {
|
||||
type.kind = builtin_kind(binding.data.builtin)
|
||||
type.kind = enummap.get(&builtin_kinds, binding.data.builtin)?
|
||||
if (binding.data.builtin == .unavailable)
|
||||
type = invalid_type(try diagnostics.add(state.diagnostics, .invalid_operand, ast.node_span(state.tree, id)))
|
||||
if (type.kind == .anyopaque and state.pointer_depth == 0) {
|
||||
@@ -1551,17 +1551,12 @@ builtin_kinds :: $enummap.init(sema.BuiltinType, TypeKind, {
|
||||
unavailable = .none,
|
||||
})
|
||||
|
||||
@hide:file
|
||||
builtin_kind proc(type sema.BuiltinType) TypeKind {
|
||||
return enummap.get(&builtin_kinds, type)?
|
||||
}
|
||||
|
||||
@hide:file
|
||||
conversion_kind proc(state @State, id ast.NodeId) TypeKind {
|
||||
callee :: ast.call_expr(state.tree, id).callee
|
||||
binding :: sema.binding(state.resolution, callee)
|
||||
if (binding.kind != .builtin) return .none
|
||||
kind :: builtin_kind(binding.data.builtin)
|
||||
kind :: enummap.get(&builtin_kinds, binding.data.builtin)?
|
||||
return if (numeric_kind(kind)) kind else .none
|
||||
}
|
||||
|
||||
@@ -1589,7 +1584,7 @@ bitwise proc(operator ast.BinaryOperator) bool {
|
||||
}
|
||||
|
||||
@hide:file
|
||||
integer_widths :: $enummap.init_filled(
|
||||
integer_widths :: $enummap.init_default(
|
||||
TypeKind, usize, usize(0), {
|
||||
i8 = 8, u8 = 8,
|
||||
i16 = 16, u16 = 16,
|
||||
@@ -2512,7 +2507,7 @@ evaluate_integer proc(state @mut State, id ast.NodeId, allow_local bool) ?Consta
|
||||
type_binding :: sema.binding(state.resolution, annotation)
|
||||
if (
|
||||
!integer_kind(state.types[usize(annotation)].kind)
|
||||
and (type_binding.kind != .builtin or !integer_kind(builtin_kind(type_binding.data.builtin)))
|
||||
and (type_binding.kind != .builtin or !integer_kind(enummap.get(&builtin_kinds, type_binding.data.builtin)?))
|
||||
) return null
|
||||
}
|
||||
return evaluate_integer(state, declaration.initializer, allow_local)
|
||||
|
||||
@@ -5297,7 +5297,7 @@ replace proc(state @mut State, target, source ast.NodeId) void ! mem.AllocError
|
||||
}
|
||||
|
||||
@hide:file
|
||||
builtin_identities :: $enummap.init_filled(
|
||||
builtin_identities :: $enummap.init_default(
|
||||
sema.BuiltinType,
|
||||
sema.BuiltinType,
|
||||
sema.BuiltinType.type, {
|
||||
|
||||
@@ -21,7 +21,7 @@ init proc(
|
||||
|
||||
#! initializes absent value slots without making their keys present.
|
||||
#! use fully initialized storage when a sparse map must be materialized at compile time.
|
||||
init_filled proc(
|
||||
init_default proc(
|
||||
$E, $V type,
|
||||
storage_default V,
|
||||
values meta.EnumFieldStruct(E, ?V, some!(null)),
|
||||
|
||||
@@ -26,7 +26,7 @@ handles_sparse_enum_get test {
|
||||
}
|
||||
|
||||
@hide:file
|
||||
sparse_optional_values :: $init_filled(TestEnum, ?i16, null, {
|
||||
sparse_optional_values :: $init_default(TestEnum, ?i16, null, {
|
||||
ident = some!(null),
|
||||
int = some!(some!(i16(42))),
|
||||
})
|
||||
|
||||
+2
-2
@@ -9045,11 +9045,11 @@ def check_typeinfo_packages(temporary, executable):
|
||||
import "@std/enummap"
|
||||
Key :: enum(i16) { absent = -3, present_null = 8, present_value = 21 }
|
||||
main proc() i16 {
|
||||
values :: $(enummap.init_filled(Key, ?i16, null, {
|
||||
values :: $(enummap.init_default(Key, ?i16, null, {
|
||||
present_null = some!(null),
|
||||
present_value = some!(some!(i16(42))),
|
||||
}))
|
||||
empty :: $(enummap.init_filled(Key, i16, i16(99), {}))
|
||||
empty :: $(enummap.init_default(Key, i16, i16(99), {}))
|
||||
if (enummap.get(&values, .absent) != null or enummap.get(&empty, .absent) != null) return -1
|
||||
payload :: enummap.get(&values, .present_null)
|
||||
if (payload == null or payload? != null) return -2
|
||||
|
||||
Reference in New Issue
Block a user