diff --git a/src/builtins.pr b/src/builtins.pr index 2be66a3..5e97e27 100644 --- a/src/builtins.pr +++ b/src/builtins.pr @@ -108,6 +108,28 @@ export let size_t_ = create_int_type("size_t", size_of size_t, true) ) } +// Reflection data +scope::create_variable( + builtins, parser::make_identifier("__reflection_data"), + parser::ShareMarker::EXPORT, parser::VarDecl::CONST, typechecking::pointer(uint8_), + [ kind = compiler::ValueKind::GLOBAL, tpe = typechecking::pointer(uint8_), name = "__reflection_data"] !&compiler::Value +) +scope::create_variable( + builtins, parser::make_identifier("__reflection_strings"), + parser::ShareMarker::EXPORT, parser::VarDecl::CONST, typechecking::pointer(char_), + [ kind = compiler::ValueKind::GLOBAL, tpe = typechecking::pointer(char_), name = "__reflection_strings"] !&compiler::Value +) +scope::create_variable( + builtins, parser::make_identifier("__reflection_data_size"), + parser::ShareMarker::EXPORT, parser::VarDecl::CONST, typechecking::pointer(size_t_), + [ kind = compiler::ValueKind::GLOBAL, tpe = typechecking::pointer(size_t_), name = "__reflection_data_size"] !&compiler::Value +) +scope::create_variable( + builtins, parser::make_identifier("__reflection_num_types"), + parser::ShareMarker::EXPORT, parser::VarDecl::CONST, typechecking::pointer(size_t_), + [ kind = compiler::ValueKind::GLOBAL, tpe = typechecking::pointer(size_t_), name = "__reflection_num_types"] !&compiler::Value +) + // These get set from toolchain export var File_: &typechecking::Type = null export var Type_: &typechecking::Type = null diff --git a/src/codegen.pr b/src/codegen.pr index 6994729..f8ae454 100644 --- a/src/codegen.pr +++ b/src/codegen.pr @@ -796,10 +796,6 @@ export def gen(module: &toolchain::Module) { gen_header(fp, module) if module.module == "main" { - module.imported.add("malloc") - module.imported.add("free") - module.imported.add("strlen") - gen_main_function(fp) } diff --git a/src/compiler.pr b/src/compiler.pr index 608dee1..87e88d1 100644 --- a/src/compiler.pr +++ b/src/compiler.pr @@ -15,6 +15,7 @@ import md5 import optional import arena import constants +import io export type Label = struct { name: Str @@ -741,6 +742,7 @@ def meta_to_debug_value(meta: &Value) -> DebugValue { } export def make_location(node: &parser::Node, state: &State) -> &Value { + if not node { return null } let discope = vector::peek(state.discope) if state.discope.length > 0 else null !&Value if not toolchain::debug_sym { return null } @@ -1013,12 +1015,8 @@ export def charp_static(global: &Value, state: &State) -> Value { return local } -export def create_global_string(str: Str, state: &State) -> Value { - let tpe = typechecking::make_type_raw(typechecking::TypeKind::STATIC_ARRAY) - tpe._tpe = builtins::char_ - tpe.length = str.length() + 1 - tpe.size = tpe.length * (size_of char) - tpe.align = align_of char +export def make_global_string(str: Str, state: &State) -> Value { + let tpe = typechecking::make_static_array(builtins::char_, str.length() + 1) let value = [ kind = ValueKind::STRING, @@ -1030,7 +1028,7 @@ export def create_global_string(str: Str, state: &State) -> Value { } export def charp(str: Str, state: &State) -> Value { - return charp_static(create_global_string(str, state), state) + return charp_static(make_global_string(str, state), state) } def push_scope(node: &parser::Node, state: &State) { @@ -2095,17 +2093,40 @@ def convert_to(kind: InsnKind, loc: &Value, value: Value, tpe: &typechecking::Ty return ret } -def get_embed_field(left: &typechecking::Type, right: &typechecking::Type, state: &State) -> &typechecking::StructMember { - if is_interface(left) and is_struct(right) { - if typechecking::implements(right, left, state.module, check_embed = false) { return null } + +def get_embed_field(left: &typechecking::Type, right: &typechecking::Type, state: &State) -> &Vector(&typechecking::StructMember) { + let res = vector::make(type &typechecking::StructMember) + // Early return if we already implement the interface! + if is_interface(left) and typechecking::implements(right, left, state.module, check_embed = false) { return res } + + if is_ref(right) { + // Unwrap reference + right = right.tpe + } + if is_struct(right) { + if is_struct(left) and typechecking::equals(left, right) or + is_interface(left) and typechecking::implements(right, left, state.module, check_embed = false) { + return res + } + for var field in @right.fields { - if field.is_embed and typechecking::implements(field.tpe, left, state.module, check_embed = false) { - return field + if field.is_embed { + if is_struct(left) and typechecking::equals(left, field.tpe) or + is_interface(left) and typechecking::implements(field.tpe, left, state.module, check_embed = false) { + + res.push(field) + return res + } else { + let embed = get_embed_field(left, field.tpe, state) + res.push(field) + res.add_all(embed) + return res + } } } } - return null + return res } // value gets loaded by this function @@ -2145,42 +2166,75 @@ def convert_to(loc: &Value, value: Value, tpe: &typechecking::Type, state: &Stat return value } } + + let cast = [src = value.tpe, dst = tpe] !toolchain::Cast + if state.module.dyn_casts.contains(cast) { + let fun = state.module.dyn_casts(cast) + + var dst = cast.dst + if not is_ref(cast.dst) { + dst = pointer(dst) + } + + let dst_value = state.alloca(dst, loc) + state.call(fun.type_name, null, [value, dst_value], loc) + + if is_ref(cast.dst) { + let val = state.load(cast.dst, dst_value) + return val + } else { + let ptr = state.load(pointer(cast.dst), dst_value) + let res = state.load(cast.dst, ptr) + res.addr = ptr + return res + } + } + let left = tpe.tpe if is_ref(tpe) else tpe let right = value.tpe.tpe if is_ref(value.tpe) else value.tpe - let embed_field = get_embed_field(left, right, state) + let embed_field = get_embed_field(left, value.tpe, state) // Try to convert to embedded struct / reference - if is_struct(right) and (is_struct(left) or embed_field) { - var is_embed = false - var field: StructMember - if embed_field { - is_embed = true - field = @embed_field + if is_struct(right) and ((is_struct(left) or is_interface(left)) and embed_field.length > 0) { + var elem = NO_VALUE + + // Unwrap reference on the value side + if is_ref(value.tpe) { + elem = state.extract_value(pointer(right), load_value(value, loc, state), [1], loc) } else { - for var f in @right.fields { - if f.is_embed and (equals(f.tpe, tpe) or equals(f.tpe, tpe.tpe)) { - is_embed = true - field = f - break + elem = @value.addr + } + + for var i in 0..embed_field.length { + let field = embed_field(i) + if is_ref(field.tpe) { + elem = state.load(elem.tpe.tpe, elem, loc) + elem = state.extract_value(field.tpe, elem, [field.index !int], loc) + if i < embed_field.length - 1 { + // Convert to a pointer for the next iteration + elem = state.extract_value(pointer(field.tpe), elem, [1], loc) + } else { + // Return reference as is + return elem } + } else { + elem = state.gep(pointer(field.tpe), field.tpe, elem, [make_int_value(0), make_int_value(field.index !int)], loc) } } - if is_embed { - var unwrap = load_value(value, loc, state) - // Unwrap reference on the value side - if is_ref(value.tpe) { - let _ref = state.extract_value(pointer(right), unwrap, [1], loc) - unwrap = state.load(right, _ref, loc) - } - // Extract element - var elem = state.extract_value(field.tpe, unwrap, [field.index !int], loc) - // Wrap in reference if needed - if is_ref(tpe) and not is_ref(field.tpe) { - elem = convert_value_to_ref(tpe, elem, loc, state, 1) - } - return elem + + var last_field = embed_field(embed_field.length - 1) + + let addr = elem + elem = state.load(elem.tpe.tpe, elem, loc) + elem.addr = addr + + // Wrap in reference if needed + if is_ref(tpe) and not is_ref(last_field.tpe) { + elem = convert_value_to_ref(tpe, elem, loc, state, 1) } + return elem } + if tpe.kind == value.tpe.kind and value.tpe.is_anon and typechecking::is_struct(value.tpe) { return convert_anon_to_struct(tpe, value, loc, state) } @@ -8033,6 +8087,105 @@ export def create_dyn_dispatch(dyn_dispatch: &Vector(&typechecking::Type), state } } +export def create_dyn_casts(state: &State) { + let dyn_casts = state.module.dyn_casts + for var cast in @dyn_casts.keys() { + let function = predeclare_function(dyn_casts(cast), state.module) + create_dyn_cast_function(function, cast, state) + consteval::const_module.result.functions(function.name) = function + } +} + +def create_dyn_cast_function(function: &Function, cast: toolchain::Cast, state: &State) { + // Setup function + function.block = make_block() + function.forward_declare = false + let previous_block = state.current_block + state.current_block = function.block + + var dst_arg = pointer(cast.dst) + if not is_ref(cast.dst) { + dst_arg = pointer(dst_arg) + } + + let src_value = [ kind = ValueKind::LOCAL, tpe = cast.src, name = "src.value"] !Value + let dst_value = [ kind = ValueKind::LOCAL, tpe = dst_arg, name = "dst.value" ] !Value + + // Extract type + let ref_tpe = state.extract_value( + typechecking::pointer(builtins::Type_), + src_value, + [2] + ) + let ref_tpe_deref = state.load(builtins::Type_, ref_tpe) + let tpe_value = state.extract_value( + typechecking::pointer(builtins::Type_), + ref_tpe_deref, + [4] + ) + let tpe_deref = state.load(builtins::Type_, tpe_value) + let tpe_id = state.extract_value(builtins::int64_, tpe_deref, [14]) + + // Switch + let switch_values = vector::make(SwitchValue) + let swtch = make_insn(InsnKind::SWITCH) + swtch.value.switch_ = [ + value = tpe_id, + switch_values = switch_values + ] !InsnSwitch + push_insn(swtch, state) + + let hashes = set::make(uint64) + let keys = map::keys(typechecking::types_map) + for var i in 0..keys.size { + let type_entry = typechecking::types_map(keys(i)) + + if not is_ref(type_entry.tpe) { continue } + let contains = typechecking::contains(type_entry.tpe) + + if not contains.contains(cast.dst) and not (cast.dst.is_ref() and contains.contains(cast.dst.tpe)) { continue } + + let hash = md5::high(md5::md5(debug::type_to_str(type_entry.tpe.tpe, full_name = true))) + if hashes.contains(hash) { continue } + hashes.add(hash) + + let if_true = make_label(state) + push_label(if_true, state) + + if cast.dst.is_ref() { + var res = convert_to(null !&Value, src_value, type_entry.tpe, state) + res = convert_to(null !&Value, res, cast.dst, state) + state.store(dst_value, res) + } else { + var res = convert_to(null !&Value, src_value, type_entry.tpe, state) + res = convert_to(null !&Value, res, cast.dst, state) + state.store(dst_value, @res.addr) + } + + state.ret(NO_VALUE) + + let svalue = [ + label_ = if_true, + value = [ kind = ValueKind::INT, i = hash, tpe = builtins::int64_ ] !Value + ] !SwitchValue + switch_values.push(svalue) + } + + let end_label = make_label(state) + push_label(end_label, state) + swtch.value.switch_.otherwise = end_label + + // TODO Abort with message! + import_cstd_function("abort", state) + state.call("abort", null, [] ![Value]) + + state.module.imported.add(function.name) + push_insn(make_insn(InsnKind::UNREACHABLE), state) + + // Reset state + state.current_block = previous_block +} + export let constructors = map::make(type &typechecking::Type) def create_constructors { var done = set::make() @@ -9001,6 +9154,9 @@ def push_structural_members(tpe: &typechecking::Type, global: Value, module: &to } def do_create_type(tpe: &typechecking::Type, svalue: &scope::Value, module: &toolchain::Module, cache: &Vector(TypeEntry)) -> &Value { + // Add to all types TODO Not this function should be responsible for that + all_types.add(tpe) + if toolchain::no_stdlib { return NO_VALUE } if tpe.kind == typechecking::TypeKind::BOX { @@ -9339,6 +9495,215 @@ def change_value_to_type(tpe: &typechecking::Type, state: &State) -> &scope::Val return value } +def make_global_data(name: Str, value: &ByteStream, state: &State) { + let tpe = typechecking::make_static_array(builtins::char_, value.data().size) + + let s = value.data() !&[byte] !&[char] + let global = [ + name = name, + tpe = tpe, + value = [ tpe = tpe, kind = ValueKind::STRING, s = s ] !Value, + line = -1 + ] !&Global + + state.module.result.globals(name) = global + state.module.imported.add(name) +} + +def make_global_data(name: Str, value: size_t, state: &State) { + let global = [ + name = name, + tpe = builtins::size_t_, + value = [ tpe = builtins::size_t_, kind = ValueKind::INT, i = value ] !Value, + line = -1 + ] !&Global + + state.module.result.globals(name) = global + state.module.imported.add(name) +} + +export let all_types = set::make(type &typechecking::Type) + +export def generate_reflection_data() { + let data = io::make_stream() + let strings = [ strings = io::make_stream(), map = map::make(size_t) ] !&UniquedStrings + + for var tpe in @all_types.keys() { + generate_reflection_data_1(tpe, data, strings) + } + + for var tpe in @all_types.keys() { + generate_reflection_data_2(tpe, data, strings) + } + + // Need to 0 terminate the whole thing so that it can be converted to a Str + data.write(0 !uint8) + strings.strings.write(0 !uint8) + + make_global_data("__reflection_data", data, toolchain::types_state) + make_global_data("__reflection_data_size", data.data().size, toolchain::types_state) + make_global_data("__reflection_strings", strings.strings, toolchain::types_state) + make_global_data("__reflection_num_types", all_types.size, toolchain::types_state) +} + +type UniquedStrings = struct { + strings: &ByteStream + map: &SMap(size_t) +} + +def write_zt(uniqued: &UniquedStrings, s: Str) -> int { + if uniqued.map.contains(s) { + return uniqued.map(s) !int + } + let val = uniqued.strings.write_zt(s) + uniqued.map(s) = val + return val !int +} + +def generate_reflection_data_2(tpe: &typechecking::Type, data: &ByteStream, strings: &UniquedStrings) { + // Collect type members + let type_members = vector::make(typechecking::TypeEntryMember) + for var member in typechecking::iterate_member_functions(tpe) { + if member.function.parameter_t(0).tpe.kind == typechecking::TypeKind::TYPE { continue } + + let function = member.function + + var is_incomplete = false + for var i in 0..vector::length(function.parameter_t) { + let np = function.parameter_t(i) + let generic = typechecking::get_generic(np.tpe) + if generic and generic.tc_incomplete or (np.tpe and np.tpe.kind == typechecking::TypeKind::TYPE_DEF) { + is_incomplete = true + break + } + } + if is_incomplete { continue } + type_members.push(member) + } + + // Write type members + data.write(type_members.length !int) + + print(debug::type_to_str(tpe), " members: ", type_members.length, "\n") + + for var member in type_members { + data.write(strings.write_zt(member.function.type_name)) + data.write(member.exported) + data.write(strings.write_zt(member.module.module)) + + print("\t", member.function.type_name, "\n") + + data.write(member.function.parameter_t.length !int) + for var arg in member.function.parameter_t { + data.write(arg.tpe.hash if arg.tpe else 0) // TODO Do we want function argument names maybe? + } + data.write(member.function.return_t.length !int) + for var t in member.function.return_t { + data.write(t.hash) + } + } + + switch tpe.kind { + case typechecking::TypeKind::STRUCT, typechecking::TypeKind::UNION + for var member in @tpe.fields { + data.write(member.tpe.hash) + } + case typechecking::TypeKind::STRUCTURAL + for var member in tpe.members { + for var np in member.parameter_t { + data.write(unbox(np.tpe).hash) + } + for var t in member.return_t { + data.write(unbox(t).hash) + } + } + case typechecking::TypeKind::POINTER, typechecking::TypeKind::REFERENCE, + typechecking::TypeKind::WEAK_REF, typechecking::TypeKind::ARRAY, + typechecking::TypeKind::STATIC_ARRAY + + if tpe.tpe and not all_types.contains(tpe.tpe) { + data.write(0 !uint64) // Probably an opaque type + } else { + data.write(tpe.tpe.hash if tpe.tpe else 0) + } + case typechecking::TypeKind::TUPLE + for var t in tpe.return_t { + data.write(t.hash) + } + case typechecking::TypeKind::VARIANT + for var t in @tpe.variants.keys() { + data.write(t.hash) + } + case typechecking::TypeKind::CLOSURE, typechecking::TypeKind::FUNCTION + for var np in tpe.parameter_t { + data.write(np.tpe.hash) + } + for var t in tpe.return_t { + data.write(t.hash) + } + } +} + +def generate_reflection_data_1(tpe: &typechecking::Type, data: &ByteStream, strings: &UniquedStrings) { + data.write(tpe.kind) // This does rely on the values beind the same + data.write(strings.write_zt(debug::type_to_str(tpe, full_name = true))) + data.write(strings.write_zt(tpe.module.module if tpe.module else to_str(""))) + data.write(tpe.hash) + + print(tpe.kind, " ", tpe.name, " ", (tpe.module.module if tpe.module else to_str("")), " ", tpe.hash, "\n") + + switch tpe.kind { + case typechecking::TypeKind::WORD + data.write(tpe.size !int) + data.write(tpe.align !int) + data.write(not tpe.unsig) + case typechecking::TypeKind::FLOAT + data.write(tpe.size !int) + data.write(tpe.align !int) + case typechecking::TypeKind::STRUCT, typechecking::TypeKind::UNION + data.write(tpe.size !int) + data.write(tpe.align !int) + data.write(tpe.fields.size !int) + + print("\tsize: ", tpe.size, " align: ", tpe.align, " members: ", tpe.fields.size, "\n") + for var i in 0..tpe.fields.size { + let field = tpe.fields(i) + let member = tpe.field_types(field.index) + data.write(strings.write_zt(field.name)) + data.write(member.offset !int) + print("\tname: ", field.name, " offset: ", member.offset, "\n") + } + case typechecking::TypeKind::STATIC_ARRAY + data.write(tpe.size) + data.write(tpe.align) + data.write(tpe.length) + case typechecking::TypeKind::FUNCTION, typechecking::TypeKind::CLOSURE + data.write(tpe.parameter_t.length !int) + data.write(tpe.return_t.length !int) + case typechecking::TypeKind::ENUM + data.write(tpe.size !int) + data.write(tpe.align !int) + data.write(not tpe.unsig) + + data.write(tpe.scope.fields.size !int) + for var name in @tpe.scope.fields.keys() { + let svalue = tpe.scope.fields(name) + data.write(strings.write_zt(name)) + data.write(svalue.value.i) + } + + case typechecking::TypeKind::STRUCTURAL + data.write(tpe.members.length !int) + for var member in tpe.members { + data.write(strings.write_zt(member.name)) + data.write(member.parameter_t.length !int) + data.write(member.return_t.length !int) + } + case typechecking::TypeKind::VARIANT, typechecking::TypeKind::TUPLE + data.write(tpe.variants.size) + } +} + export def make_result -> &Result { return [ @@ -9445,7 +9810,7 @@ def generate_vtable_function(function: &Function, tpe: &typechecking::Type, stat ] !InsnSwitch push_insn(swtch, state) - let hashes = set::make(size_t) + let hashes = set::make(uint64) let keys = map::keys(typechecking::types_map) for var i in 0..keys.size { let type_entry = typechecking::types_map(keys(i)) @@ -9563,19 +9928,26 @@ def generate_vtable_function(function: &Function, tpe: &typechecking::Type, stat if is_const { state.ret(@const_field.value) } else { - var deref = state.extract_value(pointer(type_entry.tpe.tpe), reference, [1]) - var value = state.load(type_entry.tpe.tpe, deref) - var findex: size_t = 0 - var ftpe: &typechecking::Type - for var field in @type_entry.tpe.tpe.fields { - if field.name == name { - findex = field.index - ftpe = field.tpe - } + var stpe = type_entry.tpe.tpe + // Resolve member + var deref = state.extract_value(pointer(stpe), reference, [1]) + var value = state.load(stpe, deref) + value.addr = deref + + let vec = vector::make(Member) + if not resolve_member(vec, stpe, name) { + return } - value = state.extract_value(ftpe, value, [findex !int]) - state.ret(value) + let len = vector::length(vec) + for var i in 0..len { + let j = len - i - 1 + let member = vec(j) + value = walk_MemberAccess_struct(null, stpe, member, value, state) + stpe = member.tpe + } + + state.ret(load_value(value, null, state)) } } } @@ -9585,7 +9957,7 @@ def generate_vtable_function(function: &Function, tpe: &typechecking::Type, stat push_label(end_label, state) swtch.value.switch_.otherwise = end_label - state.module.imported.add("abort") + import_cstd_function("abort", state) state.call("abort", null, [] ![Value]) state.module.imported.add(function.name) @@ -9651,6 +10023,11 @@ export def compile(module: &toolchain::Module) { export def compile(state: &State, is_main: bool, no_cleanup: bool = false) { toolchain::progress_update(state.module, toolchain::ProgressUpdate::START) + // Import required functions + import_cstd_function("malloc", state) + import_cstd_function("free", state) + import_cstd_function("strlen", state) + let node = state.module.node assert(node.kind == parser::NodeKind::PROGRAM) @@ -9669,7 +10046,7 @@ export def compile(state: &State, is_main: bool, no_cleanup: bool = false) { state.finalizer.dllexport = true if builtins::builtins.fields.contains("DEBUG_REF_CYCLES") { - state.file_name_value = create_global_string(state.module.module, state) + state.file_name_value = make_global_string(state.module.module, state) } import_structures(builtins::Type_, state.module) @@ -9861,6 +10238,7 @@ export def compile(state: &State, is_main: bool, no_cleanup: bool = false) { // TODO This doesn't work for functions that return multiple parameters predeclare_functions(state.module) create_dyn_dispatch(state.module.dyn_dispatch, state) + create_dyn_casts(state) let ident = parser::make_identifier("__main__") ident.loc.module = state.module.module diff --git a/src/debug.pr b/src/debug.pr index 7e6c3ec..43e8848 100644 --- a/src/debug.pr +++ b/src/debug.pr @@ -834,6 +834,7 @@ export def type_to_str(tpe: &typechecking::Type, full_name: bool = false) -> Str if not tpe { return "(none)" } if tpe.tc_tpe { return tc_args_to_string(tpe, full_name) } if not full_name and tpe.name { return tpe.name } + if full_name and tpe.type_name { return tpe.type_name } switch tpe.kind !int { case typechecking::TypeKind::BOX return "Box<" + type_to_str(tpe.wk, full_name) + ">" diff --git a/src/md5.pr b/src/md5.pr index 966ba1e..4fed0ce 100644 --- a/src/md5.pr +++ b/src/md5.pr @@ -358,7 +358,7 @@ export def md5(initial_msg: Str) -> [16; uint8] { return digest } -export def high(md5: [16; uint8]) -> int64 { +export def high(md5: [16; uint8]) -> uint64 { var res: int64 = 0 for var i in 0..8 { let j = 15 - i @@ -367,7 +367,7 @@ export def high(md5: [16; uint8]) -> int64 { return res } -export def low(md5: [16; uint8]) -> int64 { +export def low(md5: [16; uint8]) -> uint64 { var res: int64 = 0 for var i in 0..8 { let j = 7 - i diff --git a/src/scope.pr b/src/scope.pr index 6f41358..d8736f5 100644 --- a/src/scope.pr +++ b/src/scope.pr @@ -963,7 +963,10 @@ export def generate_function(scope: &Scope, node: &parser::Node, parameter_t: &V let fun = has_function(scope, tpe) if fun { return fun } - let value = create_function_without_checking(scope, name_node, stpe.share, tpe) + // TODO The share is export for now but maybe we want to make sure that it gets the same share as the first argument + // The problem is that tpe.share doesn't work when you define it like this: + // export type X = &interface { ... } and tpe.share is a bit of a hack anyway. + let value = create_function_without_checking(scope, name_node, parser::ShareMarker::EXPORT, tpe) value.identifier = name_node value.is_generated = true @@ -1324,9 +1327,10 @@ export def get_function( parameter_t: &Vector(typechecking::NamedParameter), dry_run: bool, force_compile: bool = true, - only_function: bool = false) -> &Value { + only_function: bool = false, + context: &Scope = null) -> &Value { - let _, value = get_function_check(scope, id, parameter_t, dry_run, force_compile, only_function) + let _, value = get_function_check(scope, id, parameter_t, dry_run, force_compile, only_function, context = context) return value } diff --git a/src/toolchain.pr b/src/toolchain.pr index 356bf4c..e6b6dec 100644 --- a/src/toolchain.pr +++ b/src/toolchain.pr @@ -197,6 +197,16 @@ export type Stage = enum { BACKEND } +export type Cast = struct { + src: &typechecking::Type + dst: &typechecking::Type +} +export def hash(cast: Cast) -> int64 { + return combine_hashes(hash(cast.src), hash(cast.dst)) +} +export def == (a: Cast, b: Cast) -> bool { return a.src == b.src and a.dst == b.dst } +export def != (a: Cast, b: Cast) -> bool { return not (a == b) } + export type Module = struct { display_name: Str filename: Str @@ -218,10 +228,13 @@ export type Module = struct { imports: &Set(Str) dependants: &Set(weak &Module) // List of Type + // TODO These should be sets // This is a list of functions that are generated for dynamic dispatch dyn_dispatch_consteval: &Vector(&typechecking::Type) dyn_dispatch: &Vector(&typechecking::Type) + dyn_casts: &Map(Cast, &typechecking::Type) + // This is needed to generate functions from create_destructor compiler_state: &compiler::State state: &typechecking::State @@ -302,11 +315,12 @@ export def make_module( scope = scpe, result = compiler::make_result(), code = compiler::make_block(), - imported = set::make(), + imported = set::make(Str), imports = set::make(Str), dependants = set::make(type weak &Module), dyn_dispatch_consteval = vector::make(type &typechecking::Type), dyn_dispatch = vector::make(type &typechecking::Type), + dyn_casts = map::make(Cast, type &typechecking::Type), unresolved = map::make(scope::Ident, type weak &scope::Value), inlay_hints = vector::make(type &parser::Node), closures = vector::make(type &scope::Value), @@ -826,7 +840,8 @@ export def compile_main_file(filename: String) { } debug::trace("Resolving types") - compiler::resolve_types() + compiler::resolve_types() // TODO Remove this function + compiler::generate_reflection_data() debug::trace("Creating builtin functions") compiler::create_builtin_functions() diff --git a/src/typechecking.pr b/src/typechecking.pr index cd7a3ae..6e53fda 100644 --- a/src/typechecking.pr +++ b/src/typechecking.pr @@ -20,46 +20,47 @@ import md5 import optional export type TypeKind = enum { - TYPE + BOOL WORD FLOAT - BOOL STRUCT UNION - ENUM - FUNCTION - CLOSURE - TUPLE + ARRAY + STATIC_ARRAY POINTER - BYREF // ref parameter REFERENCE WEAK_REF - ARRAY - STATIC_ARRAY + FUNCTION + CLOSURE + ENUM + CHAR + STRUCTURAL + VARIANT + TUPLE + TYPE + // Forward declaration + STUB + + // This is used to express types that may by resolved to multiple types + // Tagged union types, very similar to VARIANT but the equality relation is different + // This is actually called variant in runtime + TUNION + BYREF // ref parameter RANGE RANGE_INC // Used for enums NAMESPACE // Auto cast, this type will convert to anything // AUTO_CAST // TODO Actually implement this - // Forward declaration - STUB // Placeholder for arguments with polymorphic type parameter in function definition TYPE_DEF // Null NULL UNDEF - CHAR - STRUCTURAL // This is a type with arguments TYPE_CONSTRUCTOR // This is used as a function parameter that is generic GENERIC - // This is used to express types that may by resolved to multiple types - VARIANT - // Tagged union types, very similar to VARIANT but the equality relation is different - // This is actually called variant in runtime - TUNION // References to structs are using a wk reference, ie. Type::wk instead of Type::_tpe // This is because references to structs can cause cycles // TODO I think we don't really need this because we can just use wk directly @@ -99,6 +100,14 @@ export type StructuralTypeMember = struct { return_t: &Vector(&Type) } +def make_type_member(name: Str, parameter_t: &Vector(NamedParameter), return_t: &Vector(&Type)) -> StructuralTypeMember { + return [ + name = name, + parameter_t = parameter_t, + return_t = return_t + ] !StructuralTypeMember +} + export type TypeRef = struct { module: weak &toolchain::Module name: Str @@ -1072,7 +1081,7 @@ export def create_type_entry(tpe: &Type, exported: bool, entry: &Type, module: & // TODO Use a set for this for var i in 0..vector::length(type_entry.functions) { let fun = type_entry.functions(i) - if fun.function.type_name == entry.type_name { + if fun.function.name == entry.name and fun.function._defmodule == entry._defmodule and equals(fun.function, entry) { if overwrite { type_entry.functions(i) = member_function } return false } @@ -1274,6 +1283,19 @@ export def equals(a: &Type, b: &Type) -> bool { assert } +export def contains(a: &Type) -> &Set(&Type) { + if is_ref_or_weak(a) { a = a.tpe } + let res = set::make(type &Type) + if not is_struct(a) { return res } + for var field in @a.fields { + if field.is_embed { + res.add(field.tpe) + res.add_all(contains(field.tpe)) + } + } + return res +} + def is_setter(mb: StructuralTypeMember) -> bool { return mb.name.starts_with("__set_") and mb.name.ends_with("__") } @@ -1296,7 +1318,7 @@ def has_function(entry: &TypeEntry, intf: &Type, mb: StructuralTypeMember, modul if tpe and tpe.kind == typechecking::TypeKind::STRUCT { let fields = flatten_fields(tpe) if is_setter(mb) { - let name = mb.name.slice(6, mb.name.length() - 2) + let name = mb.name.slice(6, mb.name.length() - 2496) for var field in fields { if field.name == name and equals(mb.parameter_t(0).tpe, field.tpe) { return true } } @@ -2463,11 +2485,11 @@ export def generate_concrete_functions(type_constructor: &Type, tpe: &Type, stat let pars = vector::make(NamedParameter) pars.push([ _tpe = tpe ] !NamedParameter) - replace_type_defs(ftpe.parameter_t, ftpe.return_t, pars, null, state.module) - ftpe.type_name = mangle_function_name(append_module(ftpe.name, ftpe.module.module), ftpe.parameter_t, context = ftpe.context) + replace_type_defs(ftpe.parameter_t, ftpe.return_t, pars, null, state.get_context()) + ftpe.type_name = mangle_function_name(append_module(ftpe.name, ftpe.module.module), ftpe.parameter_t, context = state.get_context()) if not is_polymorph(ftpe) { - let new_entry = create_type_entry(tpe, function.exported, ftpe, function.module) + let new_entry = create_type_entry(tpe, function.exported, ftpe, state.get_context()) if new_entry { let score, value = scope::get_function_check(state.scope, parser::make_identifier(ftpe.name), pars, false, true, force_compile = true, context = state.context) } @@ -2913,11 +2935,7 @@ export def do_type_lookup(node: &parser::Node, state: &State, current_type: &Typ vector::push(return_t, ntpe) } - let structural_type_member = [ - name = name, - parameter_t = parameter_t, - return_t = return_t - ] !StructuralTypeMember + let structural_type_member = make_type_member(name, parameter_t, return_t) members.push(structural_type_member) } else { let parameter_t = vector::make(NamedParameter) @@ -2928,11 +2946,7 @@ export def do_type_lookup(node: &parser::Node, state: &State, current_type: &Typ var rtpe = box(type_lookup(return_node, state, current_return_type, false, cache)) return_t.push(rtpe) - let structural_type_member = [ - name = name, - parameter_t = parameter_t, - return_t = return_t - ] !StructuralTypeMember + let structural_type_member = make_type_member(name, parameter_t, return_t) members.push(structural_type_member) if kw == parser::MemberType::VAR { @@ -2944,11 +2958,8 @@ export def do_type_lookup(node: &parser::Node, state: &State, current_type: &Typ _tpe = rtpe ] !NamedParameter) - let structural_type_member = [ - name = "__set_" + name + "__", - parameter_t = parameter_t, - return_t = return_t - ] !StructuralTypeMember + let structural_type_member = make_type_member( + "__set_" + name + "__", parameter_t, return_t) members.push(structural_type_member) } } @@ -3572,7 +3583,7 @@ def walk_Assign(node: &parser::Node, state: &State) { args.push([ _tpe = rtpe ] !NamedParameter) let ident = parser::make_identifier("__set_" + parser::identifier_to_str(name) + "__") - let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static) + let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static, context = state.context) if fun { compile_function(*fun, state.scope, args) } @@ -3851,7 +3862,7 @@ def make_function_call(node: &parser::Node, ident: &parser::Node, args: &Vector( def convert_to_call(node: &parser::Node, name: Str, args: &Vector(NamedParameter), state: &State) { let ident = parser::make_identifier(name) - let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static) + let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static, context = state.context) if fun { compile_function(*fun, state.scope, args) let parent = node.parent @@ -3866,7 +3877,7 @@ def convert_to_call(node: &parser::Node, name: Str, args: &Vector(NamedParameter def convert_to_icall(node: &parser::Node, name: Str, args: &Vector(NamedParameter), state: &State) { let ident = parser::make_identifier(name) - let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static) + let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static, context = state.context) if fun { compile_function(*fun, state.scope, args) @@ -4129,6 +4140,12 @@ def walk_Cast(node: &parser::Node, state: &State) { var ltpe = left.tpe if not ltpe { return } + // Create necessary type entries + create_type_entry(ltpe) + if is_struct(ltpe) and is_ref(rtpe) { + create_type_entry(reference(ltpe)) + } + if left.kind == parser::NodeKind::INTEGER and is_integer(ltpe) and is_integer(rtpe) { left.tpe = rtpe @@ -4145,6 +4162,27 @@ def walk_Cast(node: &parser::Node, state: &State) { errors::errorn(left, "Invalid cast") return } + } else if ltpe.is_ref_or_weak() and + ltpe.tpe.is_interface() and + not implements(rtpe, ltpe.tpe, state.module) and + (rtpe.is_struct() or (rtpe.is_ref() and rtpe.tpe.is_struct())) { + + var dst = pointer(rtpe) + if not is_ref(rtpe) { + dst = pointer(dst) + } + + let params = vector::make(NamedParameter) + params.push([ name = "src", _tpe = ltpe ] !NamedParameter) + params.push([ name = "dst", _tpe = dst] !NamedParameter) + let fun = make_function_type_n( + parser::make_identifier("__cast"), + params, + vector::make(type &Type), + state.module) + + // Dynamic cast + state.module.dyn_casts([src = ltpe, dst = rtpe,] !Cast) = fun } else if is_struct(ltpe) or is_struct(rtpe) { if ltpe.kind != rtpe.kind and not is_ref(rtpe) { errors::errorn(left, "Invalid cast") @@ -4243,14 +4281,32 @@ export def lookup_parameters(node: &parser::Node, state: &State) -> &Type { } } } - for var i in 0..vector::length(returns) { - let n = returns(i) - let current_return_type = tpe.return_t(i) - let new_type = type_lookup(n, state, current_return_type, false) - if new_type { - //generate_ctor_and_dtor(new_type) - @current_return_type = @new_type - n.tpe = current_return_type + + var skip_returns = false + if node.value.def_.has_yield { + // If the return value is already a generator we don't need to look it up anymore + if tpe.return_t.length == 1 { + let gen = tpe.return_t(0) + if is_ref(gen) and equals(gen.tpe.tc_tpe, builtins::Generator_) { + // Only look up the inner member + // TODO This doesn't work if we have multiple returns for a generator + gen.tpe.tc_args(0) = type_lookup(returns(0), state, gen.tpe.tc_args(0), false) + skip_returns = true + } + } + } + + if not skip_returns { + for var i in 0..vector::length(returns) { + let n = returns(i) + let current_return_type = tpe.return_t(i) + + let new_type = type_lookup(n, state, current_return_type, false) + if new_type { + //generate_ctor_and_dtor(new_type) + @current_return_type = @new_type + n.tpe = current_return_type + } } } @@ -5013,7 +5069,7 @@ def check_for_apply_and_update(node: &parser::Node, left: &parser::Node, argumen vector::insert(args, 0, [ _tpe = tpe ] !NamedParameter) let ident = parser::make_identifier("apply") - let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static) + let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static, context = state.context) if fun { vector::insert(node.value.func_call.args, 0, left) @node = @make_function_call(node, ident, node.value.func_call.args) @@ -5030,7 +5086,7 @@ def check_for_apply_and_update(node: &parser::Node, left: &parser::Node, argumen args.push([ _tpe = value.tpe ] !NamedParameter) let ident = parser::make_identifier("update") - let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static) + let fun = scope::get_function(state.scope, ident, args, false, true, force_compile = not consteval::is_static, context = state.context) if fun { let args = vector::copy(node.value.func_call.args) vector::insert(args, 0, left) @@ -5084,7 +5140,7 @@ def check_for_apply_and_update(node: &parser::Node, left: &parser::Node, argumen } def access_check(function: &scope::Value, node: &parser::Node, state: &State) -> bool { - return not (function and (function.share !int & parser::ShareMarker::EXPORT !int == 0) and function.module != node.scope.module and function.module != state.context.module) + return not (function and (function.share !int & parser::ShareMarker::EXPORT !int == 0) and function.module != node.scope.module and function.module != state.get_context()) } export def walk_Call(node: &parser::Node, dry_run: bool, state: &State) -> bool { @@ -5518,7 +5574,7 @@ def wrap_iterable(expr: &parser::Node, state: &State) -> &parser::Node { let args = vector::make(NamedParameter) args.push([ _tpe = expr.tpe ] !NamedParameter) let ident = parser::make_identifier("iterate") - let fun = scope::get_function(state.scope, ident, args, true) + let fun = scope::get_function(state.scope, ident, args, true, context = state.context) if fun { let args = vector::make(type &parser::Node) args.push(expr) @@ -5662,7 +5718,7 @@ def walk_MemberAccess_ucs(node: &parser::Node, state: &State) -> bool { _tpe = (@left).tpe ] !NamedParameter) - let function = scope::get_function(node.scope, right, parameter_t, false, false, only_function = true) + let function = scope::get_function(node.scope, right, parameter_t, false, false, only_function = true, context = state.context) if not function { return false } if node.parent.kind == parser::NodeKind::ASSIGN and node.parent.value.assign.right.index_of(node) == -1 { return true @@ -6342,6 +6398,10 @@ export def walk_Def_with_type_argument(node: &parser::Node, parameter_t: &Vector node = parser::deep_copy_node(node) node.value.def_.function = null node.value.def_.is_generic_instance = true + + let prev_context = state.context + state.context = context + node.tpe = lookup_parameters(node, state) // Unbox @@ -6363,7 +6423,10 @@ export def walk_Def_with_type_argument(node: &parser::Node, parameter_t: &Vector super_scope.parent = context }*/ - if not node.scope { return old_node } + if not node.scope { + state.context = prev_context + return old_node + } node.inner_scope = scope::enter_function_scope(node.scope) let share = node.value.def_.share @@ -6443,8 +6506,6 @@ export def walk_Def_with_type_argument(node: &parser::Node, parameter_t: &Vector create_type_entry(first_param.tpe, (share !int & parser::ShareMarker::EXPORT !int) != 0, tpe, state.module, true) } - let prev_context = state.context - state.context = context walk_Def(node, state, polymorph = true) state.context = prev_context diff --git a/std/io.pr b/std/io.pr index 1ead585..d074ca3 100644 --- a/std/io.pr +++ b/std/io.pr @@ -1,5 +1,36 @@ import cstd import std +import vector +import strings + +export type ByteStream = struct { + _data: &Vector(byte) +} + +export def make_stream -> &ByteStream { + return [ _data = vector::make(byte) ] !ByteStream +} + +export def write(this: &ByteStream, data: type T) { + this._data.extend(T.size) + std::memcopy(*data, this._data.data.value ++ this._data.length, T.size) + this._data.length += T.size +} + +export def write_zt(this: &ByteStream, s: Str) -> size_t { + let offset = this._data.length + this._data.extend(s.length + 1) + std::memcopy(get_internal_buffer(*s), this._data.data.value ++ offset, s.length) + this._data.data(this._data.length + s.length) = 0 + this._data.length += s.length + 1 + return offset +} + +export def data(this: &ByteStream) -> [byte] { + let data = this._data.data + data.size = this._data.length + return data +} var stderr_orig_fd = -1 export var stderr_orig: File @@ -59,6 +90,11 @@ export const NO_BLOCKING = 1 return cstd::_isatty(cstd::_fileno(file)) != 0 } + export def open_memory_as_file(arr: [uint8]) -> File { + // TODO implement, see https://github.com/Arryboom/fmemopen_windows + return null + } + } else { import linux @@ -119,4 +155,8 @@ export const NO_BLOCKING = 1 export def is_a_tty(file: File) -> bool { return linux::isatty(cstd::fileno(file)) != 0 } + + export def open_memory_as_file(arr: [uint8], mode: String) -> File { + return cstd::fmemopen(arr.value, arr.size, mode.to_array().value) + } } \ No newline at end of file diff --git a/std/reflection.pr b/std/reflection.pr new file mode 100644 index 0000000..21c16e6 --- /dev/null +++ b/std/reflection.pr @@ -0,0 +1,432 @@ +export type Type = &interface { + let name: StringSlice + let module: StringSlice + let id: uint64 + let size: size_t + let align: size_t + let type_members: &[Function] +} + +export def implements(a: Type, b: InterfaceT) -> bool { + return false // TODO +} + +export def assignable(a: Type, b: Type) -> bool { + return false // TODO +} + +export def contains(a: StructT, b: Type) -> bool { + return false // TODO +} + +export def == (a: Type, b: Type) -> bool { + return a.id == b.id +} +export def != (a: Type, b: Type) -> bool { + return not (a == b) +} + +type BaseType = struct { + name: StringSlice + module: StringSlice + id: uint64 + type_members: &[Function] +} + +export type TypeT = &struct { + BaseType + const size: size_t = 0 + const align: size_t = 0 +} + +export type OpaqueT = &struct { + BaseType + const size: size_t = 1 + const align: size_t = 1 +} + +export type BoolT = &struct { + BaseType + const size: size_t = size_of bool + const align: size_t = align_of bool +} +export type FloatT = &struct { + BaseType + size: size_t + align: size_t +} +export type WordT = &struct { + BaseType + signed: bool + size: size_t + align: size_t +} +export type CharT = &struct { + BaseType + const size: size_t = size_of char + const align: size_t = align_of char +} + +export type BoxType = struct { + BaseType + tpe: weak Type +} + +export type PointerT = &struct { + BoxType + const size: size_t = size_of * + const align: size_t = align_of * +} +export type ReferenceT = &struct { + BoxType + const size: size_t = size_of & + const align: size_t = align_of & +} +export type WeakReferenceT = &struct { + BoxType + const size: size_t = size_of weak & + const align: size_t = align_of weak & +} +export type ArrayT = &struct { + BoxType + const size: size_t = size_of [*] + const align: size_t = align_of [*] +} +export type StaticArrayT = &struct { + BoxType + size: size_t + align: size_t + length: size_t +} + +type FunctionBase = struct { + BaseType + arguments: &[weak Type] + returns: &[weak Type] +} +export type FunctionT = &struct { + FunctionBase + const size: size_t = size_of def -> + const align: size_t = align_of def -> +} +export type ClosureT = &struct { + FunctionBase + const size: size_t = size_of -> + const align: size_t = align_of -> +} + +type RecordT = struct { + BaseType + size: size_t + align: size_t + members: &[Field] +} +export type Field = struct { + name: StringSlice + offset: size_t + tpe: weak Type +} +export type StructT = &struct { + RecordT +} +export type UnionT = &struct { + RecordT +} + +export type EnumValue = struct { + name: StringSlice + value: int64 +} +export type EnumT = &struct { + BaseType + signed: bool + size: size_t + align: size_t + values: &[EnumValue] +} + +export type Function = struct { + name: StringSlice + exported: bool + module: StringSlice + arguments: &[weak Type] + returns: &[weak Type] +} + +export type InterfaceT = &struct { + BaseType + const size: size_t = 0 + const align: size_t = 0 + members: &[Function] +} + +export type VariantT = &struct { + BaseType + size: size_t + align: size_t + variants: &[weak Type] +} + +export type TupleT = &struct { + BaseType + size: size_t + align: size_t + elements: &[weak Type] +} + +type TypeKind = enum { + BOOL + WORD + FLOAT + STRUCT + UNION + ARRAY + STATIC_ARRAY + POINTER + REFERENCE + WEAK_REF + FUNCTION + CLOSURE + ENUM + CHAR + STRUCTURAL + VARIANT + TUPLE + TYPE + OPAQUE // Same as STUB in typechecking +} + +import io +import map +import std + +var types: &Map(uint64, Type) + +load_types(__reflection_data, @__reflection_data_size, @__reflection_num_types, __reflection_strings) + +// Type registry functions +def load_types(input: *uint8, size: size_t, num: size_t, strings: *char) { + var data: [uint8] + data.value = input + data.size = size + let fp = io::open_memory_as_file(data, "r") + + defer close(fp) + + if not types { + types = map::make(uint64, Type) + } + + var index = 0 + while index < num { + let kind = fp.read(TypeKind) + let name = make_slice(strings, fp.read(int)) + let module = make_slice(strings, fp.read(int)) + let id = fp.read(uint64) + + print(kind, " ", name, " ", module, " ", id, "\n") + + switch kind { + case TypeKind::BOOL + types(id) = [ name = name, module = module, id = id ] !BoolT + case TypeKind::WORD + let size = fp.read(int) + let align = fp.read(int) + let signed = fp.read(bool) + types(id) = [ name = name, module = module, id = id, + size = size, align = align, signed = signed ] !WordT + case TypeKind::FLOAT + let size = fp.read(int) + let align = fp.read(int) + types(id) = [ name = name, module = module, id = id, + size = size, align = align ] !FloatT + case TypeKind::STRUCT, TypeKind::UNION + let size = fp.read(int) + let align = fp.read(int) + let members = zero_allocate(Field, fp.read(int)) + + print("\tsize: ", size, " align: ", align, " members: ", members.size, "\n") + + for var i in 0..members.size { + let field = [ name = make_slice(strings, fp.read(int)), offset = fp.read(int) ] !Field + members(i) = field + print("\tname: ", field.name, " offset: ", field.offset, "\n") + } + + if kind == TypeKind::STRUCT { + types(id) = [ name = name, module = module, id = id, + size = size, align = align, members = members ] !StructT + } else { + types(id) = [ name = name, module = module, id = id, + size = size, align = align, members = members ] !UnionT + } + case TypeKind::ARRAY + types(id) = [ name = name, module = module, id = id ] !ArrayT + case TypeKind::STATIC_ARRAY + let size = fp.read(size_t) + let align = fp.read(size_t) + let length = fp.read(size_t) + + types(id) = [ name = name, module = module, id = id, + size = size, align = align, length = length ] !StaticArrayT + case TypeKind::POINTER + types(id) = [ name = name, module = module, id = id ] !PointerT + case TypeKind::REFERENCE + types(id) = [ name = name, module = module, id = id ] !ReferenceT + case TypeKind::WEAK_REF + types(id) = [ name = name, module = module, id = id ] !WeakReferenceT + case TypeKind::FUNCTION, TypeKind::CLOSURE + let arg_len = fp.read(int) + let return_len = fp.read(int) + types(id) = [ name = name, module = module, id = id, + arguments = allocate_ref(type weak Type, arg_len), + returns = allocate_ref(type weak Type, return_len) ] !FunctionT + case TypeKind::ENUM + let size = fp.read(int) + let align = fp.read(int) + let signed = fp.read(bool) + + let values = allocate_ref(type EnumValue, fp.read(int)) + for var i in 0..values.size { + values(i) = [ + name = make_slice(strings, fp.read(int)), + value = fp.read(int64) + ] !EnumValue + } + types(id) = [ name = name, module = module, id = id, + size = size, align = align, values = values ] !EnumT + case TypeKind::CHAR + types(id) = [ name = name, module = module, id = id ] !CharT + case TypeKind::STRUCTURAL + let members = allocate_ref(type Function, fp.read(int)) + for var i in 0..members.size { + members(i) = [ + name = make_slice(strings, fp.read(int)), + exported = true, + arguments = allocate_ref(type weak Type, fp.read(int)), + returns = allocate_ref(type weak Type, fp.read(int)) + ] !Function + } + types(id) = [ name = name, module = module, id = id, members = members ] !InterfaceT + case TypeKind::VARIANT + types(id) = [ name = name, module = module, id = id, + variants = allocate_ref(type weak Type, fp.read(int)) + ] !VariantT + case TypeKind::TUPLE + types(id) = [ name = name, module = module, id = id, + elements = allocate_ref(type weak Type, fp.read(int)) + ] !TupleT + case TypeKind::TYPE + types(id) = [ name = name, module = module, id = id ] !TypeT + case TypeKind::OPAQUE + types(id) = [ name = name, module = module, id = id ] !OpaqueT + } + + index += 1 + } + + print("==============================================\n") + + // Resolve type references + for var id in @types.keys() { + let tpe = types(id) + + let base = *(tpe !BaseType) + let nmembers = fp.read(int) + print(tpe.name, " members: ", nmembers, "\n") + base.type_members = allocate_ref(type Function, nmembers) + for var i in 0..nmembers { + let name = make_slice(strings, fp.read(int)) + let exported = fp.read(bool) + let module = make_slice(strings, fp.read(int)) + + print("\t", name, " ", exported, " ", module, "\n") + + let member = [ + name = name, + exported = exported, + module = module + ] !Function + + let arguments = allocate_ref(type weak Type, fp.read(int)) + for var i in 0..arguments.size { + arguments(i) = type_id(fp.read(uint64)) + } + let returns = allocate_ref(type weak Type, fp.read(int)) + for var i in 0..returns.size { + returns(i) = type_id(fp.read(uint64)) + } + + member.arguments = arguments + member.returns = returns + base.type_members(i) = member + } + + if tpe.type == StructT { + let rec = tpe !StructT + for var i in 0..rec.members.size { + let f = *rec.members(i) + f.tpe = type_id(fp.read(uint64)) + } + } else if tpe.type == UnionT { + let rec = tpe !UnionT + for var i in 0..rec.members.size { + let f = *rec.members(i) + f.tpe = type_id(fp.read(uint64)) + } + } else if tpe.type == InterfaceT { + let intf = tpe !InterfaceT + print(intf.type.name, " ", intf.name, "\n") + for var i in 0..intf.members.size { + let m = *intf.members(i) + for var i in 0..m.arguments.size { + m.arguments(i) = type_id(fp.read(uint64)) + } + for var i in 0..m.returns.size { + m.returns(i) = type_id(fp.read(uint64)) + } + } + } else if tpe.type == PointerT { + (tpe !PointerT).tpe = type_id(fp.read(uint64)) + } else if tpe.type == ReferenceT { + (tpe !ReferenceT).tpe = type_id(fp.read(uint64)) + } else if tpe.type == WeakReferenceT { + (tpe !WeakReferenceT).tpe = type_id(fp.read(uint64)) + } else if tpe.type == ArrayT { + (tpe !ArrayT).tpe = type_id(fp.read(uint64)) + } else if tpe.type == StaticArrayT { + (tpe !StaticArrayT).tpe = type_id(fp.read(uint64)) + } else if (tpe.type == VariantT) { + let vnt = tpe !VariantT + for var i in 0..vnt.variants.size { + vnt.variants(i) = type_id(fp.read(uint64)) + } + } else if (tpe.type == TupleT) { + let tuple = tpe !TupleT + for var i in 0..tuple.elements.size { + tuple.elements(i) = type_id(fp.read(uint64)) + } + } else if (tpe.type == FunctionT) { + let fun = tpe !FunctionT + for var i in 0..fun.arguments.size { + fun.arguments(i) = type_id(fp.read(uint64)) + } + for var i in 0..fun.returns.size { + fun.returns(i) = type_id(fp.read(uint64)) + } + } else if (tpe.type == ClosureT) { + let fun = tpe !ClosureT + for var i in 0..fun.arguments.size { + fun.arguments(i) = type_id(fp.read(uint64)) + } + for var i in 0..fun.returns.size { + fun.returns(i) = type_id(fp.read(uint64)) + } + } + } +} + +export def type_id(id: uint64) -> Type { + if id == 0 { return null } + return types(id) +} \ No newline at end of file diff --git a/std/std.pr b/std/std.pr index 628595c..f984795 100644 --- a/std/std.pr +++ b/std/std.pr @@ -566,6 +566,12 @@ export def read(file: File, ptr: type *T) -> size_t { return cstd::fread(ptr, T.size, 1, file) } +export def read(file: File, type T) -> T { + var data: T + cstd::fread(*data, T.size, 1, file) + return data +} + export def read_str(file: File) -> Str { var len: size_t file.read(*len) diff --git a/std/strings.pr b/std/strings.pr index ab197a8..fddb4ab 100644 --- a/std/strings.pr +++ b/std/strings.pr @@ -807,7 +807,7 @@ export def int_to_hex_str(n: uint64, prefix: bool = true) -> Str { let digits = "0123456789ABCDEF" if n == 0 { - return "0x0" + return "0x0" if prefix else "0" } var str: StringBuffer = ""