stlib arraylist
This commit is contained in:
@@ -130,6 +130,19 @@ Import_Index_Entry :: struct {
|
||||
id: ast.Import_Id,
|
||||
}
|
||||
|
||||
Type_Factory_Entry :: struct {
|
||||
template: ast.Function_Id,
|
||||
values: []Comptime_Value,
|
||||
result: types.Type,
|
||||
resolving: bool,
|
||||
}
|
||||
|
||||
Generated_Type_Entry :: struct {
|
||||
expr: ast.Expr_Id,
|
||||
values: []Comptime_Value,
|
||||
result: types.Type,
|
||||
}
|
||||
|
||||
Checker :: struct {
|
||||
ast_module: ^ast.Module,
|
||||
diagnostics: ^source.Diagnostics,
|
||||
@@ -169,6 +182,8 @@ Checker :: struct {
|
||||
current_result: types.Type,
|
||||
current_build_ctx: ^Build_Ctx,
|
||||
current_comptime_values: []Comptime_Value,
|
||||
type_factories: [dynamic]Type_Factory_Entry,
|
||||
generated_types: [dynamic]Generated_Type_Entry,
|
||||
target: target.Target,
|
||||
allocator: mem.Allocator,
|
||||
}
|
||||
@@ -340,6 +355,8 @@ write_type_label :: proc(checker: ^Checker, builder: ^strings.Builder, value: ty
|
||||
write_type_label(checker, builder, item.child)
|
||||
strings.write_string(builder, " ! ")
|
||||
write_type_label(checker, builder, item.extra)
|
||||
case .Type_Call:
|
||||
strings.write_string(builder, "<type factory call>")
|
||||
case .Struct:
|
||||
strings.write_string(builder, "struct")
|
||||
case .Union:
|
||||
@@ -597,6 +614,8 @@ type_from_syntax :: proc(
|
||||
if params_changed || result != item.child {
|
||||
return types.function(store, resolved_params, result, item.c_abi, item.variadic)
|
||||
}
|
||||
case .Type_Call:
|
||||
return resolve_type_factory_call(checker, ast.Expr_Id(item.count_expr), pkg, file)
|
||||
}
|
||||
if changed {
|
||||
return types.intern(store, item)
|
||||
@@ -1130,10 +1149,133 @@ resolve_type_argument :: proc(
|
||||
value := types.find_named(&checker.module.types, u32(target_pkg), u32(expr.name), file=u32(expr_lookup_file(expr, file)))
|
||||
value = types.resolve_alias(value, &checker.module.types)
|
||||
return value, types.is_valid(value)
|
||||
case .Call:
|
||||
value := resolve_type_factory_call(checker, expr_id, pkg, file)
|
||||
return value, types.is_valid(value)
|
||||
}
|
||||
return types.INVALID, false
|
||||
}
|
||||
|
||||
clone_comptime_values :: proc(values: []Comptime_Value, allocator: mem.Allocator) -> []Comptime_Value {
|
||||
result := make([]Comptime_Value, len(values), allocator)
|
||||
copy(result, values)
|
||||
return result
|
||||
}
|
||||
|
||||
resolve_generated_struct_type :: proc(checker: ^Checker, expr_id: ast.Expr_Id, pkg: ast.Package_Id, file: ast.File_Id) -> types.Type {
|
||||
for entry in checker.generated_types {
|
||||
if entry.expr == expr_id && comptime_values_equal(entry.values, checker.current_comptime_values) {
|
||||
return entry.result
|
||||
}
|
||||
}
|
||||
if expr_id == ast.INVALID_EXPR || int(expr_id) >= len(checker.ast_module.exprs) {
|
||||
return types.INVALID
|
||||
}
|
||||
expr := checker.ast_module.exprs[expr_id]
|
||||
field_start := int(u32(expr.integer>>32))
|
||||
field_count := int(u32(expr.integer))
|
||||
if field_start < 0 || field_count < 0 || field_start+field_count > len(checker.ast_module.type_fields) {
|
||||
return types.INVALID
|
||||
}
|
||||
template_fields := checker.ast_module.type_fields[field_start:field_start+field_count]
|
||||
fields := make([]types.Field, len(template_fields), checker.allocator)
|
||||
defer delete(fields, checker.allocator)
|
||||
for field, index in template_fields {
|
||||
resolved := type_from_syntax(checker, field.type, pkg, file)
|
||||
if !is_runtime_type(checker, resolved) || types.is_void(resolved) {
|
||||
source.addf(checker.diagnostics, expr.span, "anonymous struct field '%s' requires a concrete runtime type, got %s", symbol_text(checker, symbol.Id(field.name)), type_label(checker, resolved))
|
||||
return types.INVALID
|
||||
}
|
||||
fields[index] = types.Field{name=field.name, type=resolved}
|
||||
}
|
||||
result := types.struct_generated(&checker.module.types, fields)
|
||||
append(&checker.generated_types, Generated_Type_Entry{
|
||||
expr=expr_id,
|
||||
values=clone_comptime_values(checker.current_comptime_values, checker.allocator),
|
||||
result=result,
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
resolve_type_factory_call :: proc(checker: ^Checker, expr_id: ast.Expr_Id, pkg: ast.Package_Id, file: ast.File_Id) -> types.Type {
|
||||
if expr_id == ast.INVALID_EXPR || int(expr_id) >= len(checker.ast_module.exprs) {
|
||||
return types.INVALID
|
||||
}
|
||||
expr := checker.ast_module.exprs[expr_id]
|
||||
if expr.kind != .Call || expr.left != ast.INVALID_EXPR {
|
||||
source.add(checker.diagnostics, expr.span, "type position requires a direct type-factory call")
|
||||
return types.INVALID
|
||||
}
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file, true)
|
||||
if !available {
|
||||
return types.INVALID
|
||||
}
|
||||
template := find_template(checker, expr.name, target_pkg, expr_lookup_file(expr, file))
|
||||
if template == ast.INVALID_FUNCTION || int(template) >= len(checker.ast_module.functions) {
|
||||
source.addf(checker.diagnostics, expr.span, "unknown type factory '%s'", symbol_text(checker, expr.name))
|
||||
return types.INVALID
|
||||
}
|
||||
function := checker.ast_module.functions[template]
|
||||
if !is_type_metatype_syntax(checker, function.result) || types.is_valid(function.error) {
|
||||
source.addf(checker.diagnostics, expr.span, "function '%s' does not return a type", symbol_text(checker, expr.name))
|
||||
return types.INVALID
|
||||
}
|
||||
for param in function.params {
|
||||
if !param.comptime_value {
|
||||
source.addf(checker.diagnostics, param.span, "type-factory parameter '%s' must be comptime", symbol_text(checker, param.name))
|
||||
return types.INVALID
|
||||
}
|
||||
}
|
||||
if !valid_call_arity(function, len(expr.args)) {
|
||||
source.addf(checker.diagnostics, expr.span, "type factory '%s' expects %d arguments, got %d", symbol_text(checker, expr.name), len(function.params), len(expr.args))
|
||||
return types.INVALID
|
||||
}
|
||||
values, ok := collect_comptime_values(checker, function, expr.args, pkg, file, true, checker.current_comptime_values)
|
||||
defer delete(values, checker.allocator)
|
||||
if !ok {
|
||||
return types.INVALID
|
||||
}
|
||||
// A generic function's declaration is validated before it has a specialization.
|
||||
// Leave calls containing its unresolved type parameters pending until then.
|
||||
for value in values {
|
||||
if value.kind != .Type {
|
||||
continue
|
||||
}
|
||||
if item, item_ok := types.node(&checker.module.types, value.type); item_ok && item.kind == .Named && !item.declared {
|
||||
return types.INVALID
|
||||
}
|
||||
}
|
||||
for &entry in checker.type_factories {
|
||||
if entry.template != template || !comptime_values_equal(entry.values, values) {
|
||||
continue
|
||||
}
|
||||
if entry.resolving {
|
||||
source.addf(checker.diagnostics, expr.span, "recursive type-factory specialization of '%s'", symbol_text(checker, expr.name))
|
||||
return types.INVALID
|
||||
}
|
||||
return entry.result
|
||||
}
|
||||
entry_index := len(checker.type_factories)
|
||||
append(&checker.type_factories, Type_Factory_Entry{
|
||||
template=template,
|
||||
values=clone_comptime_values(values, checker.allocator),
|
||||
result=types.INVALID,
|
||||
resolving=true,
|
||||
})
|
||||
state := ct_state_make(checker, pkg, file)
|
||||
value, flow, eval_ok := ct_eval_call_expr(&state, expr, function.result, 0)
|
||||
result := types.INVALID
|
||||
if eval_ok && flow.kind == .Normal && value != INVALID_CT_VALUE && int(value) < len(state.values) && state.values[value].kind == .Type {
|
||||
result = types.Type(state.values[value].index)
|
||||
} else if state.diagnostic == source.INVALID_DIAGNOSTIC {
|
||||
source.addf(checker.diagnostics, expr.span, "type factory '%s' did not return a type", symbol_text(checker, expr.name))
|
||||
}
|
||||
ct_state_destroy(&state)
|
||||
checker.type_factories[entry_index].result = result
|
||||
checker.type_factories[entry_index].resolving = false
|
||||
return result
|
||||
}
|
||||
|
||||
collect_comptime_values :: proc(
|
||||
checker: ^Checker,
|
||||
function: ast.Function,
|
||||
@@ -1362,7 +1504,7 @@ mark_expr_imports_used :: proc(checker: ^Checker, expr_id: ast.Expr_Id, file: as
|
||||
}
|
||||
case .Add, .Sub, .Mul, .Div, .Index, .Orelse, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or, .Range:
|
||||
append(&stack, expr.left, expr.right)
|
||||
case .Invalid, .Integer, .Float, .String, .Bool, .None, .Undefined, .Type, .Name:
|
||||
case .Invalid, .Integer, .Float, .String, .Bool, .None, .Undefined, .Type, .Name, .Anonymous_Struct_Type:
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2173,6 +2315,9 @@ infer_expr :: proc(
|
||||
case .Type:
|
||||
last = types.INVALID
|
||||
_ = pop(&stack)
|
||||
case .Anonymous_Struct_Type:
|
||||
last = types.INVALID
|
||||
_ = pop(&stack)
|
||||
case .Integer:
|
||||
last = types.I64
|
||||
if expr.integer <= 0x7fff_ffff_ffff_ffff {
|
||||
@@ -4789,7 +4934,10 @@ build_compound_expr :: proc(
|
||||
})
|
||||
case .Struct_Literal:
|
||||
struct_type := types.INVALID
|
||||
if symbol.is_valid(expr.name) {
|
||||
if expr.left != ast.INVALID_EXPR {
|
||||
struct_type, _ = resolve_type_argument(checker, expr.left, pkg, file)
|
||||
struct_type = types.resolve_alias(struct_type, store)
|
||||
} else if symbol.is_valid(expr.name) {
|
||||
target_pkg, available := expr_package(checker, expr, pkg, file, true)
|
||||
struct_type = types.find_named(store, u32(target_pkg), u32(expr.name), file=u32(expr_lookup_file(expr, file))) if available else types.INVALID
|
||||
struct_type = types.resolve_alias(struct_type, store)
|
||||
@@ -4977,7 +5125,7 @@ build_expr :: proc(
|
||||
template := ast.Function_Id(u32(expr.integer))
|
||||
last = build_function_value(checker, template, expr.span, frame.expected)
|
||||
_ = pop(&stack)
|
||||
case .Type:
|
||||
case .Type, .Anonymous_Struct_Type:
|
||||
id := source.add(checker.diagnostics, expr.span, "type is not a runtime value")
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
@@ -5294,6 +5442,12 @@ build_expr :: proc(
|
||||
continue
|
||||
}
|
||||
function := checker.ast_module.functions[template]
|
||||
if is_type_metatype_syntax(checker, function.result) {
|
||||
id := source.addf(checker.diagnostics, expr.span, "type factory '%s' is only valid in type position", symbol_text(checker, expr.name))
|
||||
last = invalid_hir_expr(checker, expr.span, id)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
if !valid_call_arity(function, len(expr.args)) {
|
||||
message := "function '%s' expects at least %d arguments, got %d" if function.variadic else
|
||||
"function '%s' expects %d arguments, got %d"
|
||||
@@ -8821,6 +8975,8 @@ check :: proc(
|
||||
checker.build_stack.allocator = allocator
|
||||
checker.cycle_stack.allocator = allocator
|
||||
checker.anon_globals.allocator = allocator
|
||||
checker.type_factories.allocator = allocator
|
||||
checker.generated_types.allocator = allocator
|
||||
build_symbol_indexes(&checker)
|
||||
checker.global_types = make([]types.Type, len(ast_module.globals), allocator)
|
||||
checker.global_demands = make([]types.Type, len(ast_module.globals), allocator)
|
||||
@@ -8864,6 +9020,14 @@ check :: proc(
|
||||
delete(checker.infer_stack)
|
||||
delete(checker.build_stack)
|
||||
delete(checker.cycle_stack)
|
||||
for entry in checker.type_factories {
|
||||
delete(entry.values, allocator)
|
||||
}
|
||||
for entry in checker.generated_types {
|
||||
delete(entry.values, allocator)
|
||||
}
|
||||
delete(checker.type_factories)
|
||||
delete(checker.generated_types)
|
||||
}
|
||||
|
||||
for function, index in ast_module.functions {
|
||||
|
||||
Reference in New Issue
Block a user