for loops

This commit is contained in:
2026-06-22 20:11:18 +02:00
parent 380b5943b3
commit 27f42dd253
15 changed files with 1369 additions and 22 deletions
+8
View File
@@ -92,6 +92,7 @@ Expr_Kind :: enum u8 {
Ge,
And,
Or,
Range,
Call,
}
@@ -104,6 +105,7 @@ Expr :: struct {
left: Expr_Id,
right: Expr_Id,
diagnostic: source.Diagnostic_Id,
parenthesized: bool,
kind: Expr_Kind,
}
@@ -121,14 +123,17 @@ Stmt_Kind :: enum u8 {
Expression,
If,
While,
For,
}
Stmt :: struct {
kind: Stmt_Kind,
span: source.Span,
name: symbol.Id,
index_name: symbol.Id,
type: Type_Syntax,
immutable: bool,
pointer_capture: bool,
target: Expr_Id,
expr: Expr_Id,
// `If` statements use `expr` as the condition, `body` as the then-block, and
@@ -136,6 +141,9 @@ Stmt :: struct {
// `else_body` holding a single nested `If` statement.
// `While` statements use `expr` as the condition, `body` as the loop body,
// and `update` as the optional post-iteration statement.
// `For` statements use `expr` as the iterable, `name` as the item capture,
// `index_name` as the optional index capture, and `pointer_capture` to
// distinguish `|@item|` from copy capture.
body: []Stmt_Id,
else_body: []Stmt_Id,
update: Stmt_Id,
+208 -6
View File
@@ -611,7 +611,7 @@ mark_expr_imports_used :: proc(checker: ^Checker, expr_id: ast.Expr_Id, file: as
}
case .Negate, .Not, .Address, .Deref, .Field, .Unwrap, .Keyed:
append(&stack, expr.left)
case .Add, .Index, .Orelse, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or:
case .Add, .Index, .Orelse, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range:
append(&stack, expr.left, expr.right)
case .Invalid, .Integer, .Float, .String, .Bool, .None, .Name:
}
@@ -638,6 +638,9 @@ mark_block_imports_used :: proc(checker: ^Checker, statements: []ast.Stmt_Id, fi
update := [1]ast.Stmt_Id{statement.update}
mark_block_imports_used(checker, update[:], file)
}
case .For:
mark_expr_imports_used(checker, statement.expr, file)
mark_block_imports_used(checker, statement.body, file)
case .Invalid:
}
}
@@ -1028,6 +1031,23 @@ infer_compound_expr :: proc(
_ = infer_nested_expr(checker, expr.left, locals, pkg, file, demanded)
_ = infer_nested_expr(checker, expr.right, locals, pkg, file, demanded)
return types.BOOL
case .Range:
left := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded)
right := infer_nested_expr(checker, expr.right, locals, pkg, file, demanded)
left_const := eval_constant(checker, expr.left)
right_const := eval_constant(checker, expr.right)
child := types.INVALID
if left_const.kind == .Value && right_const.kind != .Value {
child = right
} else if right_const.kind == .Value && left_const.kind != .Value {
child = left
} else {
child = types.widest(left, right)
}
if !types.is_concrete_integer(child) {
return types.INVALID
}
return types.range(store, child)
case .String:
return string_literal_type(checker, expr.integer)
case .Array:
@@ -1165,7 +1185,7 @@ infer_expr :: proc(
_ = pop(&stack)
case .String, .Array, .None, .Address, .Deref, .Index, .Slice,
.Field, .Unwrap, .Orelse, .Struct_Literal, .Keyed,
.Bool, .Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or:
.Bool, .Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range:
last = infer_compound_expr(checker, expr, locals, pkg, file, demanded)
_ = pop(&stack)
case .Name:
@@ -1413,7 +1433,7 @@ infer_statements :: proc(
returned := infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded)
if !types.is_valid(result^) {
result^ = returned
} else {
} else if !types.equal(result^, returned) {
result^ = types.widest(result^, returned)
}
}
@@ -1438,6 +1458,29 @@ infer_statements :: proc(
update := [1]ast.Stmt_Id{statement.update}
infer_statements(checker, update[:], locals, pkg, file, demanded, result)
}
case .For:
iterable_type := infer_expr(checker, statement.expr, locals^[:], pkg, file, demanded)
capture_start := len(locals^)
capture_type := types.INVALID
if types.is_range(iterable_type, &checker.module.types) {
capture_type = types.child_type(iterable_type, &checker.module.types)
} else {
item, ok := sequence_item(iterable_type, &checker.module.types)
if ok {
capture_type = item.child
if statement.pointer_capture {
capture_type = types.pointer(&checker.module.types, item.child, item.mutable, false)
}
}
}
if symbol.is_valid(statement.name) {
append(locals, Infer_Local{name=statement.name, type=capture_type})
}
if symbol.is_valid(statement.index_name) {
append(locals, Infer_Local{name=statement.index_name, type=types.USIZE})
}
infer_statements(checker, statement.body, locals, pkg, file, demanded, result)
resize(locals, capture_start)
}
}
resize(locals, scope_start)
@@ -1899,6 +1942,19 @@ hir_is_location :: proc(checker: ^Checker, expr_id: hir.Expr_Id) -> bool {
return false
}
sequence_item :: proc(value: types.Type, store: ^types.Store) -> (types.Node, bool) {
item, ok := types.node(store, value)
if ok && (item.kind == .Array || item.kind == .Slice) {
return item, true
}
pointer, array, pointer_ok := types.array_pointer(value, store)
if pointer_ok {
array.mutable = pointer.mutable && array.mutable
return array, true
}
return {}, false
}
find_struct_field :: proc(checker: ^Checker, struct_type: types.Type, name: symbol.Id) -> (int, types.Field, bool) {
for field, index in types.fields_for(&checker.module.types, struct_type) {
if field.name == u32(name) {
@@ -2192,6 +2248,46 @@ build_compound_expr :: proc(
kind=.Orelse, span=expr.span, type=child, left=optional, right=fallback,
target=hir.INVALID_REF, diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Range:
expected_child := types.INVALID
if types.is_range(expected, store) {
expected_child = types.child_type(expected, store)
}
left_const := eval_constant(checker, expr.left)
right_const := eval_constant(checker, expr.right)
left, right: hir.Expr_Id
if types.is_valid(expected_child) {
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, expected_child, pkg, file)
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, expected_child, pkg, file)
} else if right_const.kind == .Value && left_const.kind != .Value {
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, checker.module.exprs[left].type, pkg, file)
} else if left_const.kind == .Value && right_const.kind != .Value {
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, types.INVALID, pkg, file)
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, checker.module.exprs[right].type, pkg, file)
} else {
left = build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
right = build_nested_expr(checker, expr.right, locals, global_reads, calls, types.INVALID, pkg, file)
}
child := expected_child
if !types.is_valid(child) {
child = types.widest(checker.module.exprs[left].type, checker.module.exprs[right].type)
}
if !types.is_concrete_integer(child) {
id := source.add(checker.diagnostics, expr.span, "range bounds must be compatible concrete integers")
return invalid_hir_expr(checker, expr.span, id)
}
left = coerce_expr(checker, left, child, checker.module.exprs[left].span)
right = coerce_expr(checker, right, child, checker.module.exprs[right].span)
args := make([]hir.Expr_Id, 2, checker.allocator)
args[0] = left
args[1] = right
return add_hir_expr(checker, hir.Expr{
kind=.Range, span=expr.span, type=types.range(store, child),
integer=i64(expr.integer), args=args,
target=hir.INVALID_REF, left=hir.INVALID_EXPR, right=hir.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Bool:
return add_hir_expr(checker, hir.Expr{
kind=.Bool, span=expr.span, type=types.BOOL, integer=i64(expr.integer),
@@ -2391,7 +2487,7 @@ build_expr :: proc(
switch expr.kind {
case .String, .Array, .None, .Address, .Deref, .Index, .Slice,
.Field, .Unwrap, .Orelse, .Struct_Literal, .Keyed,
.Bool, .Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or:
.Bool, .Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range:
last = build_compound_expr(
checker, expr, locals, global_reads, calls, frame.expected, pkg, file,
)
@@ -2849,11 +2945,16 @@ make_link_name :: proc(checker: ^Checker, id: Spec_Id) -> string {
return fmt.aprintf("%s", strings.to_string(builder), allocator = checker.allocator)
}
build_block :: proc(ctx: ^Build_Ctx, statements: []ast.Stmt_Id) -> []hir.Stmt_Id {
build_block :: proc(
ctx: ^Build_Ctx,
statements: []ast.Stmt_Id,
duplicate_scope_start := -1,
) -> []hir.Stmt_Id {
checker := ctx.checker
body: [dynamic]hir.Stmt_Id
body.allocator = checker.allocator
scope_start := len(ctx.locals^)
duplicate_start := scope_start if duplicate_scope_start < 0 else duplicate_scope_start
for statement_id in statements {
statement := checker.ast_module.statements[statement_id]
switch statement.kind {
@@ -2876,7 +2977,7 @@ build_block :: proc(ctx: ^Build_Ctx, statements: []ast.Stmt_Id) -> []hir.Stmt_Id
value = invalid_hir_expr(checker, statement.span, id)
value_type = types.INVALID
}
if _, found := find_build_local(ctx.locals^[scope_start:], statement.name); found {
if _, found := find_build_local(ctx.locals^[duplicate_start:], statement.name); found {
id := source.addf(
checker.diagnostics, statement.span,
"duplicate local '%s'", symbol_text(checker, statement.name),
@@ -3126,6 +3227,107 @@ build_block :: proc(ctx: ^Build_Ctx, statements: []ast.Stmt_Id) -> []hir.Stmt_Id
diagnostic=source.INVALID_DIAGNOSTIC,
})
ctx.problematic^ = ctx.problematic^ || checker.module.exprs[condition].kind == .Invalid
case .For:
iterable := build_expr(
checker, statement.expr, ctx.locals^[:], ctx.global_reads, ctx.calls,
types.INVALID, ctx.pkg, ctx.file,
)
iterable_type := checker.module.exprs[iterable].type
capture_type := types.I64
iterator_type := types.INVALID
valid_loop := checker.module.exprs[iterable].kind != .Invalid
diagnostic := source.INVALID_DIAGNOSTIC
is_range := types.is_range(iterable_type, &checker.module.types)
if is_range {
capture_type = types.child_type(iterable_type, &checker.module.types)
if statement.pointer_capture {
diagnostic = source.add(checker.diagnostics, statement.span, "range loops do not support pointer captures")
valid_loop = false
}
if symbol.is_valid(statement.index_name) {
diagnostic = source.add(checker.diagnostics, statement.span, "range loops do not support index captures")
valid_loop = false
}
} else {
item, ok := sequence_item(iterable_type, &checker.module.types)
if !ok {
diagnostic = source.add(
checker.diagnostics,
statement.span,
"for-loop iterable must be a range, array, slice, or pointer-to-array",
)
valid_loop = false
} else {
iterator_type = types.pointer(
&checker.module.types,
item.child,
item.mutable,
true,
item.has_sentinel,
item.sentinel,
)
capture_type = item.child
if statement.pointer_capture {
_, _, array_pointer_ok := types.array_pointer(iterable_type, &checker.module.types)
if !types.is_slice(iterable_type, &checker.module.types) && !array_pointer_ok {
diagnostic = source.add(
checker.diagnostics,
statement.span,
"pointer capture over an array requires a pointer-to-array such as '&items'",
)
valid_loop = false
}
capture_type = types.pointer(&checker.module.types, item.child, item.mutable, false)
}
}
}
capture_start := len(ctx.locals^)
item_local := hir.local_id(len(ctx.hir_locals^))
append(ctx.hir_locals, hir.Local{name=statement.name, type=capture_type, mutable=false})
append(ctx.locals, Build_Local{name=statement.name, type=capture_type, mutable=false, id=item_local})
index_local := hir.INVALID_LOCAL
if symbol.is_valid(statement.index_name) {
if statement.index_name == statement.name {
diagnostic = source.add(checker.diagnostics, statement.span, "for-loop captures must have distinct names")
valid_loop = false
} else {
index_local = hir.local_id(len(ctx.hir_locals^))
append(ctx.hir_locals, hir.Local{name=statement.index_name, type=types.USIZE, mutable=false})
append(ctx.locals, Build_Local{name=statement.index_name, type=types.USIZE, mutable=false, id=index_local})
}
}
loop_body := build_block(ctx, statement.body, capture_start)
resize(ctx.locals, capture_start)
append(&body, hir.stmt_id(len(checker.module.statements)))
if valid_loop {
append(&checker.module.statements, hir.Stmt{
kind=.For,
span=statement.span,
local=item_local,
index_local=index_local,
expr=iterable,
iterator_type=iterator_type,
pointer_capture=statement.pointer_capture,
then_body=loop_body,
update=hir.INVALID_STMT,
target=hir.INVALID_EXPR,
diagnostic=source.INVALID_DIAGNOSTIC,
})
} else {
delete(loop_body, checker.allocator)
append(&checker.module.statements, hir.Stmt{
kind=.Trap,
span=statement.span,
local=hir.INVALID_LOCAL,
index_local=hir.INVALID_LOCAL,
expr=hir.INVALID_EXPR,
target=hir.INVALID_EXPR,
diagnostic=diagnostic,
})
ctx.problematic^ = true
}
case .Invalid:
append(&body, hir.stmt_id(len(checker.module.statements)))
append(&checker.module.statements, hir.Stmt{
+8
View File
@@ -111,6 +111,7 @@ Expr_Kind :: enum u8 {
Ge,
And,
Or,
Range,
Call,
}
@@ -142,18 +143,25 @@ Stmt_Kind :: enum u8 {
Trap,
If,
While,
For,
}
Stmt :: struct {
kind: Stmt_Kind,
span: source.Span,
local: Local_Id,
index_local: Local_Id,
target: Expr_Id,
expr: Expr_Id,
iterator_type: types.Type,
pointer_capture: bool,
// `If` statements use `expr` as the condition and `then_body`/`else_body` as
// the branch statement lists.
// `While` statements use `expr` as the condition, `then_body` as the loop
// body, and `update` as the optional post-iteration statement.
// `For` statements use `expr` as the iterable, `local` as the item capture,
// `index_local` as the optional sequence index, and `iterator_type` as the
// normalized many-item pointer type for sequence iteration.
then_body: []Stmt_Id,
else_body: []Stmt_Id,
update: Stmt_Id,
+2
View File
@@ -84,6 +84,8 @@ Opcode :: enum u8 {
Slice,
Length,
Slice_Ptr,
Extract,
Select,
Unwrap,
Optional_Is_Some,
Optional_Value,
+4
View File
@@ -27,6 +27,7 @@ keyword_kind :: proc(text: string) -> token.Kind {
case "or": return .Keyword_Or
case "if": return .Keyword_If
case "while": return .Keyword_While
case "for": return .Keyword_For
case "else": return .Keyword_Else
case "true": return .Keyword_True
case "false": return .Keyword_False
@@ -162,6 +163,9 @@ lex :: proc(
if cursor < len(bytes) && bytes[cursor] == '.' {
cursor += 1
append_token(&stream, source_file, .Ellipsis, start, cursor)
} else if cursor < len(bytes) && bytes[cursor] == '=' {
cursor += 1
append_token(&stream, source_file, .Range_Inclusive, start, cursor)
} else {
append_token(&stream, source_file, .Range, start, cursor)
}
+59 -5
View File
@@ -146,6 +146,10 @@ llvm_type :: proc(value: types.Type, store: ^types.Store = nil) -> string {
return "ptr"
case .Slice:
return "{ ptr, i64 }"
case .Range:
item, _ := types.node(store, value)
child := llvm_type(item.child, store)
return fmt.tprintf("{{ %s, %s, i1 }}", child, child)
case .Array:
item, _ := types.node(store, value)
return fmt.tprintf("[%d x %s]", types.physical_count(value, store), llvm_type(item.child, store))
@@ -232,7 +236,8 @@ valid_value :: proc(
}
switch instructions[value_id].op {
case .Param, .Const, .String, .Aggregate, .None, .Optional_Some,
.Load_Global, .Function_Address, .Address_Of, .Load, .Slice, .Length, .Slice_Ptr, .Unwrap,
.Load_Global, .Function_Address, .Address_Of, .Load, .Slice, .Length, .Slice_Ptr,
.Extract, .Select, .Unwrap,
.Optional_Is_Some, .Optional_Value, .Orelse,
.Widen, .C_Vararg_Promote, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer,
.Neg_Checked, .Add_Checked, .Pointer_Add, .Not, .Compare, .Call:
@@ -551,6 +556,8 @@ emit_instruction_stream :: proc(
expected_count = int(item.field_count)
} else if ok && item.kind == .Union {
expected_count = 1
} else if ok && item.kind == .Range {
expected_count = 3
} else {
emit_recovery_value(emitter, instruction_index, instruction, "invalid aggregate type")
continue
@@ -585,6 +592,8 @@ emit_instruction_stream :: proc(
element_type := item.child
if item.kind == .Struct {
element_type = types.fields_for(&emitter.module.types, instruction.type)[arg_index].type
} else if item.kind == .Range && arg_index == 2 {
element_type = types.BOOL
}
final := arg_index == total-1
if final {
@@ -924,6 +933,46 @@ emit_instruction_stream :: proc(
} else {
emit_recovery_value(emitter, instruction_index, instruction, "invalid container pointer")
}
case .Extract:
if !valid_instruction(instructions, instruction.a) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid aggregate extraction")
continue
}
aggregate_type := instructions[instruction.a].type
item, ok := types.node(&emitter.module.types, aggregate_type)
field_index := int(instruction.integer)
expected_type := types.INVALID
if ok && item.kind == .Range && field_index >= 0 && field_index < 3 {
expected_type = types.BOOL if field_index == 2 else item.child
}
if !types.equal(expected_type, instruction.type) ||
!valid_value(instructions, instruction.a, aggregate_type, &emitter.module.types) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid aggregate extraction")
continue
}
fmt.sbprintf(
&emitter.builder,
" %%v%d = extractvalue %s %%v%d, %d\n",
instruction_index,
llvm_type(aggregate_type, &emitter.module.types),
instruction.a,
field_index,
)
case .Select:
if len(instruction.args) != 2 ||
!valid_value(instructions, instruction.a, types.BOOL, &emitter.module.types) ||
!valid_value(instructions, instruction.args[0], instruction.type, &emitter.module.types) ||
!valid_value(instructions, instruction.args[1], instruction.type, &emitter.module.types) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid selection")
continue
}
fmt.sbprintf(&emitter.builder, " %%v%d = select i1 ", instruction_index)
write_operand(&emitter.builder, instructions, instruction.a, types.BOOL, &emitter.module.types)
fmt.sbprintf(&emitter.builder, ", %s ", llvm_type(instruction.type, &emitter.module.types))
write_operand(&emitter.builder, instructions, instruction.args[0], instruction.type, &emitter.module.types)
fmt.sbprintf(&emitter.builder, ", %s ", llvm_type(instruction.type, &emitter.module.types))
write_operand(&emitter.builder, instructions, instruction.args[1], instruction.type, &emitter.module.types)
strings.write_string(&emitter.builder, "\n")
case .Unwrap:
optional_type := instructions[instruction.a].type if valid_instruction(instructions, instruction.a) else types.INVALID
item, ok := types.node(&emitter.module.types, optional_type)
@@ -1152,14 +1201,19 @@ emit_instruction_stream :: proc(
emit_trap_call(emitter, message)
fmt.sbprintf(&emitter.builder, " unreachable\noverflow_continue%d:\n", instruction_index)
case .Pointer_Add:
item, ok := types.node(&emitter.module.types, instruction.type)
if !ok || item.kind != .Pointer || !item.many ||
!valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) ||
result_item, result_ok := types.node(&emitter.module.types, instruction.type)
base_type := instructions[instruction.a].type if valid_instruction(instructions, instruction.a) else types.INVALID
base_item, base_ok := types.node(&emitter.module.types, base_type)
if !result_ok || result_item.kind != .Pointer ||
!base_ok || base_item.kind != .Pointer || !base_item.many ||
result_item.child != base_item.child ||
result_item.mutable != base_item.mutable ||
!valid_value(instructions, instruction.a, base_type, &emitter.module.types) ||
!valid_value(instructions, instruction.b, types.USIZE, &emitter.module.types) {
emit_recovery_value(emitter, instruction_index, instruction, "invalid pointer offset")
continue
}
fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr %s, ptr %%v%d, i64 ", instruction_index, llvm_type(item.child, &emitter.module.types), instruction.a)
fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr %s, ptr %%v%d, i64 ", instruction_index, llvm_type(result_item.child, &emitter.module.types), instruction.a)
write_operand(&emitter.builder, instructions, instruction.b, types.USIZE, &emitter.module.types)
strings.write_string(&emitter.builder, "\n")
case .Call:
+357 -1
View File
@@ -150,6 +150,18 @@ lower_location :: proc(state: ^State, expr_id: hir.Expr_Id, for_write := false)
return ir.INVALID_INSTRUCTION
}
hir_expr_is_location :: proc(state: ^State, expr_id: hir.Expr_Id) -> bool {
if expr_id == hir.INVALID_EXPR || int(expr_id) >= len(state.hir_module.exprs) {
return false
}
#partial switch state.hir_module.exprs[expr_id].kind {
case .Local, .Global, .Deref, .Index, .Field:
return true
case:
return false
}
}
lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
expr := state.hir_module.exprs[expr_id]
#partial switch expr.kind {
@@ -169,6 +181,30 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Range:
args := make([]ir.Instruction_Id, 3, state.allocator)
args[0] = lower_nested_expr(state, expr.args[0])
args[1] = lower_nested_expr(state, expr.args[1])
args[2] = append_instruction(state, ir.Instruction{
op=.Const,
span=expr.span,
type=types.BOOL,
integer=expr.integer,
target=ir.INVALID_REF,
a=ir.INVALID_INSTRUCTION,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
return append_instruction(state, ir.Instruction{
op=.Aggregate,
span=expr.span,
type=expr.type,
args=args,
target=ir.INVALID_REF,
a=ir.INVALID_INSTRUCTION,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .None:
return append_instruction(state, ir.Instruction{
op=.None, span=expr.span, type=expr.type,
@@ -381,7 +417,7 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
})
_ = pop(&stack)
case .String, .Array, .Struct, .None, .Optional_Some, .Address, .Deref,
case .String, .Array, .Struct, .Range, .None, .Optional_Some, .Address, .Deref,
.Index, .Slice, .Field, .Length, .Slice_Ptr, .Unwrap, .Orelse,
.Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or:
last = lower_compound_expr(state, frame.expr)
@@ -762,6 +798,326 @@ lower_statements :: proc(state: ^State, statements: []hir.Stmt_Id) {
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .For:
iterable_type := hir_module.exprs[statement.expr].type
if types.is_range(iterable_type, &hir_module.types) {
child := types.child_type(iterable_type, &hir_module.types)
range_value := lower_expr(state, statement.expr)
start := append_instruction(state, ir.Instruction{
op=.Extract, span=statement.span, type=child, integer=0,
target=ir.INVALID_REF, a=range_value, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
end := append_instruction(state, ir.Instruction{
op=.Extract, span=statement.span, type=child, integer=1,
target=ir.INVALID_REF, a=range_value, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
inclusive := append_instruction(state, ir.Instruction{
op=.Extract, span=statement.span, type=types.BOOL, integer=2,
target=ir.INVALID_REF, a=range_value, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
current_slot := append_instruction(state, ir.Instruction{
op=.Alloca, span=statement.span, type=child,
target=ir.local_ref(ir.Local_Id(statement.local)),
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
state.local_slots[statement.local] = current_slot
append_instruction(state, ir.Instruction{
op=.Store, span=statement.span, type=child,
target=ir.INVALID_REF, a=current_slot, b=start,
diagnostic=source.INVALID_DIAGNOSTIC,
})
condition_lbl := fresh_label(state)
body_lbl := fresh_label(state)
update_lbl := fresh_label(state)
exit_lbl := fresh_label(state)
append_instruction(state, ir.Instruction{
op=.Br, span=statement.span, type=types.VOID, integer=condition_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Label, span=statement.span, type=types.VOID, integer=condition_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
current := append_instruction(state, ir.Instruction{
op=.Load, span=statement.span, type=child,
target=ir.INVALID_REF, a=current_slot, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
less := append_instruction(state, ir.Instruction{
op=.Compare, span=statement.span, type=types.BOOL,
integer=i64(ir.Compare_Predicate.Lt),
target=ir.INVALID_REF, a=current, b=end,
diagnostic=source.INVALID_DIAGNOSTIC,
})
equal := append_instruction(state, ir.Instruction{
op=.Compare, span=statement.span, type=types.BOOL,
integer=i64(ir.Compare_Predicate.Eq),
target=ir.INVALID_REF, a=current, b=end,
diagnostic=source.INVALID_DIAGNOSTIC,
})
false_value := append_instruction(state, ir.Instruction{
op=.Const, span=statement.span, type=types.BOOL, integer=0,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
true_value := append_instruction(state, ir.Instruction{
op=.Const, span=statement.span, type=types.BOOL, integer=1,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
inclusive_args := make([]ir.Instruction_Id, 2, state.allocator)
inclusive_args[0] = equal
inclusive_args[1] = false_value
inclusive_equal := append_instruction(state, ir.Instruction{
op=.Select, span=statement.span, type=types.BOOL,
target=ir.INVALID_REF, a=inclusive, b=ir.INVALID_INSTRUCTION,
args=inclusive_args, diagnostic=source.INVALID_DIAGNOSTIC,
})
condition_args := make([]ir.Instruction_Id, 2, state.allocator)
condition_args[0] = true_value
condition_args[1] = inclusive_equal
condition := append_instruction(state, ir.Instruction{
op=.Select, span=statement.span, type=types.BOOL,
target=ir.INVALID_REF, a=less, b=ir.INVALID_INSTRUCTION,
args=condition_args, diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Cond_Br, span=statement.span, type=types.VOID,
a=condition, integer=body_lbl, target=ir.Ref(u32(exit_lbl)),
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Label, span=statement.span, type=types.VOID, integer=body_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
lower_statements(state, statement.then_body)
after_body := append_instruction(state, ir.Instruction{
op=.Load, span=statement.span, type=child,
target=ir.INVALID_REF, a=current_slot, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
at_end := append_instruction(state, ir.Instruction{
op=.Compare, span=statement.span, type=types.BOOL,
integer=i64(ir.Compare_Predicate.Eq),
target=ir.INVALID_REF, a=after_body, b=end,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Cond_Br, span=statement.span, type=types.VOID,
a=at_end, integer=exit_lbl, target=ir.Ref(u32(update_lbl)),
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Label, span=statement.span, type=types.VOID, integer=update_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
one := append_instruction(state, ir.Instruction{
op=.Const, span=statement.span, type=child, integer=1,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
next := append_instruction(state, ir.Instruction{
op=.Add_Checked, span=statement.span, type=child,
target=ir.INVALID_REF, a=after_body, b=one,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Store, span=statement.span, type=child,
target=ir.INVALID_REF, a=current_slot, b=next,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Br, span=statement.span, type=types.VOID, integer=condition_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Label, span=statement.span, type=types.VOID, integer=exit_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
continue
}
item, item_ok := types.container(iterable_type, &hir_module.types)
if !item_ok {
append_instruction(state, ir.Instruction{
op=.Trap, span=statement.span, type=types.VOID,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=statement.diagnostic,
})
continue
}
iterable_value := ir.INVALID_INSTRUCTION
if types.is_array(iterable_type, &hir_module.types) {
if hir_expr_is_location(state, statement.expr) {
iterable_value = lower_location(state, statement.expr)
} else {
value := lower_expr(state, statement.expr)
iterable_value = append_instruction(state, ir.Instruction{
op=.Alloca, span=statement.span, type=iterable_type,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Store, span=statement.span, type=iterable_type,
target=ir.INVALID_REF, a=iterable_value, b=value,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
} else {
iterable_value = lower_expr(state, statement.expr)
}
base := append_instruction(state, ir.Instruction{
op=.Slice_Ptr, span=statement.span, type=statement.iterator_type,
target=ir.INVALID_REF, a=iterable_value, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
length := ir.INVALID_INSTRUCTION
if item.kind == .Array {
length = append_instruction(state, ir.Instruction{
op=.Const, span=statement.span, type=types.USIZE, integer=i64(item.count),
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
} else {
length = append_instruction(state, ir.Instruction{
op=.Length, span=statement.span, type=types.USIZE,
target=ir.INVALID_REF, a=iterable_value, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
counter_target := ir.INVALID_REF
if statement.index_local != hir.INVALID_LOCAL {
counter_target = ir.local_ref(ir.Local_Id(statement.index_local))
}
counter_slot := append_instruction(state, ir.Instruction{
op=.Alloca, span=statement.span, type=types.USIZE,
target=counter_target, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
if statement.index_local != hir.INVALID_LOCAL {
state.local_slots[statement.index_local] = counter_slot
}
zero := append_instruction(state, ir.Instruction{
op=.Const, span=statement.span, type=types.USIZE, integer=0,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Store, span=statement.span, type=types.USIZE,
target=ir.INVALID_REF, a=counter_slot, b=zero,
diagnostic=source.INVALID_DIAGNOSTIC,
})
capture_type := state.func_locals[statement.local].type
capture_slot := append_instruction(state, ir.Instruction{
op=.Alloca, span=statement.span, type=capture_type,
target=ir.local_ref(ir.Local_Id(statement.local)),
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
state.local_slots[statement.local] = capture_slot
condition_lbl := fresh_label(state)
body_lbl := fresh_label(state)
update_lbl := fresh_label(state)
exit_lbl := fresh_label(state)
append_instruction(state, ir.Instruction{
op=.Br, span=statement.span, type=types.VOID, integer=condition_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Label, span=statement.span, type=types.VOID, integer=condition_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
index := append_instruction(state, ir.Instruction{
op=.Load, span=statement.span, type=types.USIZE,
target=ir.INVALID_REF, a=counter_slot, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
condition := append_instruction(state, ir.Instruction{
op=.Compare, span=statement.span, type=types.BOOL,
integer=i64(ir.Compare_Predicate.Lt),
target=ir.INVALID_REF, a=index, b=length,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Cond_Br, span=statement.span, type=types.VOID,
a=condition, integer=body_lbl, target=ir.Ref(u32(exit_lbl)),
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Label, span=statement.span, type=types.VOID, integer=body_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
pointer_type := statement.iterator_type
if statement.pointer_capture {
pointer_type = capture_type
}
element_pointer := append_instruction(state, ir.Instruction{
op=.Pointer_Add, span=statement.span, type=pointer_type,
target=ir.INVALID_REF, a=base, b=index,
diagnostic=source.INVALID_DIAGNOSTIC,
})
captured := element_pointer
if !statement.pointer_capture {
captured = append_instruction(state, ir.Instruction{
op=.Load, span=statement.span, type=item.child,
target=ir.INVALID_REF, a=element_pointer, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
append_instruction(state, ir.Instruction{
op=.Store, span=statement.span, type=capture_type,
target=ir.INVALID_REF, a=capture_slot, b=captured,
diagnostic=source.INVALID_DIAGNOSTIC,
})
lower_statements(state, statement.then_body)
append_instruction(state, ir.Instruction{
op=.Br, span=statement.span, type=types.VOID, integer=update_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Label, span=statement.span, type=types.VOID, integer=update_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
one := append_instruction(state, ir.Instruction{
op=.Const, span=statement.span, type=types.USIZE, integer=1,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
next := append_instruction(state, ir.Instruction{
op=.Add_Checked, span=statement.span, type=types.USIZE,
target=ir.INVALID_REF, a=index, b=one,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Store, span=statement.span, type=types.USIZE,
target=ir.INVALID_REF, a=counter_slot, b=next,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Br, span=statement.span, type=types.VOID, integer=condition_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Label, span=statement.span, type=types.VOID, integer=exit_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
}
}
+102 -1
View File
@@ -20,6 +20,7 @@ Parser :: struct {
file: ast.File_Id,
cursor: int,
delimiter_depth: int,
range_disabled: int,
// Suppresses `Name { ... }` struct-literal parsing at delimiter depth 0 so a
// control-flow condition like `if foo { ... }` does not swallow the block as a
// struct literal. Nested `(`/`[`/call-arg contexts (delimiter_depth > 0) still
@@ -630,6 +631,9 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
if _, ok := allow(parser, .Right_Paren); !ok {
source.add(parser.diagnostics, current(parser).span, "expected ')'")
}
if expr != ast.INVALID_EXPR && int(expr) < len(parser.module.exprs) {
parser.module.exprs[expr].parenthesized = true
}
return expr
case .Invalid:
advance(parser)
@@ -649,6 +653,8 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
infix_binding_power :: proc(kind: token.Kind) -> (left, right: int, ok: bool) {
#partial switch kind {
case .Range, .Range_Inclusive:
return 0, 1, true
case .Keyword_Orelse:
return 2, 3, true
case .Keyword_Or:
@@ -665,6 +671,7 @@ infix_binding_power :: proc(kind: token.Kind) -> (left, right: int, ok: bool) {
infix_expr_kind :: proc(kind: token.Kind) -> ast.Expr_Kind {
#partial switch kind {
case .Range, .Range_Inclusive: return .Range
case .Keyword_Orelse: return .Orelse
case .Keyword_Or: return .Or
case .Keyword_And: return .And
@@ -678,6 +685,18 @@ infix_expr_kind :: proc(kind: token.Kind) -> ast.Expr_Kind {
}
}
is_simple_range_bound :: proc(expr: ast.Expr) -> bool {
if expr.parenthesized {
return true
}
#partial switch expr.kind {
case .Integer, .Float, .String, .Bool, .Name:
return true
case:
return false
}
}
prefix_binding_power :: proc(kind: token.Kind) -> (right: int, ok: bool) {
#partial switch kind {
case .Minus, .Ampersand, .Bang:
@@ -762,7 +781,9 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
if _, ok := allow(parser, .Range); ok {
slicing = true
} else {
parser.range_disabled += 1
start_expr = parse_expression_bp(parser, 0, nesting+1)
parser.range_disabled -= 1
skip_newlines(parser)
if _, ok := allow(parser, .Range); ok {
slicing = true
@@ -770,7 +791,9 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
}
skip_newlines(parser)
if slicing && current(parser).kind != .Right_Bracket {
parser.range_disabled += 1
end_expr = parse_expression_bp(parser, 0, nesting+1)
parser.range_disabled -= 1
skip_newlines(parser)
}
end_token, ok := allow(parser, .Right_Bracket)
@@ -822,6 +845,10 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
continue
}
left_power, right_power, ok := infix_binding_power(current(parser).kind)
if ok && (current(parser).kind == .Range || current(parser).kind == .Range_Inclusive) &&
parser.range_disabled > 0 {
ok = false
}
if !ok || left_power < minimum_binding_power {
break
}
@@ -830,9 +857,26 @@ parse_expression_bp :: proc(parser: ^Parser, minimum_binding_power, nesting: int
right := parse_expression_bp(parser, right_power, nesting+1)
left_expr := parser.module.exprs[left]
right_expr := parser.module.exprs[right]
if operator.kind == .Range || operator.kind == .Range_Inclusive {
if !is_simple_range_bound(left_expr) {
source.add(
parser.diagnostics,
left_expr.span,
"range bounds with operators must be parenthesized",
)
}
if !is_simple_range_bound(right_expr) {
source.add(
parser.diagnostics,
right_expr.span,
"range bounds with operators must be parenthesized",
)
}
}
left = add_expr(parser, ast.Expr{
kind=infix_expr_kind(operator.kind),
span=span_from(left_expr.span, right_expr.span),
integer=1 if operator.kind == .Range_Inclusive else 0,
left=left,
right=right,
diagnostic=source.INVALID_DIAGNOSTIC,
@@ -931,6 +975,9 @@ parse_statement :: proc(parser: ^Parser) -> ast.Stmt_Id {
if current(parser).kind == .Keyword_While {
return parse_while(parser)
}
if current(parser).kind == .Keyword_For {
return parse_for(parser)
}
if current(parser).kind == .Identifier || current(parser).kind == .Underscore {
start_cursor := parser.cursor
@@ -1159,7 +1206,7 @@ parse_while_update :: proc(parser: ^Parser) -> ast.Stmt_Id {
statement := &parser.module.statements[update]
switch statement.kind {
case .Assignment, .Expression:
case .Invalid, .Declaration, .Return, .If, .While:
case .Invalid, .Declaration, .Return, .If, .While, .For:
diagnostic := source.add(
parser.diagnostics,
statement.span,
@@ -1187,6 +1234,60 @@ parse_while_update :: proc(parser: ^Parser) -> ast.Stmt_Id {
return update
}
parse_for :: proc(parser: ^Parser) -> ast.Stmt_Id {
start := advance(parser) // consume 'for'
skip_newlines(parser)
saved := parser.no_struct_literal
parser.no_struct_literal = true
iterable := parse_expression(parser)
parser.no_struct_literal = saved
skip_newlines(parser)
pointer_capture := false
item_name := symbol.INVALID
index_name := symbol.INVALID
if _, ok := allow(parser, .Pipe); !ok {
source.add(parser.diagnostics, current(parser).span, "expected '|' before for-loop captures")
} else {
if _, ok := allow(parser, .At); ok {
pointer_capture = true
}
item, item_ok := allow(parser, .Identifier)
if item_ok {
item_name = item.symbol
} else {
source.add(parser.diagnostics, current(parser).span, "expected a for-loop item capture")
}
if _, ok := allow(parser, .Comma); ok {
index, index_ok := allow(parser, .Identifier)
if index_ok {
index_name = index.symbol
} else {
source.add(parser.diagnostics, current(parser).span, "expected an index capture after ','")
}
}
if _, ok := allow(parser, .Pipe); !ok {
source.add(parser.diagnostics, current(parser).span, "expected '|' to close for-loop captures")
}
}
skip_newlines(parser)
body := parse_block(parser)
id := ast.stmt_id(len(parser.module.statements))
append(&parser.module.statements, ast.Stmt{
kind=.For,
span=span_from(start.span, previous(parser).span),
name=item_name,
index_name=index_name,
pointer_capture=pointer_capture,
expr=iterable,
body=body,
update=ast.INVALID_STMT,
diagnostic=source.INVALID_DIAGNOSTIC,
})
return id
}
parse_while :: proc(parser: ^Parser) -> ast.Stmt_Id {
start := advance(parser) // consume 'while'
skip_newlines(parser)
+2
View File
@@ -27,6 +27,7 @@ Kind :: enum u8 {
Minus,
Dot,
Range,
Range_Inclusive,
Ellipsis,
At,
Star,
@@ -55,6 +56,7 @@ Kind :: enum u8 {
Keyword_Or,
Keyword_If,
Keyword_While,
Keyword_For,
Keyword_Else,
Keyword_True,
Keyword_False,
+18 -4
View File
@@ -59,6 +59,7 @@ Kind :: enum u8 {
Array,
Pointer,
Slice,
Range,
Optional,
Function,
Named,
@@ -392,7 +393,7 @@ is_concrete_scalar :: proc(value: Type) -> bool {
is_concrete :: proc(value: Type, store: ^Store = nil) -> bool {
value_kind := kind(value, store)
if value_kind == .Scalar || value_kind == .Array || value_kind == .Pointer ||
value_kind == .Slice || value_kind == .Optional {
value_kind == .Slice || value_kind == .Range || value_kind == .Optional {
return true
}
if value_kind == .Struct || value_kind == .Union {
@@ -414,6 +415,10 @@ is_slice :: proc(value: Type, store: ^Store) -> bool {
return kind(value, store) == .Slice
}
is_range :: proc(value: Type, store: ^Store) -> bool {
return kind(value, store) == .Range
}
is_optional :: proc(value: Type, store: ^Store) -> bool {
return kind(value, store) == .Optional
}
@@ -486,7 +491,7 @@ is_runtime_value :: proc(value: Type, store: ^Store) -> bool {
if value_kind == .Scalar || value_kind == .Pointer {
return true
}
if value_kind == .Slice || value_kind == .Array || value_kind == .Optional {
if value_kind == .Slice || value_kind == .Array || value_kind == .Range || value_kind == .Optional {
return !contains_c_struct_by_value(value, store)
}
if value_kind == .Struct || value_kind == .Union {
@@ -521,7 +526,7 @@ contains_c_struct_by_value :: proc(value: Type, store: ^Store, depth := 0) -> bo
if item.kind == .Union {
return item.opaque || (item.c_layout && item.field_count == 0)
}
if item.kind == .Array || item.kind == .Slice || item.kind == .Optional {
if item.kind == .Array || item.kind == .Slice || item.kind == .Range || item.kind == .Optional {
return contains_c_struct_by_value(item.child, store, depth+1)
}
return false
@@ -665,6 +670,10 @@ slice :: proc(store: ^Store, child: Type, mutable: bool, has_sentinel := false,
})
}
range :: proc(store: ^Store, child: Type) -> Type {
return intern(store, Node{kind=.Range, child=child})
}
array :: proc(
store: ^Store,
child: Type,
@@ -805,6 +814,11 @@ size :: proc(value: Type, store: ^Store, selected := target.DEFAULT) -> u64 {
return u64(target.pointer_bits(selected)/8)
case .Slice:
return u64(target.pointer_bits(selected)/8*2)
case .Range:
child_size := size(child_type(value, store), store, selected)
child_align := u64(alignment_of(child_type(value, store), store, selected))
raw_size := child_size*2+1
return (raw_size+child_align-1)/child_align*child_align
case .Array:
item, _ := node(store, value)
return physical_count(value, store)*size(item.child, store, selected)
@@ -855,7 +869,7 @@ alignment_of :: proc(value: Type, store: ^Store, selected := target.DEFAULT) ->
return alignment(value, selected)
case .Pointer, .Slice:
return target.pointer_bits(selected)/8
case .Array, .Optional:
case .Array, .Range, .Optional:
return alignment_of(child_type(value, store), store, selected)
case .Function:
return 1