sentinel pointers and c strings
This commit is contained in:
@@ -221,6 +221,19 @@ is_runtime_type :: proc(checker: ^Checker, value: types.Type) -> bool {
|
||||
return types.is_runtime_value(value, &checker.module.types)
|
||||
}
|
||||
|
||||
string_literal_type :: proc(checker: ^Checker, string_id: u64) -> types.Type {
|
||||
length: u64
|
||||
if string_id < u64(len(checker.ast_module.strings)) {
|
||||
length = u64(len(checker.ast_module.strings[string_id]))
|
||||
}
|
||||
array := types.array(&checker.module.types, types.U8, length, false, true, 0)
|
||||
return types.pointer(&checker.module.types, array, false, false)
|
||||
}
|
||||
|
||||
container_pointer_type :: proc(store: ^types.Store, item: types.Node) -> types.Type {
|
||||
return types.pointer(store, item.child, item.mutable, true, item.has_sentinel, item.sentinel)
|
||||
}
|
||||
|
||||
resolve_inferred_array :: proc(checker: ^Checker, value: types.Type, expr_id: ast.Expr_Id) -> types.Type {
|
||||
item, ok := types.node(&checker.module.types, value)
|
||||
if !ok || item.kind != .Array || !item.inferred_count ||
|
||||
@@ -665,7 +678,7 @@ validate_type_nodes :: proc(checker: ^Checker) {
|
||||
source.addf(
|
||||
checker.diagnostics,
|
||||
source.Span{},
|
||||
"sentinel value does not fit array or slice element type %s",
|
||||
"sentinel value does not fit array, slice, or pointer element type %s",
|
||||
types.name(item.child),
|
||||
)
|
||||
}
|
||||
@@ -809,7 +822,7 @@ infer_compound_expr :: proc(
|
||||
store := &checker.module.types
|
||||
#partial switch expr.kind {
|
||||
case .String:
|
||||
return types.slice(store, types.U8, false, true, 0)
|
||||
return string_literal_type(checker, expr.integer)
|
||||
case .Array:
|
||||
element := types.INVALID
|
||||
for arg in expr.args {
|
||||
@@ -835,11 +848,12 @@ infer_compound_expr :: proc(
|
||||
case .Index:
|
||||
value := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded)
|
||||
_ = infer_nested_expr(checker, expr.right, locals, pkg, file, demanded)
|
||||
return types.child_type(value, store)
|
||||
item, ok := types.container(value, store)
|
||||
return item.child if ok else types.INVALID
|
||||
case .Slice:
|
||||
value := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded)
|
||||
item, ok := types.node(store, value)
|
||||
if !ok || (item.kind != .Array && item.kind != .Slice) {
|
||||
item, ok := types.container(value, store)
|
||||
if !ok || item.kind == .Pointer {
|
||||
return types.INVALID
|
||||
}
|
||||
for bound in expr.args {
|
||||
@@ -851,14 +865,15 @@ infer_compound_expr :: proc(
|
||||
return types.slice(store, item.child, item.mutable, preserve, item.sentinel)
|
||||
case .Field:
|
||||
value := infer_nested_expr(checker, expr.left, locals, pkg, file, demanded)
|
||||
item, has_item := types.node(store, value)
|
||||
field_name := symbol_text(checker, expr.name)
|
||||
item, has_item := types.container(value, store)
|
||||
if has_item && (item.kind == .Array || item.kind == .Slice) {
|
||||
if field_name == "len" {
|
||||
return types.USIZE
|
||||
}
|
||||
if field_name == "ptr" {
|
||||
return types.pointer(store, item.child, item.mutable, true)
|
||||
if field_name == "ptr" &&
|
||||
(item.kind == .Slice || types.is_pointer(value, store)) {
|
||||
return container_pointer_type(store, item)
|
||||
}
|
||||
}
|
||||
if types.is_pointer(value, store) {
|
||||
@@ -950,13 +965,14 @@ infer_expr :: proc(
|
||||
last = find_infer_local(locals, expr.name)
|
||||
} else {
|
||||
base_type := find_infer_local(locals, expr.qualifier)
|
||||
item, has_item := types.node(&checker.module.types, base_type)
|
||||
item, has_item := types.container(base_type, &checker.module.types)
|
||||
field_name := symbol_text(checker, expr.name)
|
||||
if has_item && (item.kind == .Array || item.kind == .Slice) {
|
||||
if field_name == "len" {
|
||||
last = types.USIZE
|
||||
} else if field_name == "ptr" {
|
||||
last = types.pointer(&checker.module.types, item.child, item.mutable, true)
|
||||
} else if field_name == "ptr" &&
|
||||
(item.kind == .Slice || types.is_pointer(base_type, &checker.module.types)) {
|
||||
last = container_pointer_type(&checker.module.types, item)
|
||||
}
|
||||
}
|
||||
if types.is_pointer(base_type, &checker.module.types) {
|
||||
@@ -1296,11 +1312,35 @@ coerce_expr :: proc(
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
if types.can_weaken_slice(actual, expected, &checker.module.types) {
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Weaken_Slice,
|
||||
span=span,
|
||||
type=expected,
|
||||
left=expr_id,
|
||||
target=hir.INVALID_REF,
|
||||
right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
if types.can_decay_array_pointer(actual, expected, &checker.module.types) {
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Decay_Array_Pointer,
|
||||
span=span,
|
||||
type=expected,
|
||||
left=expr_id,
|
||||
target=hir.INVALID_REF,
|
||||
right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
if types.is_optional(expected, &checker.module.types) {
|
||||
child := types.child_type(expected, &checker.module.types)
|
||||
if types.equal(actual, child) ||
|
||||
types.can_widen(actual, child) ||
|
||||
types.can_weaken_pointer(actual, child, &checker.module.types) {
|
||||
types.can_weaken_pointer(actual, child, &checker.module.types) ||
|
||||
types.can_weaken_slice(actual, child, &checker.module.types) ||
|
||||
types.can_decay_array_pointer(actual, child, &checker.module.types) {
|
||||
value := coerce_expr(checker, expr_id, child, span)
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Optional_Some,
|
||||
@@ -1475,7 +1515,8 @@ hir_location_writable :: proc(checker: ^Checker, expr_id: hir.Expr_Id, locals: [
|
||||
return types.is_mutable(pointer_type, &checker.module.types)
|
||||
case .Index:
|
||||
container_type := checker.module.exprs[expr.left].type
|
||||
return types.is_mutable(container_type, &checker.module.types)
|
||||
item, ok := types.container(container_type, &checker.module.types)
|
||||
return ok && item.mutable
|
||||
case .Field:
|
||||
base_type := checker.module.exprs[expr.left].type
|
||||
if types.is_pointer(base_type, &checker.module.types) {
|
||||
@@ -1539,7 +1580,7 @@ build_compound_expr :: proc(
|
||||
store := &checker.module.types
|
||||
#partial switch expr.kind {
|
||||
case .String:
|
||||
string_type := types.slice(store, types.U8, false, true, 0)
|
||||
string_type := string_literal_type(checker, expr.integer)
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.String, span=expr.span, type=string_type, integer=i64(expr.integer),
|
||||
target=hir.INVALID_REF, left=hir.INVALID_EXPR, right=hir.INVALID_EXPR,
|
||||
@@ -1639,9 +1680,9 @@ build_compound_expr :: proc(
|
||||
container := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
index := build_nested_expr(checker, expr.right, locals, global_reads, calls, types.USIZE, pkg, file)
|
||||
container_type := checker.module.exprs[container].type
|
||||
item, ok := types.node(store, container_type)
|
||||
if !ok || (item.kind != .Array && item.kind != .Slice && !(item.kind == .Pointer && item.many)) {
|
||||
id := source.add(checker.diagnostics, expr.span, "indexing requires an array, slice, or many-item pointer")
|
||||
item, ok := types.container(container_type, store)
|
||||
if !ok {
|
||||
id := source.add(checker.diagnostics, expr.span, "indexing requires an array, slice, pointer-to-array, or many-item pointer")
|
||||
return invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
@@ -1651,9 +1692,9 @@ build_compound_expr :: proc(
|
||||
case .Slice:
|
||||
container := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
container_type := checker.module.exprs[container].type
|
||||
item, ok := types.node(store, container_type)
|
||||
if !ok || (item.kind != .Array && item.kind != .Slice) {
|
||||
id := source.add(checker.diagnostics, expr.span, "slicing requires an array or slice")
|
||||
item, ok := types.container(container_type, store)
|
||||
if !ok || item.kind == .Pointer {
|
||||
id := source.add(checker.diagnostics, expr.span, "slicing requires an array, slice, or pointer-to-array")
|
||||
return invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
bounds := make([]hir.Expr_Id, 2, checker.allocator)
|
||||
@@ -1673,8 +1714,8 @@ build_compound_expr :: proc(
|
||||
case .Field:
|
||||
base := build_nested_expr(checker, expr.left, locals, global_reads, calls, types.INVALID, pkg, file)
|
||||
base_type := checker.module.exprs[base].type
|
||||
item, has_item := types.node(store, base_type)
|
||||
field_name := symbol_text(checker, expr.name)
|
||||
item, has_item := types.container(base_type, store)
|
||||
if has_item && (item.kind == .Array || item.kind == .Slice) {
|
||||
if field_name == "len" {
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
@@ -1682,13 +1723,18 @@ build_compound_expr :: proc(
|
||||
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
if field_name == "ptr" {
|
||||
if field_name == "ptr" &&
|
||||
(item.kind == .Slice || types.is_pointer(base_type, store)) {
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Slice_Ptr, span=expr.span,
|
||||
type=types.pointer(store, item.child, item.mutable, true), left=base,
|
||||
type=container_pointer_type(store, item), left=base,
|
||||
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
if field_name == "ptr" && item.kind == .Array {
|
||||
id := source.add(checker.diagnostics, expr.span, "arrays do not expose '.ptr'; take their address first")
|
||||
return invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
}
|
||||
if types.is_pointer(base_type, store) {
|
||||
base_type = types.child_type(base_type, store)
|
||||
@@ -1848,7 +1894,7 @@ build_expr :: proc(
|
||||
left=hir.INVALID_EXPR, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
base_type := local.type
|
||||
item, has_item := types.node(&checker.module.types, base_type)
|
||||
item, has_item := types.container(base_type, &checker.module.types)
|
||||
field_name := symbol_text(checker, expr.name)
|
||||
if has_item && (item.kind == .Array || item.kind == .Slice) {
|
||||
if field_name == "len" {
|
||||
@@ -1856,12 +1902,16 @@ build_expr :: proc(
|
||||
kind=.Length, span=expr.span, type=types.USIZE, left=base,
|
||||
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
} else if field_name == "ptr" {
|
||||
} else if field_name == "ptr" &&
|
||||
(item.kind == .Slice || types.is_pointer(base_type, &checker.module.types)) {
|
||||
last = add_hir_expr(checker, hir.Expr{
|
||||
kind=.Slice_Ptr, span=expr.span,
|
||||
type=types.pointer(&checker.module.types, item.child, item.mutable, true), left=base,
|
||||
type=container_pointer_type(&checker.module.types, item), left=base,
|
||||
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
} else if field_name == "ptr" && item.kind == .Array {
|
||||
id := source.add(checker.diagnostics, expr.span, "arrays do not expose '.ptr'; take their address first")
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
}
|
||||
}
|
||||
if types.is_pointer(base_type, &checker.module.types) {
|
||||
|
||||
@@ -95,6 +95,8 @@ Expr_Kind :: enum u8 {
|
||||
Widen,
|
||||
C_Vararg_Promote,
|
||||
Weaken_Pointer,
|
||||
Weaken_Slice,
|
||||
Decay_Array_Pointer,
|
||||
Negate,
|
||||
Add,
|
||||
Pointer_Add,
|
||||
|
||||
@@ -89,6 +89,8 @@ Opcode :: enum u8 {
|
||||
Widen,
|
||||
C_Vararg_Promote,
|
||||
Weaken_Pointer,
|
||||
Weaken_Slice,
|
||||
Decay_Array_Pointer,
|
||||
Neg_Checked,
|
||||
Add_Checked,
|
||||
Pointer_Add,
|
||||
|
||||
+54
-14
@@ -112,7 +112,8 @@ valid_value :: proc(
|
||||
switch instructions[value_id].op {
|
||||
case .Param, .Const, .String, .Aggregate, .None, .Optional_Some,
|
||||
.Load_Global, .Address_Of, .Load, .Slice, .Length, .Slice_Ptr, .Unwrap, .Orelse,
|
||||
.Widen, .C_Vararg_Promote, .Weaken_Pointer, .Neg_Checked, .Add_Checked, .Pointer_Add, .Call:
|
||||
.Widen, .C_Vararg_Promote, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer,
|
||||
.Neg_Checked, .Add_Checked, .Pointer_Add, .Call:
|
||||
return true
|
||||
case .Address_Global, .Alloca, .Index_Address, .Field_Address, .Orelse_Begin,
|
||||
.Store, .Trap, .Return, .Return_Void:
|
||||
@@ -306,21 +307,17 @@ emit_instruction_stream :: proc(
|
||||
case .Param, .Const:
|
||||
case .String:
|
||||
string_id := int(instruction.integer)
|
||||
_, array, pointer_ok := types.array_pointer(instruction.type, &emitter.module.types)
|
||||
if string_id < 0 || string_id >= len(emitter.module.strings) ||
|
||||
!types.is_slice(instruction.type, &emitter.module.types) {
|
||||
!pointer_ok || array.child != types.U8 || !array.has_sentinel ||
|
||||
array.sentinel != 0 || array.count != u64(len(emitter.module.strings[string_id])) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid string literal")
|
||||
continue
|
||||
}
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
" %%string_ptr%d = insertvalue %s poison, ptr @bro.str.%d, 0\n",
|
||||
instruction_index, type_name, string_id,
|
||||
)
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
" %%v%d = insertvalue %s %%string_ptr%d, i64 %d, 1\n",
|
||||
instruction_index, type_name, instruction_index, len(emitter.module.strings[string_id]),
|
||||
" %%v%d = getelementptr %s, ptr @bro.str.%d, i64 0\n",
|
||||
instruction_index, llvm_type(types.child_type(instruction.type, &emitter.module.types), &emitter.module.types), string_id,
|
||||
)
|
||||
case .Aggregate:
|
||||
item, ok := types.node(&emitter.module.types, instruction.type)
|
||||
@@ -465,7 +462,7 @@ emit_instruction_stream :: proc(
|
||||
continue
|
||||
}
|
||||
container := instructions[instruction.a]
|
||||
container_node, container_ok := types.node(&emitter.module.types, container.type)
|
||||
container_node, container_ok := types.container(container.type, &emitter.module.types)
|
||||
if !container_ok {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid index container")
|
||||
continue
|
||||
@@ -504,7 +501,13 @@ emit_instruction_stream :: proc(
|
||||
fmt.sbprintf(&emitter.builder, " unreachable\nindex_continue%d:\n", instruction_index)
|
||||
}
|
||||
if container_node.kind == .Array {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr %s, ptr %s, i64 0, i64 ", instruction_index, llvm_type(container.type, &emitter.module.types), pointer_name)
|
||||
array_type := container.type
|
||||
_, array, array_pointer_ok := types.array_pointer(container.type, &emitter.module.types)
|
||||
if array_pointer_ok {
|
||||
array_type = types.child_type(container.type, &emitter.module.types)
|
||||
container_node = array
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr %s, ptr %s, i64 0, i64 ", instruction_index, llvm_type(array_type, &emitter.module.types), pointer_name)
|
||||
} else {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr %s, ptr %s, i64 ", instruction_index, llvm_type(instruction.type, &emitter.module.types), pointer_name)
|
||||
}
|
||||
@@ -558,7 +561,7 @@ emit_instruction_stream :: proc(
|
||||
continue
|
||||
}
|
||||
container := instructions[instruction.a]
|
||||
item, ok := types.node(&emitter.module.types, container.type)
|
||||
item, ok := types.container(container.type, &emitter.module.types)
|
||||
if !ok || (item.kind != .Array && item.kind != .Slice) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid slice container")
|
||||
continue
|
||||
@@ -566,7 +569,11 @@ emit_instruction_stream :: proc(
|
||||
pointer_name := fmt.tprintf("%%v%d", instruction.a)
|
||||
length_name := fmt.tprintf("%d", item.count)
|
||||
if item.kind == .Array {
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_ptr%d = getelementptr %s, ptr %%v%d, i64 0, i64 0\n", instruction_index, llvm_type(container.type, &emitter.module.types), instruction.a)
|
||||
array_type := container.type
|
||||
if types.is_pointer(container.type, &emitter.module.types) {
|
||||
array_type = types.child_type(container.type, &emitter.module.types)
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_ptr%d = getelementptr %s, ptr %%v%d, i64 0, i64 0\n", instruction_index, llvm_type(array_type, &emitter.module.types), instruction.a)
|
||||
pointer_name = fmt.tprintf("%%slice_ptr%d", instruction_index)
|
||||
} else if item.kind == .Slice {
|
||||
fmt.sbprintf(&emitter.builder, " %%slice_ptr%d = extractvalue %s %%v%d, 0\n", instruction_index, llvm_type(container.type, &emitter.module.types), instruction.a)
|
||||
@@ -621,12 +628,21 @@ emit_instruction_stream :: proc(
|
||||
continue
|
||||
}
|
||||
container_type := instructions[instruction.a].type
|
||||
_, _, array_pointer_ok := types.array_pointer(container_type, &emitter.module.types)
|
||||
if types.is_array(container_type, &emitter.module.types) {
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
" %%v%d = getelementptr %s, ptr %%v%d, i64 0, i64 0\n",
|
||||
instruction_index, llvm_type(container_type, &emitter.module.types), instruction.a,
|
||||
)
|
||||
} else if array_pointer_ok {
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
" %%v%d = getelementptr %s, ptr %%v%d, i64 0, i64 0\n",
|
||||
instruction_index,
|
||||
llvm_type(types.child_type(container_type, &emitter.module.types), &emitter.module.types),
|
||||
instruction.a,
|
||||
)
|
||||
} else if types.is_slice(container_type, &emitter.module.types) {
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
@@ -741,6 +757,30 @@ emit_instruction_stream :: proc(
|
||||
continue
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, ptr %%v%d, ptr null\n", instruction_index, instruction.a)
|
||||
case .Weaken_Slice:
|
||||
if !valid_instruction(instructions, instruction.a) ||
|
||||
!types.can_weaken_slice(instructions[instruction.a].type, instruction.type, &emitter.module.types) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid slice weakening operand")
|
||||
continue
|
||||
}
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = select i1 true, %s %%v%d, %s zeroinitializer\n", instruction_index, type_name, instruction.a, type_name)
|
||||
case .Decay_Array_Pointer:
|
||||
if !valid_instruction(instructions, instruction.a) ||
|
||||
!types.can_decay_array_pointer(instructions[instruction.a].type, instruction.type, &emitter.module.types) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid array pointer decay operand")
|
||||
continue
|
||||
}
|
||||
array_type := types.child_type(instructions[instruction.a].type, &emitter.module.types)
|
||||
array, _ := types.node(&emitter.module.types, array_type)
|
||||
if types.is_pointer(instruction.type, &emitter.module.types) {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = getelementptr %s, ptr %%v%d, i64 0, i64 0\n", instruction_index, llvm_type(array_type, &emitter.module.types), instruction.a)
|
||||
} else {
|
||||
type_name := llvm_type(instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, " %%decay_ptr%d = getelementptr %s, ptr %%v%d, i64 0, i64 0\n", instruction_index, llvm_type(array_type, &emitter.module.types), instruction.a)
|
||||
fmt.sbprintf(&emitter.builder, " %%decay_slice%d = insertvalue %s poison, ptr %%decay_ptr%d, 0\n", instruction_index, type_name, instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = insertvalue %s %%decay_slice%d, i64 %d, 1\n", instruction_index, type_name, instruction_index, array.count)
|
||||
}
|
||||
case .Neg_Checked:
|
||||
if !valid_value(instructions, instruction.a, instruction.type, &emitter.module.types) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid negation operand")
|
||||
|
||||
@@ -227,6 +227,14 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
_, array, array_ok := types.array_pointer(container_type, &state.hir_module.types)
|
||||
if array_ok {
|
||||
return append_instruction(state, ir.Instruction{
|
||||
op=.Const, span=expr.span, type=types.USIZE, integer=i64(array.count),
|
||||
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
value := lower_nested_expr(state, expr.left)
|
||||
return append_instruction(state, ir.Instruction{
|
||||
op=.Length, span=expr.span, type=types.USIZE,
|
||||
@@ -324,7 +332,7 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
||||
})
|
||||
}
|
||||
_ = pop(&stack)
|
||||
case .Widen, .C_Vararg_Promote, .Weaken_Pointer:
|
||||
case .Widen, .C_Vararg_Promote, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer:
|
||||
stack[frame_index].stage = 1
|
||||
append(&stack, Lower_Expr_Frame{expr=expr.left})
|
||||
case .Negate:
|
||||
@@ -357,9 +365,16 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
||||
continue
|
||||
}
|
||||
if frame.stage == 1 {
|
||||
op := ir.Opcode.Widen
|
||||
#partial switch expr.kind {
|
||||
case .Weaken_Pointer: op = .Weaken_Pointer
|
||||
case .Weaken_Slice: op = .Weaken_Slice
|
||||
case .Decay_Array_Pointer: op = .Decay_Array_Pointer
|
||||
case .C_Vararg_Promote: op = .C_Vararg_Promote
|
||||
case: op = .Widen
|
||||
}
|
||||
last = append_instruction(state, ir.Instruction{
|
||||
op=.Weaken_Pointer if expr.kind == .Weaken_Pointer else
|
||||
(.C_Vararg_Promote if expr.kind == .C_Vararg_Promote else .Widen),
|
||||
op=op,
|
||||
span=expr.span, type=expr.type, target=ir.INVALID_REF,
|
||||
a=last, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
|
||||
@@ -166,6 +166,17 @@ parse_type :: proc(parser: ^Parser) -> ast.Type_Syntax {
|
||||
node := types.Node{}
|
||||
if _, ok := allow(parser, .Right_Bracket); ok {
|
||||
node.kind = .Slice
|
||||
} else if _, ok := allow(parser, .Star); ok {
|
||||
node.kind = .Pointer
|
||||
node.many = true
|
||||
node.has_sentinel = true
|
||||
if _, ok = allow(parser, .Semicolon); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected ';' after '*' in sentinel pointer type")
|
||||
}
|
||||
node.sentinel, _ = parse_type_constant(parser)
|
||||
if _, ok = allow(parser, .Right_Bracket); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected ']' after sentinel pointer type")
|
||||
}
|
||||
} else if _, ok := allow(parser, .Semicolon); ok {
|
||||
node.kind = .Slice
|
||||
node.has_sentinel = true
|
||||
|
||||
@@ -488,13 +488,56 @@ is_many_pointer :: proc(value: Type, store: ^Store) -> bool {
|
||||
return ok && item.kind == .Pointer && item.many
|
||||
}
|
||||
|
||||
array_pointer :: proc(value: Type, store: ^Store) -> (pointer_item, array_item: Node, ok: bool) {
|
||||
pointer_ok: bool
|
||||
pointer_item, pointer_ok = node(store, value)
|
||||
if !pointer_ok || pointer_item.kind != .Pointer || pointer_item.many {
|
||||
return {}, {}, false
|
||||
}
|
||||
array_ok: bool
|
||||
array_item, array_ok = node(store, pointer_item.child)
|
||||
if !array_ok || array_item.kind != .Array {
|
||||
return {}, {}, false
|
||||
}
|
||||
return pointer_item, array_item, true
|
||||
}
|
||||
|
||||
container :: proc(value: Type, store: ^Store) -> (Node, bool) {
|
||||
item, ok := node(store, value)
|
||||
if !ok {
|
||||
return {}, false
|
||||
}
|
||||
if item.kind == .Array || item.kind == .Slice || (item.kind == .Pointer && item.many) {
|
||||
return item, true
|
||||
}
|
||||
pointer_node, array_node, array_ok := array_pointer(value, store)
|
||||
if !array_ok {
|
||||
return {}, false
|
||||
}
|
||||
array_node.mutable = pointer_node.mutable && array_node.mutable
|
||||
return array_node, true
|
||||
}
|
||||
|
||||
is_c_struct :: proc(value: Type, store: ^Store) -> bool {
|
||||
item, ok := node(store, value)
|
||||
return ok && item.kind == .Struct && item.c_layout
|
||||
}
|
||||
|
||||
pointer :: proc(store: ^Store, child: Type, mutable, many: bool) -> Type {
|
||||
return intern(store, Node{kind=.Pointer, child=child, mutable=mutable, many=many})
|
||||
pointer :: proc(
|
||||
store: ^Store,
|
||||
child: Type,
|
||||
mutable, many: bool,
|
||||
has_sentinel := false,
|
||||
sentinel: u64 = 0,
|
||||
) -> Type {
|
||||
return intern(store, Node{
|
||||
kind=.Pointer,
|
||||
child=child,
|
||||
mutable=mutable,
|
||||
many=many,
|
||||
has_sentinel=has_sentinel,
|
||||
sentinel=sentinel,
|
||||
})
|
||||
}
|
||||
|
||||
slice :: proc(store: ^Store, child: Type, mutable: bool, has_sentinel := false, sentinel: u64 = 0) -> Type {
|
||||
@@ -540,12 +583,57 @@ with_array_count :: proc(store: ^Store, value: Type, count: u64) -> Type {
|
||||
}
|
||||
|
||||
can_weaken_pointer :: proc(from, to: Type, store: ^Store) -> bool {
|
||||
from_node, from_ok := node(store, from)
|
||||
to_node, to_ok := node(store, to)
|
||||
if !from_ok || !to_ok || from_node.kind != .Pointer || to_node.kind != .Pointer ||
|
||||
from_node.many != to_node.many || (to_node.mutable && !from_node.mutable) {
|
||||
return false
|
||||
}
|
||||
if to_node.has_sentinel &&
|
||||
(!from_node.has_sentinel || from_node.sentinel != to_node.sentinel) {
|
||||
return false
|
||||
}
|
||||
same_child := from_node.child == to_node.child
|
||||
c_string := from_node.many && from_node.child == U8 && to_node.child == C_CHAR &&
|
||||
from_node.has_sentinel && from_node.sentinel == 0 && !to_node.mutable
|
||||
return same_child || c_string
|
||||
}
|
||||
|
||||
can_weaken_slice :: proc(from, to: Type, store: ^Store) -> bool {
|
||||
from_node, from_ok := node(store, from)
|
||||
to_node, to_ok := node(store, to)
|
||||
return from_ok && to_ok &&
|
||||
from_node.kind == .Pointer && to_node.kind == .Pointer &&
|
||||
from_node.child == to_node.child && from_node.many == to_node.many &&
|
||||
from_node.mutable && !to_node.mutable
|
||||
from_node.kind == .Slice && to_node.kind == .Slice &&
|
||||
from_node.child == to_node.child &&
|
||||
(!to_node.mutable || from_node.mutable) &&
|
||||
(!to_node.has_sentinel ||
|
||||
(from_node.has_sentinel && from_node.sentinel == to_node.sentinel))
|
||||
}
|
||||
|
||||
can_decay_array_pointer :: proc(from, to: Type, store: ^Store) -> bool {
|
||||
from_pointer, array, from_ok := array_pointer(from, store)
|
||||
to_node, to_ok := node(store, to)
|
||||
if !from_ok || !to_ok {
|
||||
return false
|
||||
}
|
||||
mutable := from_pointer.mutable && array.mutable
|
||||
if to_node.mutable && !mutable {
|
||||
return false
|
||||
}
|
||||
if to_node.has_sentinel &&
|
||||
(!array.has_sentinel || array.sentinel != to_node.sentinel) {
|
||||
return false
|
||||
}
|
||||
if to_node.kind == .Slice {
|
||||
return array.child == to_node.child
|
||||
}
|
||||
if to_node.kind != .Pointer || !to_node.many {
|
||||
return false
|
||||
}
|
||||
same_child := array.child == to_node.child
|
||||
c_string := array.child == U8 && to_node.child == C_CHAR &&
|
||||
array.has_sentinel && array.sentinel == 0 && !to_node.mutable
|
||||
return same_child || c_string
|
||||
}
|
||||
|
||||
is_opaque_struct :: proc(value: Type, store: ^Store) -> bool {
|
||||
|
||||
Reference in New Issue
Block a user