allocator interface (first pass)
This commit is contained in:
+1
-1
@@ -290,7 +290,7 @@ unwrap_coercions :: proc(m: ^hir.Module, id: hir.Expr_Id) -> hir.Expr_Id {
|
||||
cur := id
|
||||
for cur != hir.INVALID_EXPR && int(cur) < len(m.exprs) {
|
||||
#partial switch m.exprs[cur].kind {
|
||||
case .Retype, .Weaken_Slice, .Weaken_Pointer, .Decay_Array_Pointer, .Slice_Ptr,
|
||||
case .Retype, .Pointer_Cast, .Weaken_Slice, .Weaken_Pointer, .Decay_Array_Pointer, .Slice_Ptr,
|
||||
.Widen, .Sum_Widen, .Optional_Some, .C_Coerce, .Scalar_Cast:
|
||||
cur = m.exprs[cur].left
|
||||
case:
|
||||
|
||||
@@ -180,6 +180,21 @@ type_label :: proc(checker: ^Checker, value: types.Type) -> string {
|
||||
return types.name(value)
|
||||
}
|
||||
|
||||
is_ptr_cast_call :: proc(checker: ^Checker, expr: ast.Expr) -> bool {
|
||||
return expr.left == ast.INVALID_EXPR &&
|
||||
!symbol.is_valid(expr.qualifier) &&
|
||||
symbol_text(checker, expr.name) == "ptr_cast"
|
||||
}
|
||||
|
||||
valid_ptr_cast_child :: proc(checker: ^Checker, value: types.Type) -> bool {
|
||||
return types.is_valid(value) &&
|
||||
!types.is_void(value) &&
|
||||
!types.is_anyopaque(value) &&
|
||||
!types.is_function(value, &checker.module.types) &&
|
||||
(types.is_runtime_value(value, &checker.module.types) ||
|
||||
types.is_opaque_struct(value, &checker.module.types))
|
||||
}
|
||||
|
||||
is_type_metatype_syntax :: proc(checker: ^Checker, value: ast.Type_Syntax) -> bool {
|
||||
item, ok := types.node(&checker.module.types, value)
|
||||
return ok && item.name == u32(checker.type_symbol) && item.qualifier == 0
|
||||
@@ -958,7 +973,7 @@ function_pointer_type_for_template :: proc(
|
||||
defer delete(params, checker.allocator)
|
||||
function := checker.ast_module.functions[template]
|
||||
function_type := types.function(&checker.module.types, params, result, function.c_abi, function.variadic)
|
||||
pointer_type := types.pointer(&checker.module.types, function_type, false, true)
|
||||
pointer_type := types.pointer(&checker.module.types, function_type, false, false)
|
||||
spec := INVALID_SPEC
|
||||
if demanded == nil {
|
||||
if demand_spec {
|
||||
@@ -1970,6 +1985,42 @@ infer_expr :: proc(
|
||||
}
|
||||
continue
|
||||
}
|
||||
if is_ptr_cast_call(checker, expr) {
|
||||
if len(expr.args) != 2 {
|
||||
last = types.INVALID
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
child, child_ok := resolve_type_argument(checker, expr.args[0], pkg, file)
|
||||
operand := infer_nested_expr(checker, expr.args[1], locals, pkg, file, demanded, local_types)
|
||||
result := types.INVALID
|
||||
if child_ok && valid_ptr_cast_child(checker, child) {
|
||||
result, _ = types.replace_pointer_child(&checker.module.types, operand, child)
|
||||
}
|
||||
last = result
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
if callee_type, handled := infer_qualified_value_field_type(checker, expr, locals, pkg, file); handled {
|
||||
_, function_item, function_type, ok := types.function_pointer(callee_type, &checker.module.types)
|
||||
if !ok {
|
||||
last = types.INVALID
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
stack[frame_index].left = function_type
|
||||
stack[frame_index].args = make([]types.Type, len(expr.args), checker.allocator)
|
||||
stack[frame_index].stage = 6
|
||||
if len(expr.args) > 0 {
|
||||
append(&stack, Infer_Frame{expr=expr.args[0], template=ast.INVALID_FUNCTION})
|
||||
} else if valid_callable_arity(function_item, 0) {
|
||||
last = function_item.child
|
||||
delete(stack[frame_index].args, checker.allocator)
|
||||
stack[frame_index].args = nil
|
||||
_ = pop(&stack)
|
||||
}
|
||||
continue
|
||||
}
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file)
|
||||
template := ast.INVALID_FUNCTION
|
||||
if available {
|
||||
@@ -3477,6 +3528,126 @@ find_struct_field :: proc(checker: ^Checker, struct_type: types.Type, name: symb
|
||||
return 0, {}, false
|
||||
}
|
||||
|
||||
field_type_from_value :: proc(checker: ^Checker, expr: ast.Expr, base_type: types.Type) -> types.Type {
|
||||
store := &checker.module.types
|
||||
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 types.USIZE
|
||||
}
|
||||
if field_name == "ptr" &&
|
||||
(item.kind == .Slice || types.is_pointer(base_type, store)) {
|
||||
return container_pointer_type(store, item)
|
||||
}
|
||||
}
|
||||
value_type := base_type
|
||||
if types.is_pointer(value_type, store) {
|
||||
value_type = types.child_type(value_type, store)
|
||||
}
|
||||
_, field, ok := find_struct_field(checker, value_type, expr.name)
|
||||
return field.type if ok else types.INVALID
|
||||
}
|
||||
|
||||
infer_qualified_value_field_type :: proc(
|
||||
checker: ^Checker,
|
||||
expr: ast.Expr,
|
||||
locals: []Infer_Local,
|
||||
pkg: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
) -> (types.Type, bool) {
|
||||
if !symbol.is_valid(expr.qualifier) ||
|
||||
find_import(checker, file, expr.qualifier) != ast.INVALID_IMPORT {
|
||||
return types.INVALID, false
|
||||
}
|
||||
base_type := find_infer_local(locals, expr.qualifier)
|
||||
if !types.is_valid(base_type) {
|
||||
if global := find_global(checker, expr.qualifier, pkg); global != ast.INVALID_GLOBAL {
|
||||
base_type = checker.global_types[global]
|
||||
}
|
||||
}
|
||||
if !types.is_valid(base_type) {
|
||||
return types.INVALID, false
|
||||
}
|
||||
return field_type_from_value(checker, expr, base_type), true
|
||||
}
|
||||
|
||||
build_field_from_value :: proc(
|
||||
checker: ^Checker,
|
||||
expr: ast.Expr,
|
||||
base: hir.Expr_Id,
|
||||
base_type: types.Type,
|
||||
) -> (hir.Expr_Id, bool) {
|
||||
store := &checker.module.types
|
||||
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{
|
||||
kind=.Length, span=expr.span, type=types.USIZE, left=base,
|
||||
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
}), true
|
||||
}
|
||||
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=container_pointer_type(store, item), left=base,
|
||||
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
}), true
|
||||
}
|
||||
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), false
|
||||
}
|
||||
}
|
||||
value_type := base_type
|
||||
if types.is_pointer(value_type, store) {
|
||||
value_type = types.child_type(value_type, store)
|
||||
}
|
||||
index, field, ok := find_struct_field(checker, value_type, expr.name)
|
||||
if !ok {
|
||||
id := source.addf(checker.diagnostics, expr.span, "unknown struct field '%s'", symbol_text(checker, expr.name))
|
||||
return invalid_hir_expr(checker, expr.span, id), false
|
||||
}
|
||||
if types.is_void(field.type) {
|
||||
id := source.addf(checker.diagnostics, expr.span, "variant '%s' has no payload to read", symbol_text(checker, expr.name))
|
||||
return invalid_hir_expr(checker, expr.span, id), false
|
||||
}
|
||||
return add_hir_expr(checker, hir.Expr{
|
||||
kind=.Field, span=expr.span, type=field.type, integer=i64(index), left=base,
|
||||
target=hir.INVALID_REF, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
}), true
|
||||
}
|
||||
|
||||
build_qualified_value_field :: proc(
|
||||
checker: ^Checker,
|
||||
expr: ast.Expr,
|
||||
locals: []Build_Local,
|
||||
global_reads: ^[dynamic]hir.Global_Id,
|
||||
pkg: ast.Package_Id,
|
||||
file: ast.File_Id,
|
||||
) -> (hir.Expr_Id, bool, bool) {
|
||||
if !symbol.is_valid(expr.qualifier) ||
|
||||
find_import(checker, file, expr.qualifier, true) != ast.INVALID_IMPORT {
|
||||
return hir.INVALID_EXPR, false, false
|
||||
}
|
||||
if local, ok := find_build_local(locals, expr.qualifier); ok {
|
||||
base := add_hir_expr(checker, hir.Expr{
|
||||
kind=.Local, span=expr.span, type=local.type, target=hir.local_ref(local.id),
|
||||
left=hir.INVALID_EXPR, right=hir.INVALID_EXPR, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
value, ok := build_field_from_value(checker, expr, base, local.type)
|
||||
return value, true, ok
|
||||
}
|
||||
if global := find_global(checker, expr.qualifier, pkg); global != ast.INVALID_GLOBAL {
|
||||
base := build_global_reference(checker, global, expr.span, global_reads)
|
||||
value, ok := build_field_from_value(checker, expr, base, checker.global_types[global])
|
||||
return value, true, ok
|
||||
}
|
||||
return hir.INVALID_EXPR, false, false
|
||||
}
|
||||
|
||||
find_enum_member :: proc(checker: ^Checker, enum_type: types.Type, name: symbol.Id) -> (types.Enum_Member, bool) {
|
||||
for member in types.enum_members_for(&checker.module.types, enum_type) {
|
||||
if member.name == u32(name) {
|
||||
@@ -3581,7 +3752,15 @@ build_function_value :: proc(
|
||||
}
|
||||
defer delete(params, checker.allocator)
|
||||
function_type := types.function(&checker.module.types, params, result, function.c_abi, function.variadic)
|
||||
pointer_type := types.pointer(&checker.module.types, function_type, false, true)
|
||||
pointer_type := types.pointer(&checker.module.types, function_type, false, false)
|
||||
expected_pointer := expected
|
||||
if types.is_optional(expected_pointer, &checker.module.types) {
|
||||
expected_pointer = types.child_type(expected_pointer, &checker.module.types)
|
||||
}
|
||||
if _, _, expected_function, ok := types.function_pointer(expected_pointer, &checker.module.types); ok &&
|
||||
types.equal(expected_function, function_type) {
|
||||
pointer_type = expected_pointer
|
||||
}
|
||||
spec := find_spec(checker, template, params)
|
||||
if spec == INVALID_SPEC {
|
||||
id := source.addf(
|
||||
@@ -4553,6 +4732,41 @@ build_expr :: proc(
|
||||
append(&stack, Build_Expr_Frame{expr=expr.left, expected=types.INVALID, template=ast.INVALID_FUNCTION})
|
||||
continue
|
||||
}
|
||||
if is_ptr_cast_call(checker, expr) {
|
||||
if len(expr.args) != 2 {
|
||||
id := source.addf(checker.diagnostics, expr.span, "ptr_cast expects 2 arguments, got %d", len(expr.args))
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
target, target_ok := resolve_type_argument(checker, expr.args[0], pkg, file)
|
||||
if !target_ok {
|
||||
id := source.add(checker.diagnostics, checker.ast_module.exprs[expr.args[0]].span, "ptr_cast target must be a type")
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
if !valid_ptr_cast_child(checker, target) {
|
||||
id := source.addf(checker.diagnostics, checker.ast_module.exprs[expr.args[0]].span, "ptr_cast target must be a sized runtime object type, got %s", type_label(checker, target))
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
stack[frame_index].target_type = target
|
||||
stack[frame_index].stage = 9
|
||||
append(&stack, Build_Expr_Frame{expr=expr.args[1], expected=types.INVALID, template=ast.INVALID_FUNCTION})
|
||||
continue
|
||||
}
|
||||
if callee, handled, ok := build_qualified_value_field(checker, expr, locals, global_reads, pkg, file); handled {
|
||||
if !ok {
|
||||
last = callee
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
stack[frame_index].stage = 6
|
||||
last = callee
|
||||
continue
|
||||
}
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file, true)
|
||||
if !available {
|
||||
id := add_package_resolution_diagnostic(checker, expr, file)
|
||||
@@ -4992,6 +5206,24 @@ build_expr :: proc(
|
||||
}
|
||||
_ = pop(&stack)
|
||||
}
|
||||
if frame.stage == 9 {
|
||||
result, ok := types.replace_pointer_child(&checker.module.types, checker.module.exprs[last].type, frame.target_type)
|
||||
if !ok {
|
||||
id := source.add(checker.diagnostics, expr.span, "ptr_cast operand must be a pointer or optional pointer")
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
} else {
|
||||
last = add_hir_expr(checker, hir.Expr{
|
||||
kind=.Pointer_Cast,
|
||||
span=expr.span,
|
||||
type=result,
|
||||
left=last,
|
||||
target=hir.INVALID_REF,
|
||||
right=hir.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
_ = pop(&stack)
|
||||
}
|
||||
}
|
||||
return last
|
||||
}
|
||||
|
||||
@@ -103,6 +103,7 @@ Expr_Kind :: enum u8 {
|
||||
C_Vararg_Promote,
|
||||
Retype,
|
||||
Scalar_Cast,
|
||||
Pointer_Cast,
|
||||
Weaken_Pointer,
|
||||
Weaken_Slice,
|
||||
Decay_Array_Pointer,
|
||||
|
||||
@@ -100,6 +100,7 @@ Opcode :: enum u8 {
|
||||
C_Vararg_Promote,
|
||||
Retype,
|
||||
Scalar_Cast,
|
||||
Pointer_Cast,
|
||||
Weaken_Pointer,
|
||||
Weaken_Slice,
|
||||
Decay_Array_Pointer,
|
||||
|
||||
@@ -18,6 +18,7 @@ keyword_kind :: proc(text: string) -> token.Kind {
|
||||
case "c_func": return .Keyword_C_Func
|
||||
case "struct": return .Keyword_Struct
|
||||
case "c_struct": return .Keyword_C_Struct
|
||||
case "opaque": return .Keyword_Opaque
|
||||
case "union": return .Keyword_Union
|
||||
case "enum": return .Keyword_Enum
|
||||
case "distinct": return .Keyword_Distinct
|
||||
@@ -44,6 +45,7 @@ keyword_kind :: proc(text: string) -> token.Kind {
|
||||
case "true": return .Keyword_True
|
||||
case "false": return .Keyword_False
|
||||
case "void": return .Keyword_Void
|
||||
case "anyopaque": return .Keyword_Anyopaque
|
||||
case "bool": return .Keyword_Bool
|
||||
case "int": return .Keyword_Int
|
||||
case "float": return .Keyword_Float
|
||||
|
||||
@@ -256,7 +256,7 @@ valid_value :: proc(
|
||||
.Load_Global, .Function_Address, .Address_Of, .Load, .Union_Tag, .Slice, .Length, .Slice_Ptr,
|
||||
.Fallible_Error, .Extract, .Select, .Unwrap,
|
||||
.Optional_Is_Some, .Optional_Value, .Orelse,
|
||||
.Widen, .Sum_Widen, .C_Coerce, .C_Vararg_Promote, .Retype, .Scalar_Cast, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer,
|
||||
.Widen, .Sum_Widen, .C_Coerce, .C_Vararg_Promote, .Retype, .Scalar_Cast, .Pointer_Cast, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer,
|
||||
.Neg_Checked, .Add_Checked, .Sub_Checked, .Mul_Checked, .Div_Checked, .Pointer_Add, .Not, .Compare, .Call:
|
||||
return true
|
||||
case .Address_Global, .Alloca, .Index_Address, .Field_Address, .Orelse_Begin,
|
||||
@@ -1511,6 +1511,13 @@ 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 .Pointer_Cast:
|
||||
if !valid_instruction(instructions, instruction.a) ||
|
||||
!types.same_pointer_shape(instructions[instruction.a].type, instruction.type, &emitter.module.types) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid pointer cast operand")
|
||||
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) {
|
||||
|
||||
@@ -173,6 +173,9 @@ translate_c_type :: proc(
|
||||
case .C_Longdouble: translated = types.C_LONGDOUBLE
|
||||
case .Pointer:
|
||||
child := translate_c_type(state, result, item.child, pkg, record_mapping, type_mapping)
|
||||
if child == types.VOID {
|
||||
child = types.ANYOPAQUE
|
||||
}
|
||||
if types.is_valid(child) {
|
||||
pointer := types.pointer(&state.module.type_store, child, item.mutable, true)
|
||||
translated = types.optional(&state.module.type_store, pointer)
|
||||
@@ -964,7 +967,7 @@ load_header :: proc(state: ^State, path: string, import_span: source.Span) -> as
|
||||
name = fmt.tprintf("__c_record_%d", len(state.record_types))
|
||||
}
|
||||
record_type = types.named(&state.module.type_store, u32(pkg_id), u32(symbol.intern(state.symbols, name)))
|
||||
_ = types.define_record(&state.module.type_store, record_type, nil, true, true, record.kind == .Union)
|
||||
_ = types.define_record(&state.module.type_store, record_type, nil, false, true, record.kind == .Union)
|
||||
append(&state.record_identities, strings.clone(record.identity, state.allocator))
|
||||
append(&state.record_types, record_type)
|
||||
}
|
||||
|
||||
@@ -665,7 +665,7 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
||||
})
|
||||
}
|
||||
_ = pop(&stack)
|
||||
case .Widen, .Sum_Widen, .C_Coerce, .C_Vararg_Promote, .Retype, .Scalar_Cast, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer:
|
||||
case .Widen, .Sum_Widen, .C_Coerce, .C_Vararg_Promote, .Retype, .Scalar_Cast, .Pointer_Cast, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer:
|
||||
stack[frame_index].stage = 1
|
||||
append(&stack, Lower_Expr_Frame{expr=expr.left})
|
||||
case .Negate:
|
||||
@@ -727,6 +727,7 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
||||
case .C_Vararg_Promote: op = .C_Vararg_Promote
|
||||
case .Retype: op = .Retype
|
||||
case .Scalar_Cast: op = .Scalar_Cast
|
||||
case .Pointer_Cast: op = .Pointer_Cast
|
||||
case: op = .Widen
|
||||
}
|
||||
last = append_instruction(state, ir.Instruction{
|
||||
|
||||
@@ -113,7 +113,8 @@ is_type_token :: proc(kind: token.Kind) -> bool {
|
||||
.Keyword_C_Short, .Keyword_C_Ushort, .Keyword_C_Int, .Keyword_C_Uint,
|
||||
.Keyword_C_Long, .Keyword_C_Ulong, .Keyword_C_Longlong, .Keyword_C_Ulonglong,
|
||||
.Keyword_C_Float, .Keyword_C_Double, .Keyword_C_Longdouble,
|
||||
.Keyword_Void, .Keyword_Bool, .Keyword_Func, .Keyword_C_Func, .Identifier, .Question, .At, .Star, .Left_Bracket:
|
||||
.Keyword_Void, .Keyword_Anyopaque, .Keyword_Bool, .Keyword_Func, .Keyword_C_Func,
|
||||
.Identifier, .Question, .At, .Star, .Left_Bracket:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -326,6 +327,9 @@ parse_type_atom :: proc(parser: ^Parser) -> ast.Type_Syntax {
|
||||
case .Keyword_Void:
|
||||
advance(parser)
|
||||
return types.VOID
|
||||
case .Keyword_Anyopaque:
|
||||
advance(parser)
|
||||
return types.ANYOPAQUE
|
||||
case .Keyword_Bool:
|
||||
advance(parser)
|
||||
return types.BOOL
|
||||
@@ -600,7 +604,7 @@ parse_integer_magnitude :: proc(text: string) -> (u64, bool) {
|
||||
parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
||||
tok := current(parser)
|
||||
#partial switch tok.kind {
|
||||
case .Keyword_Int, .Keyword_Float, .Keyword_Range, .Keyword_Void, .Keyword_Bool:
|
||||
case .Keyword_Int, .Keyword_Float, .Keyword_Range, .Keyword_Void, .Keyword_Anyopaque, .Keyword_Bool:
|
||||
start := tok
|
||||
target := parse_type_atom(parser)
|
||||
return add_expr(parser, ast.Expr{
|
||||
@@ -2214,6 +2218,13 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout: bool, is_unio
|
||||
ended_by_newline := current(parser).kind == .Newline
|
||||
skip_newlines(parser)
|
||||
if current(parser).kind != .Left_Brace {
|
||||
if c_layout {
|
||||
source.add(parser.diagnostics, start.span, "c_struct declarations require a body; use 'opaque' for incomplete types")
|
||||
if !ended_by_newline {
|
||||
_ = finish_statement(parser)
|
||||
}
|
||||
return
|
||||
}
|
||||
if !c_layout {
|
||||
source.add(
|
||||
parser.diagnostics, start.span,
|
||||
@@ -2242,6 +2253,18 @@ parse_struct :: proc(parser: ^Parser, name: token.Token, c_layout: bool, is_unio
|
||||
_ = finish_statement(parser)
|
||||
}
|
||||
|
||||
parse_opaque :: proc(parser: ^Parser, name: token.Token) {
|
||||
start := advance(parser)
|
||||
id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol))
|
||||
if !types.define_record(&parser.module.type_store, id, nil, false, true, false) {
|
||||
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||
}
|
||||
if current(parser).kind == .Left_Brace {
|
||||
source.add(parser.diagnostics, start.span, "opaque declarations do not have a body")
|
||||
}
|
||||
_ = finish_statement(parser)
|
||||
}
|
||||
|
||||
// synthesize_union_tag builds the anonymous runtime discriminant enum for a tagged
|
||||
// union: one member per variant, valued by the program-global (name, payload-type)
|
||||
// ID. The declared tag enum, if any, remains only the validation surface.
|
||||
@@ -2573,6 +2596,10 @@ parse_top_level :: proc(parser: ^Parser) {
|
||||
parse_struct(parser, name, current(parser).kind == .Keyword_C_Struct)
|
||||
return
|
||||
}
|
||||
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Opaque {
|
||||
parse_opaque(parser, name)
|
||||
return
|
||||
}
|
||||
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Union {
|
||||
parse_struct(parser, name, false, is_union=true)
|
||||
return
|
||||
|
||||
@@ -54,6 +54,7 @@ Kind :: enum u8 {
|
||||
Keyword_C_Func,
|
||||
Keyword_Struct,
|
||||
Keyword_C_Struct,
|
||||
Keyword_Opaque,
|
||||
Keyword_Union,
|
||||
Keyword_Enum,
|
||||
Keyword_Distinct,
|
||||
@@ -80,6 +81,7 @@ Kind :: enum u8 {
|
||||
Keyword_True,
|
||||
Keyword_False,
|
||||
Keyword_Void,
|
||||
Keyword_Anyopaque,
|
||||
Keyword_Bool,
|
||||
Keyword_Int,
|
||||
Keyword_Float,
|
||||
|
||||
@@ -99,7 +99,11 @@ render_type :: proc(b: ^strings.Builder, result: ^cimport.Result, id: cimport.Ty
|
||||
if item.mutable {
|
||||
strings.write_string(b, "mut ")
|
||||
}
|
||||
render_type(b, result, item.child, record_names)
|
||||
if int(item.child) >= 0 && int(item.child) < len(result.types) && result.types[item.child].kind == .Void {
|
||||
strings.write_string(b, "anyopaque")
|
||||
} else {
|
||||
render_type(b, result, item.child, record_names)
|
||||
}
|
||||
case .Array:
|
||||
fmt.sbprintf(b, "[%d]", item.count)
|
||||
render_type(b, result, item.child, record_names)
|
||||
@@ -157,7 +161,7 @@ emit_records :: proc(b: ^strings.Builder, result: ^cimport.Result, record_names:
|
||||
}
|
||||
if !record.complete || len(record.reason) > 0 {
|
||||
// opaque / pointer-only struct
|
||||
fmt.sbprintf(b, "%s :: c_struct\n", name)
|
||||
fmt.sbprintf(b, "%s :: opaque\n", name)
|
||||
wrote = true
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ C_LONGDOUBLE :: Type(28)
|
||||
BOOL :: Type(29)
|
||||
FLOAT :: Type(30)
|
||||
RANGE :: Type(31)
|
||||
ANYOPAQUE :: Type(32)
|
||||
|
||||
DYNAMIC_START :: Type(64)
|
||||
|
||||
@@ -56,6 +57,7 @@ Numeric_Category :: enum u8 {
|
||||
Kind :: enum u8 {
|
||||
Invalid,
|
||||
Void,
|
||||
Anyopaque,
|
||||
Int_Constraint,
|
||||
Float_Constraint,
|
||||
Range_Constraint,
|
||||
@@ -555,6 +557,8 @@ kind :: proc(value: Type, store: ^Store = nil) -> Kind {
|
||||
return .Invalid
|
||||
case VOID:
|
||||
return .Void
|
||||
case ANYOPAQUE:
|
||||
return .Anyopaque
|
||||
case INT:
|
||||
return .Int_Constraint
|
||||
case FLOAT:
|
||||
@@ -595,6 +599,10 @@ is_void :: proc(value: Type) -> bool {
|
||||
return value == VOID
|
||||
}
|
||||
|
||||
is_anyopaque :: proc(value: Type) -> bool {
|
||||
return value == ANYOPAQUE
|
||||
}
|
||||
|
||||
is_bool :: proc(value: Type) -> bool {
|
||||
return value == BOOL
|
||||
}
|
||||
@@ -915,8 +923,19 @@ is_runtime_value :: proc(value: Type, store: ^Store, depth := 0) -> bool {
|
||||
if value_kind == .Scalar || value_kind == .Pointer {
|
||||
return true
|
||||
}
|
||||
if value_kind == .Slice || value_kind == .Array || value_kind == .Range || value_kind == .Optional {
|
||||
return !contains_c_struct_by_value(value, store)
|
||||
if value_kind == .Slice || value_kind == .Array || value_kind == .Range {
|
||||
item, ok := node(store, value)
|
||||
return ok && is_runtime_value(item.child, store, depth+1) && !contains_c_struct_by_value(value, store)
|
||||
}
|
||||
if value_kind == .Optional {
|
||||
item, ok := node(store, value)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
if is_pointer(item.child, store) {
|
||||
return true
|
||||
}
|
||||
return is_runtime_value(item.child, store, depth+1) && !contains_c_struct_by_value(value, store)
|
||||
}
|
||||
if value_kind == .Struct || value_kind == .Union {
|
||||
item, ok := node(store, value)
|
||||
@@ -1204,6 +1223,42 @@ function_pointer :: proc(value: Type, store: ^Store) -> (pointer_item, function_
|
||||
return pointer_node, function_node, pointer_node.child, true
|
||||
}
|
||||
|
||||
replace_pointer_child :: proc(store: ^Store, value, child: Type) -> (Type, bool) {
|
||||
item, ok := node(store, value)
|
||||
if !ok {
|
||||
return INVALID, false
|
||||
}
|
||||
if item.kind == .Optional {
|
||||
replaced, replaced_ok := replace_pointer_child(store, item.child, child)
|
||||
if !replaced_ok || !is_pointer(replaced, store) {
|
||||
return INVALID, false
|
||||
}
|
||||
return optional(store, replaced), true
|
||||
}
|
||||
if item.kind != .Pointer {
|
||||
return INVALID, false
|
||||
}
|
||||
item.child = child
|
||||
return intern(store, item), true
|
||||
}
|
||||
|
||||
same_pointer_shape :: proc(left, right: Type, store: ^Store) -> bool {
|
||||
left_item, left_ok := node(store, left)
|
||||
right_item, right_ok := node(store, right)
|
||||
if !left_ok || !right_ok {
|
||||
return false
|
||||
}
|
||||
if left_item.kind == .Optional || right_item.kind == .Optional {
|
||||
return left_item.kind == .Optional && right_item.kind == .Optional &&
|
||||
same_pointer_shape(left_item.child, right_item.child, store)
|
||||
}
|
||||
return left_item.kind == .Pointer && right_item.kind == .Pointer &&
|
||||
left_item.many == right_item.many &&
|
||||
left_item.mutable == right_item.mutable &&
|
||||
left_item.has_sentinel == right_item.has_sentinel &&
|
||||
(!left_item.has_sentinel || left_item.sentinel == right_item.sentinel)
|
||||
}
|
||||
|
||||
is_c_struct :: proc(value: Type, store: ^Store) -> bool {
|
||||
item, ok := node(store, value)
|
||||
return ok && (item.kind == .Struct || item.kind == .Union) && item.c_layout
|
||||
@@ -1318,6 +1373,10 @@ 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 == .Optional || to_node.kind == .Optional) {
|
||||
return from_node.kind == .Optional && to_node.kind == .Optional &&
|
||||
can_weaken_pointer(from_node.child, to_node.child, store)
|
||||
}
|
||||
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
|
||||
@@ -1327,9 +1386,12 @@ can_weaken_pointer :: proc(from, to: Type, store: ^Store) -> bool {
|
||||
return false
|
||||
}
|
||||
same_child := from_node.child == to_node.child
|
||||
anyopaque_erasure := to_node.child == ANYOPAQUE &&
|
||||
(is_runtime_value(from_node.child, store) ||
|
||||
is_opaque_struct(from_node.child, store))
|
||||
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
|
||||
return same_child || anyopaque_erasure || c_string
|
||||
}
|
||||
|
||||
can_weaken_slice :: proc(from, to: Type, store: ^Store) -> bool {
|
||||
@@ -1594,6 +1656,7 @@ name :: proc(value: Type) -> string {
|
||||
switch value {
|
||||
case INVALID: return "<invalid>"
|
||||
case VOID: return "void"
|
||||
case ANYOPAQUE: return "anyopaque"
|
||||
case BOOL: return "bool"
|
||||
case INT: return "int"
|
||||
case FLOAT: return "float"
|
||||
|
||||
Reference in New Issue
Block a user