feat: support checked explicit sentinel slices
This commit is contained in:
@@ -400,6 +400,9 @@ block_reads_name :: proc(checker: ^Checker, statements: []ast.Stmt_Id, name: sym
|
||||
case .Call, .Array, .Struct_Literal, .Slice:
|
||||
append(&expr_stack, ..expr.args)
|
||||
append(&expr_stack, expr.left)
|
||||
if expr.kind == .Slice {
|
||||
append(&expr_stack, expr.right)
|
||||
}
|
||||
case .Negate, .Not, .Bit_Not, .Address, .Deref, .Field, .Unwrap, .Try, .Keyed, .Enum_Literal, .Cast:
|
||||
append(&expr_stack, expr.left)
|
||||
case .Comptime:
|
||||
@@ -743,12 +746,18 @@ comptime_slice_bytes :: proc(
|
||||
pkg: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
) -> (string, bool) {
|
||||
if expr.right != ast.INVALID_EXPR {
|
||||
return "", false
|
||||
}
|
||||
state := ct_state_make(checker, pkg, file, diagnose=false)
|
||||
defer ct_state_destroy(&state)
|
||||
value, flow, ok := ct_eval_slice_expr(&state, expr, 0)
|
||||
if !ok || flow.kind != .Normal {
|
||||
return "", false
|
||||
}
|
||||
if item, item_ok := types.container(state.values[value].type, &checker.module.types); item_ok && item.has_sentinel {
|
||||
return "", false
|
||||
}
|
||||
return ct_value_bytes(&state, value)
|
||||
}
|
||||
|
||||
@@ -2753,6 +2762,15 @@ match_inferred_type_pattern :: proc(
|
||||
values, bound, span, diagnose, depth+1,
|
||||
)
|
||||
}
|
||||
if actual_ok && actual_item.kind == .Slice {
|
||||
if pattern_item.has_sentinel && (!actual_item.has_sentinel || pattern_item.sentinel != actual_item.sentinel) {
|
||||
return false
|
||||
}
|
||||
return match_inferred_type_pattern(
|
||||
checker, function, prefix, pattern_item.child, actual_item.child,
|
||||
values, bound, span, diagnose, depth+1,
|
||||
)
|
||||
}
|
||||
}
|
||||
if !actual_ok || pattern_item.kind != actual_item.kind {
|
||||
resolved := type_from_syntax(checker, pattern, function.pkg, function.file)
|
||||
@@ -3786,6 +3804,9 @@ mark_expr_imports_used :: proc(checker: ^Checker, expr_id: ast.Expr_Id, file: as
|
||||
if expr.left != ast.INVALID_EXPR {
|
||||
append(&stack, expr.left)
|
||||
}
|
||||
if expr.kind == .Slice && expr.right != ast.INVALID_EXPR {
|
||||
append(&stack, expr.right)
|
||||
}
|
||||
case .Negate, .Not, .Bit_Not, .Address, .Deref, .Field, .Unwrap, .Try, .Keyed, .Enum_Literal, .Cast:
|
||||
append(&stack, expr.left)
|
||||
case .Comptime:
|
||||
@@ -5168,6 +5189,15 @@ infer_compound_expr :: proc(
|
||||
_ = record_demand(checker, bound, types.USIZE, locals, local_types, pkg, file)
|
||||
}
|
||||
}
|
||||
if expr.right != ast.INVALID_EXPR {
|
||||
state := ct_state_make(checker, pkg, file, diagnose=false)
|
||||
defer ct_state_destroy(&state)
|
||||
sentinel, sentinel_ok := checked_slice_sentinel(&state, expr.right, item.child)
|
||||
if !sentinel_ok {
|
||||
return types.INVALID
|
||||
}
|
||||
return types.slice(store, item.child, item.mutable, true, u64(sentinel))
|
||||
}
|
||||
preserve := item.has_sentinel && expr.args[1] == ast.INVALID_EXPR
|
||||
return types.slice(store, item.child, item.mutable, preserve, item.sentinel)
|
||||
case .Field:
|
||||
@@ -8773,6 +8803,22 @@ build_compound_expr :: proc(
|
||||
id := source.add(checker.diagnostics, expr.span, "many-item pointer slicing requires an explicit end bound")
|
||||
return invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
sentinel_expr := hir.INVALID_EXPR
|
||||
sentinel := item.sentinel
|
||||
if expr.right != ast.INVALID_EXPR {
|
||||
state := ct_state_make(checker, pkg, file)
|
||||
defer ct_state_destroy(&state)
|
||||
constant, sentinel_ok := checked_slice_sentinel(&state, expr.right, item.child)
|
||||
if !sentinel_ok {
|
||||
return invalid_hir_expr(checker, expr.span, state.diagnostic)
|
||||
}
|
||||
sentinel = u64(constant)
|
||||
sentinel_expr = add_hir_expr(checker, hir.Expr{
|
||||
kind=.Integer, span=checker.ast_module.exprs[expr.right].span, type=item.child, integer=i64(sentinel),
|
||||
target=hir.INVALID_REF, left=hir.INVALID_EXPR, right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
bounds := make([]hir.Expr_Id, 2, checker.allocator)
|
||||
bounds[0] = hir.INVALID_EXPR
|
||||
bounds[1] = hir.INVALID_EXPR
|
||||
@@ -8791,11 +8837,11 @@ build_compound_expr :: proc(
|
||||
}
|
||||
}
|
||||
}
|
||||
preserve_sentinel := item.has_sentinel && expr.args[1] == ast.INVALID_EXPR
|
||||
result_type := types.slice(store, item.child, item.mutable, preserve_sentinel, item.sentinel)
|
||||
preserve_sentinel := expr.right != ast.INVALID_EXPR || item.has_sentinel && expr.args[1] == ast.INVALID_EXPR
|
||||
result_type := types.slice(store, item.child, item.mutable, preserve_sentinel, sentinel)
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Slice, span=expr.span, type=result_type, args=bounds, left=container,
|
||||
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
target=hir.INVALID_REF, right=sentinel_expr, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Field:
|
||||
if enum_type, enum_ok := enum_type_from_field_expr(checker, expr, pkg, file, true); enum_ok {
|
||||
|
||||
+110
-10
@@ -792,6 +792,17 @@ ct_place_get :: proc(state: ^Ct_State, place_id: Ct_Place_Id) -> (Ct_Value_Id, b
|
||||
#partial switch elem.kind {
|
||||
case .Index:
|
||||
index := int(elem.index)
|
||||
if value.kind == .Array {
|
||||
item, ok := types.node(&state.checker.module.types, value.type)
|
||||
if ok && item.has_sentinel && u64(index) == item.count {
|
||||
integer := i128(item.sentinel)
|
||||
if types.is_signed(types.resolve_alias(item.child, &state.checker.module.types), state.checker.target) {
|
||||
integer = i128(i64(item.sentinel))
|
||||
}
|
||||
current = ct_add_value(state, Ct_Value{kind=.Integer, type=item.child, integer=integer})
|
||||
continue
|
||||
}
|
||||
}
|
||||
if value.kind != .Array || index < 0 || index >= len(children) {
|
||||
return INVALID_CT_VALUE, false
|
||||
}
|
||||
@@ -963,7 +974,7 @@ ct_value_can_materialize :: proc(state: ^Ct_State, id: Ct_Value_Id, depth := 0)
|
||||
store := &state.checker.module.types
|
||||
slice, slice_ok := types.node(store, value.type)
|
||||
place_id := Ct_Place_Id(value.index)
|
||||
if !slice_ok || slice.kind != .Slice || slice.mutable || value.start != 0 ||
|
||||
if !slice_ok || slice.kind != .Slice || slice.mutable ||
|
||||
place_id == INVALID_CT_PLACE || int(place_id) >= len(state.places) {
|
||||
return false
|
||||
}
|
||||
@@ -977,7 +988,7 @@ ct_value_can_materialize :: proc(state: ^Ct_State, id: Ct_Value_Id, depth := 0)
|
||||
}
|
||||
root := state.values[root_id]
|
||||
array, array_ok := types.node(store, root.type)
|
||||
return array_ok && array.kind == .Array && u64(value.count) == array.count &&
|
||||
return array_ok && array.kind == .Array && u64(value.start)+u64(value.count) <= array.count &&
|
||||
types.equal(slice.child, array.child) && ct_value_can_materialize(state, root_id, depth+1)
|
||||
case .Range, .Array, .Struct, .Optional_Some, .Fallible:
|
||||
for child in ct_child_slice(state, value) {
|
||||
@@ -1065,7 +1076,7 @@ ct_materialize_array_slice :: proc(
|
||||
store := &state.checker.module.types
|
||||
slice, slice_ok := types.node(store, value.type)
|
||||
place_id := Ct_Place_Id(value.index)
|
||||
if !slice_ok || slice.kind != .Slice || slice.mutable || value.start != 0 ||
|
||||
if !slice_ok || slice.kind != .Slice || slice.mutable ||
|
||||
place_id == INVALID_CT_PLACE || int(place_id) >= len(state.places) {
|
||||
return hir.INVALID_EXPR, false
|
||||
}
|
||||
@@ -1079,7 +1090,7 @@ ct_materialize_array_slice :: proc(
|
||||
}
|
||||
root := state.values[root_id]
|
||||
array, array_ok := types.node(store, root.type)
|
||||
if !array_ok || array.kind != .Array || u64(value.count) != array.count ||
|
||||
if !array_ok || array.kind != .Array || u64(value.start)+u64(value.count) > array.count ||
|
||||
!types.equal(slice.child, array.child) {
|
||||
return hir.INVALID_EXPR, false
|
||||
}
|
||||
@@ -1093,7 +1104,28 @@ ct_materialize_array_slice :: proc(
|
||||
if !ok {
|
||||
return hir.INVALID_EXPR, false
|
||||
}
|
||||
return coerce_expr(state.checker, address, value.type, span), true
|
||||
checker := state.checker
|
||||
bounds := make([]hir.Expr_Id, 2, checker.allocator)
|
||||
endpoints := [2]u32{value.start, value.start+value.count}
|
||||
for integer, index in endpoints {
|
||||
bounds[index] = add_hir_expr(checker, hir.Expr{
|
||||
kind=.Integer, span=span, type=types.USIZE, integer=i64(integer),
|
||||
target=hir.INVALID_REF, left=hir.INVALID_EXPR, right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
sentinel_expr := hir.INVALID_EXPR
|
||||
if slice.has_sentinel {
|
||||
sentinel_expr = add_hir_expr(checker, hir.Expr{
|
||||
kind=.Integer, span=span, type=slice.child, integer=i64(slice.sentinel),
|
||||
target=hir.INVALID_REF, left=hir.INVALID_EXPR, right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Slice, span=span, type=value.type, args=bounds, left=address, right=sentinel_expr,
|
||||
target=hir.INVALID_REF, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
}), true
|
||||
}
|
||||
|
||||
ct_materialize_value :: proc(
|
||||
@@ -1223,7 +1255,7 @@ ct_materialize_value :: proc(
|
||||
return result
|
||||
}
|
||||
if state.diagnostic == source.INVALID_DIAGNOSTIC {
|
||||
state.diagnostic = source.add(checker.diagnostics, span, "only immutable full-array comptime slices can materialize as runtime memory")
|
||||
state.diagnostic = source.add(checker.diagnostics, span, "only immutable array-backed comptime slices can materialize as runtime memory")
|
||||
}
|
||||
return invalid_hir_expr(checker, span, state.diagnostic, value.type)
|
||||
case .Function:
|
||||
@@ -1948,6 +1980,22 @@ ct_eval_struct_expr :: proc(state: ^Ct_State, expr: ast.Expr, expected: types.Ty
|
||||
return ct_add_value(state, Ct_Value{kind=.Struct, type=struct_type, start=start, count=u32(len(values)), active=active_field}), ct_flow(.Normal), true
|
||||
}
|
||||
|
||||
checked_slice_sentinel :: proc(state: ^Ct_State, id: ast.Expr_Id, child: types.Type) -> (i128, bool) {
|
||||
checker := state.checker
|
||||
span := checker.ast_module.exprs[id].span
|
||||
previous_error := state.error
|
||||
constant := eval_integer_constant_in_state(state, id, allow_bool=false)
|
||||
state.error = previous_error
|
||||
if constant.kind != .Value {
|
||||
return 0, ct_fail(state, .Not_Comptime, span, "slice sentinel must be a compile-time integer")
|
||||
}
|
||||
representation := types.resolve_alias(child, &checker.module.types)
|
||||
if !types.is_concrete_integer(representation) || !fits_integer_type(constant.value, representation, checker.target) {
|
||||
return 0, ct_failf(state, .Not_Comptime, span, "slice sentinel value does not fit element type %s", type_label(checker, child))
|
||||
}
|
||||
return constant.value, true
|
||||
}
|
||||
|
||||
ct_eval_slice_expr :: proc(state: ^Ct_State, expr: ast.Expr, depth: int) -> (Ct_Value_Id, Ct_Flow, bool) {
|
||||
checker := state.checker
|
||||
store := &checker.module.types
|
||||
@@ -1958,6 +2006,7 @@ ct_eval_slice_expr :: proc(state: ^Ct_State, expr: ast.Expr, depth: int) -> (Ct_
|
||||
container_place := INVALID_CT_PLACE
|
||||
container_start := 0
|
||||
container_len := 0
|
||||
bounded := true
|
||||
item: types.Node
|
||||
item_ok := false
|
||||
if base_value != INVALID_CT_VALUE && int(base_value) < len(state.values) {
|
||||
@@ -1967,6 +2016,25 @@ ct_eval_slice_expr :: proc(state: ^Ct_State, expr: ast.Expr, depth: int) -> (Ct_
|
||||
container_start = int(base.start)
|
||||
container_len = int(base.count)
|
||||
item, item_ok = types.node(store, base.type)
|
||||
} else if base.kind == .Array && checker.ast_module.exprs[expr.left].kind == .Array && expr.right != ast.INVALID_EXPR {
|
||||
item, item_ok = types.node(store, base.type)
|
||||
container_place = ct_add_place(state, ct_add_cell(state, base_value, false), base.type, false)
|
||||
container_len = int(base.count)
|
||||
item.mutable = false
|
||||
} else if base.kind == .String && expr.right != ast.INVALID_EXPR && base.index < u64(len(checker.ast_module.strings)) {
|
||||
text := checker.ast_module.strings[base.index]
|
||||
array_type := types.array(store, types.U8, u64(len(text)), false, true, 0)
|
||||
children := make([]Ct_Value_Id, len(text), checker.allocator)
|
||||
defer delete(children, checker.allocator)
|
||||
for byte, index in transmute([]u8)text {
|
||||
children[index] = ct_add_value(state, Ct_Value{kind=.Integer, type=types.U8, integer=i128(byte)})
|
||||
}
|
||||
child_start := u32(len(state.children))
|
||||
append(&state.children, ..children)
|
||||
array_value := ct_add_value(state, Ct_Value{kind=.Array, type=array_type, start=child_start, count=u32(len(text))})
|
||||
container_place = ct_add_place(state, ct_add_cell(state, array_value, false), array_type, false)
|
||||
container_len = len(text)
|
||||
item, item_ok = types.container(base.type, store)
|
||||
} else if base.kind == .Pointer {
|
||||
pointer_item, pointer_ok := types.node(store, base.type)
|
||||
array_item: types.Node
|
||||
@@ -1980,6 +2048,15 @@ ct_eval_slice_expr :: proc(state: ^Ct_State, expr: ast.Expr, depth: int) -> (Ct_
|
||||
item = array_item
|
||||
item.mutable = pointer_item.mutable && array_item.mutable
|
||||
item_ok = true
|
||||
} else if pointer_ok && pointer_item.many {
|
||||
if expr.args[1] == ast.INVALID_EXPR {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "many-item pointer slicing requires an explicit end bound")
|
||||
}
|
||||
container_place = Ct_Place_Id(base.index)
|
||||
container_start = int(base.active)
|
||||
item = pointer_item
|
||||
item_ok = true
|
||||
bounded = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2031,11 +2108,31 @@ ct_eval_slice_expr :: proc(state: ^Ct_State, expr: ast.Expr, depth: int) -> (Ct_
|
||||
end = int(integer)
|
||||
}
|
||||
}
|
||||
if start < 0 || end < start || end > container_len {
|
||||
if start < 0 || end < start || bounded && end > container_len || container_start < 0 {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "comptime slice bounds out of range")
|
||||
}
|
||||
preserve_sentinel := item.has_sentinel && expr.args[1] == ast.INVALID_EXPR
|
||||
result_type := types.slice(store, item.child, item.mutable, preserve_sentinel, item.sentinel)
|
||||
sentinel := item.sentinel
|
||||
if expr.right != ast.INVALID_EXPR {
|
||||
constant, sentinel_ok := checked_slice_sentinel(state, expr.right, item.child)
|
||||
if !sentinel_ok {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), false
|
||||
}
|
||||
if bounded && (end == container_len && !item.has_sentinel) {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "comptime sentinel slice endpoint out of range")
|
||||
}
|
||||
endpoint := ct_extend_place(state, container_place, Ct_Path_Elem{kind=.Index, index=u32(container_start+end)}, item.child, false)
|
||||
value, value_ok := ct_place_get(state, endpoint)
|
||||
if !value_ok {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "comptime sentinel slice endpoint out of range")
|
||||
}
|
||||
actual, actual_ok := ct_integer_value(state, value)
|
||||
if !actual_ok || actual != constant {
|
||||
return INVALID_CT_VALUE, ct_flow(.Normal), ct_fail(state, .Not_Comptime, expr.span, "comptime slice sentinel mismatch")
|
||||
}
|
||||
sentinel = u64(constant)
|
||||
}
|
||||
preserve_sentinel := expr.right != ast.INVALID_EXPR || item.has_sentinel && expr.args[1] == ast.INVALID_EXPR
|
||||
result_type := types.slice(store, item.child, item.mutable, preserve_sentinel, sentinel)
|
||||
return ct_add_value(state, Ct_Value{
|
||||
kind=.Slice, type=result_type, index=u64(container_place),
|
||||
start=u32(container_start+start), count=u32(end-start),
|
||||
@@ -5973,7 +6070,7 @@ eval_integer_constant_in_context :: proc(
|
||||
return eval_integer_constant_in_state(&state, expr_id, depth)
|
||||
}
|
||||
|
||||
eval_integer_constant_in_state :: proc(state: ^Ct_State, expr_id: ast.Expr_Id, depth := 0) -> Constant {
|
||||
eval_integer_constant_in_state :: proc(state: ^Ct_State, expr_id: ast.Expr_Id, depth := 0, allow_bool := true) -> Constant {
|
||||
if depth > 64 || expr_id == ast.INVALID_EXPR || int(expr_id) >= len(state.checker.ast_module.exprs) {
|
||||
return Constant{kind=.Not_Constant}
|
||||
}
|
||||
@@ -5994,6 +6091,9 @@ eval_integer_constant_in_state :: proc(state: ^Ct_State, expr_id: ast.Expr_Id, d
|
||||
}
|
||||
return Constant{kind=.Not_Constant}
|
||||
}
|
||||
if !allow_bool && state.values[value].kind != .Integer {
|
||||
return Constant{kind=.Not_Constant}
|
||||
}
|
||||
integer, integer_ok := ct_integer_value(state, value)
|
||||
if !integer_ok {
|
||||
return Constant{kind=.Not_Constant}
|
||||
|
||||
+18
-1
@@ -1556,6 +1556,11 @@ emit_instruction_stream :: proc(
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid slice container")
|
||||
continue
|
||||
}
|
||||
check_sentinel := instruction.b != ir.INVALID_INSTRUCTION
|
||||
if check_sentinel && !valid_value(instructions, instruction.b, item.child, &emitter.module.types) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid slice sentinel")
|
||||
continue
|
||||
}
|
||||
pointer_name := fmt.tprintf("%%v%d", instruction.a)
|
||||
length_name := fmt.tprintf("%d", item.count)
|
||||
if item.kind == .Array {
|
||||
@@ -1597,13 +1602,25 @@ emit_instruction_stream :: proc(
|
||||
if item.kind == .Pointer {
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_ok%d = or i1 false, %%slice_order%d\n", instruction_index, instruction_index)
|
||||
} else {
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_end_ok%d = icmp ule i64 %s, %s\n", instruction_index, end_name, length_name)
|
||||
comparison := "ult" if check_sentinel && !item.has_sentinel else "ule"
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_end_ok%d = icmp %s i64 %s, %s\n", instruction_index, comparison, end_name, length_name)
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_ok%d = and i1 %%slice_order%d, %%slice_end_ok%d\n", instruction_index, instruction_index, instruction_index)
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " br i1 %%slice_ok%d, label %%slice_continue%d, label %%slice_trap%d\nslice_trap%d:\n", instruction_index, instruction_index, instruction_index, instruction_index)
|
||||
message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "slice bounds out of range")
|
||||
emit_trap_call(emitter, message)
|
||||
fmt.sbprintf(&emitter.builder, " unreachable\nslice_continue%d:\n", instruction_index)
|
||||
if check_sentinel {
|
||||
element_type := llvm_type(item.child, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_sentinel_ptr%d = getelementptr %s, ptr %s, i64 %s\n", instruction_index, element_type, pointer_name, end_name)
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_sentinel_value%d = load %s, ptr %%slice_sentinel_ptr%d\n", instruction_index, element_type, instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_sentinel_ok%d = icmp eq %s %%slice_sentinel_value%d, ", instruction_index, element_type, instruction_index)
|
||||
write_operand(&emitter.builder, instructions, instruction.b, item.child, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, "\n br i1 %%slice_sentinel_ok%d, label %%slice_sentinel_continue%d, label %%slice_sentinel_trap%d\nslice_sentinel_trap%d:\n", instruction_index, instruction_index, instruction_index, instruction_index)
|
||||
sentinel_message := diagnostic_message(emitter, source.INVALID_DIAGNOSTIC, instruction.span, "slice sentinel mismatch")
|
||||
emit_trap_call(emitter, sentinel_message)
|
||||
fmt.sbprintf(&emitter.builder, " unreachable\nslice_sentinel_continue%d:\n", instruction_index)
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_start%d = getelementptr %s, ptr %s, i64 %s\n", instruction_index, llvm_type(item.child, &emitter.module.types), pointer_name, start_name)
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_result%d = insertvalue %s poison, ptr %%slice_start%d, 0\n", instruction_index, llvm_type(instruction.type, &emitter.module.types), instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_result_len%d = sub i64 %s, %s\n", instruction_index, end_name, start_name)
|
||||
|
||||
@@ -306,9 +306,13 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
|
||||
args[index] = lower_nested_expr(state, arg)
|
||||
}
|
||||
}
|
||||
checked_sentinel := ir.INVALID_INSTRUCTION
|
||||
if expr.right != hir.INVALID_EXPR {
|
||||
checked_sentinel = lower_nested_expr(state, expr.right)
|
||||
}
|
||||
return append_instruction(state, ir.Instruction{
|
||||
op=.Slice, span=expr.span, type=expr.type, args=args,
|
||||
target=ir.INVALID_REF, a=container, b=ir.INVALID_INSTRUCTION,
|
||||
target=ir.INVALID_REF, a=container, b=checked_sentinel,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Unwrap:
|
||||
|
||||
@@ -1450,6 +1450,7 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
|
||||
skip_newlines(parser)
|
||||
start_expr := ast.INVALID_EXPR
|
||||
end_expr := ast.INVALID_EXPR
|
||||
sentinel_expr := ast.INVALID_EXPR
|
||||
slicing := false
|
||||
if _, ok := allow(parser, .Range); ok {
|
||||
slicing = true
|
||||
@@ -1463,12 +1464,19 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
|
||||
}
|
||||
}
|
||||
skip_newlines(parser)
|
||||
if slicing && current(parser).kind != .Right_Bracket {
|
||||
if slicing && current(parser).kind != .Right_Bracket && current(parser).kind != .Semicolon {
|
||||
parser.range_disabled += 1
|
||||
end_expr = parse_expression_bp(parser, 0, nesting+1)
|
||||
parser.range_disabled -= 1
|
||||
skip_newlines(parser)
|
||||
}
|
||||
if slicing {
|
||||
if _, ok := allow(parser, .Semicolon); ok {
|
||||
skip_newlines(parser)
|
||||
sentinel_expr = parse_expression_bp(parser, 0, nesting+1)
|
||||
skip_newlines(parser)
|
||||
}
|
||||
}
|
||||
end_token, ok := allow(parser, .Right_Bracket)
|
||||
if !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected ']' after index or slice")
|
||||
@@ -1485,7 +1493,7 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
|
||||
span=span_from(left_expr.span, end_token.span),
|
||||
args=args,
|
||||
left=left,
|
||||
right=ast.INVALID_EXPR,
|
||||
right=sentinel_expr,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user