From 406496ca3371b7f50aee141fa37c52e86d96783f Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Thu, 1 Feb 2018 23:32:09 -0500 Subject: [PATCH] *WIP* error sets - allow peer type resolution to create new error set --- src/analyze.cpp | 2 +- src/ir.cpp | 102 +++++++++++++++++++++++++++++++++++++--------- std/fmt/index.zig | 4 +- std/io.zig | 3 +- 4 files changed, 88 insertions(+), 23 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index f69b131626..36aac9b29e 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1277,7 +1277,7 @@ static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { TypeTableEntry *get_auto_err_set_type(CodeGen *g, FnTableEntry *fn_entry) { TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet); buf_resize(&err_set_type->name, 0); - buf_appendf(&err_set_type->name, "%s.errors", buf_ptr(&fn_entry->symbol_name)); + buf_appendf(&err_set_type->name, "@typeOf(%s).ReturnType.ErrorSet", buf_ptr(&fn_entry->symbol_name)); err_set_type->is_copyable = true; err_set_type->type_ref = g->builtin_types.entry_global_error_set->type_ref; err_set_type->di_type = g->builtin_types.entry_global_error_set->di_type; diff --git a/src/ir.cpp b/src/ir.cpp index e6899da48b..d2b2c4fc82 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -5380,12 +5380,61 @@ static IrInstruction *ir_gen_container_decl(IrBuilder *irb, Scope *parent_scope, return ir_build_const_type(irb, parent_scope, node, container_type); } +// errors should be populated with set1's values +static TypeTableEntry *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, TypeTableEntry *set1, TypeTableEntry *set2) { + assert(set1->id == TypeTableEntryIdErrorSet); + assert(set2->id == TypeTableEntryIdErrorSet); + + TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet); + buf_resize(&err_set_type->name, 0); + buf_appendf(&err_set_type->name, "error{"); + + uint32_t count = set1->data.error_set.err_count; + for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; + if (errors[error_entry->value] == nullptr) { + count += 1; + } + } + + err_set_type->is_copyable = true; + err_set_type->type_ref = g->builtin_types.entry_global_error_set->type_ref; + err_set_type->di_type = g->builtin_types.entry_global_error_set->di_type; + err_set_type->data.error_set.err_count = count; + err_set_type->data.error_set.errors = allocate(count); + + for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = set1->data.error_set.errors[i]; + buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name)); + err_set_type->data.error_set.errors[i] = error_entry; + } + + uint32_t index = set1->data.error_set.err_count; + for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; + if (errors[error_entry->value] == nullptr) { + errors[error_entry->value] = error_entry; + buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name)); + err_set_type->data.error_set.errors[index] = error_entry; + index += 1; + } + } + assert(index == count); + + buf_appendf(&err_set_type->name, "}"); + + g->error_di_types.append(&err_set_type->di_type); + + return err_set_type; + +} + static TypeTableEntry *make_err_set_with_one_item(CodeGen *g, Scope *parent_scope, AstNode *node, ErrorTableEntry *err_entry) { TypeTableEntry *err_set_type = new_type_table_entry(TypeTableEntryIdErrorSet); buf_resize(&err_set_type->name, 0); - buf_appendf(&err_set_type->name, "@typeOf(error.%s)", buf_ptr(&err_entry->name)); + buf_appendf(&err_set_type->name, "error{%s}", buf_ptr(&err_entry->name)); err_set_type->is_copyable = true; err_set_type->type_ref = g->builtin_types.entry_global_error_set->type_ref; err_set_type->di_type = g->builtin_types.entry_global_error_set->di_type; @@ -6656,7 +6705,6 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } // if err_set_type is a superset of cur_type, keep err_set_type. // if cur_type is a superset of err_set_type, switch err_set_type to cur_type - // otherwise emit a compile error bool prev_is_superset = true; for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i]; @@ -6689,12 +6737,16 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } } if (cur_is_superset) { - err_set_type = cur_inst->value.type; + err_set_type = cur_type; prev_inst = cur_inst; continue; } + + // neither of them are supersets. so we invent a new error set type that is a union of both of them + err_set_type = get_error_set_union(ira->codegen, errors, cur_type, err_set_type); + continue; } else if (cur_type->id == TypeTableEntryIdErrorUnion) { - // err_set_type must be a subset of cur_type's error set + // test if err_set_type is a subset of cur_type's error set // unset everything in errors for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; @@ -6719,6 +6771,11 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod prev_inst = cur_inst; continue; } + + // not a subset. invent new error set type, union of both of them + err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, err_set_type); + prev_inst = cur_inst; + continue; } else { prev_inst = cur_inst; continue; @@ -6746,7 +6803,7 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod continue; } if (prev_type->id == TypeTableEntryIdErrorUnion) { - // the cur type error set must be a subset + // check if the cur type error set must be a subset bool prev_is_superset = true; for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i]; @@ -6759,6 +6816,9 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod if (prev_is_superset) { continue; } + // not a subset. invent new error set type, union of both of them + err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_type); + continue; } } @@ -6927,21 +6987,25 @@ static TypeTableEntry *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_nod } else { return slice_type; } - } else if (err_set_type != nullptr && prev_inst->value.type->id != TypeTableEntryIdErrorSet) { - if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt || - prev_inst->value.type->id == TypeTableEntryIdNumLitFloat) - { - ir_add_error_node(ira, source_node, - buf_sprintf("unable to make error union out of number literal")); - return ira->codegen->builtin_types.entry_invalid; - } else if (prev_inst->value.type->id == TypeTableEntryIdNullLit) { - ir_add_error_node(ira, source_node, - buf_sprintf("unable to make error union out of null literal")); - return ira->codegen->builtin_types.entry_invalid; - } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) { - return prev_inst->value.type; + } else if (err_set_type != nullptr) { + if (prev_inst->value.type->id == TypeTableEntryIdErrorSet) { + return err_set_type; } else { - return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type); + if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt || + prev_inst->value.type->id == TypeTableEntryIdNumLitFloat) + { + ir_add_error_node(ira, source_node, + buf_sprintf("unable to make error union out of number literal")); + return ira->codegen->builtin_types.entry_invalid; + } else if (prev_inst->value.type->id == TypeTableEntryIdNullLit) { + ir_add_error_node(ira, source_node, + buf_sprintf("unable to make error union out of null literal")); + return ira->codegen->builtin_types.entry_invalid; + } else if (prev_inst->value.type->id == TypeTableEntryIdErrorUnion) { + return prev_inst->value.type; + } else { + return get_error_union_type(ira->codegen, err_set_type, prev_inst->value.type); + } } } else if (any_are_null && prev_inst->value.type->id != TypeTableEntryIdNullLit) { if (prev_inst->value.type->id == TypeTableEntryIdNumLitInt || diff --git a/std/fmt/index.zig b/std/fmt/index.zig index a5c1c854b7..99a8f69c24 100644 --- a/std/fmt/index.zig +++ b/std/fmt/index.zig @@ -498,12 +498,12 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 { pub fn allocPrint(allocator: &mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 { var size: usize = 0; - format(&size, error{}, countSize, fmt, args); + format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {}; const buf = try allocator.alloc(u8, size); return bufPrint(buf, fmt, args); } -fn countSize(size: &usize, bytes: []const u8) void { +fn countSize(size: &usize, bytes: []const u8) !void { *size += bytes.len; } diff --git a/std/io.zig b/std/io.zig index b9f7157101..e110d4ddf5 100644 --- a/std/io.zig +++ b/std/io.zig @@ -350,10 +350,11 @@ pub const File = struct { }; pub const InStream = struct { + // TODO allow specifying the error set /// Return the number of bytes read. If the number read is smaller than buf.len, it /// means the stream reached the end. Reaching the end of a stream is not an error /// condition. - readFn: fn(self: &InStream, buffer: []u8) !usize, + readFn: fn(self: &InStream, buffer: []u8) error!usize, /// Replaces `buffer` contents by reading from the stream until it is finished. /// If `buffer.len()` would exceed `max_size`, `error.StreamTooLong` is returned and