diff --git a/doc/langref.md b/doc/langref.md index f5a338e0cc..98ad619f63 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -7,7 +7,7 @@ Root : many(TopLevelDecl) "EOF" TopLevelDecl : FnDef | ExternBlock | RootExportDecl | Import | ContainerDecl | VariableDeclaration | ErrorValueDecl -ErrorValueDecl : option(FnVisibleMod) "%." "Symbol" +ErrorValueDecl : option(FnVisibleMod) "error" "Symbol" VariableDeclaration : option(FnVisibleMod) ("var" | "const") "Symbol" ("=" Expression | ":" PrefixOpExpression option("=" Expression)) @@ -133,9 +133,9 @@ ContainerInitBody : list(StructLiteralField, ",") | list(Expression, ",") StructLiteralField : "." "Symbol" "=" Expression -PrefixOp : "!" | "-" | "~" | "*" | ("&" option("const")) | "?" +PrefixOp : "!" | "-" | "~" | "*" | ("&" option("const")) | "?" | "%" -PrimaryExpression : "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | AsmExpression | ("%." "Symbol") +PrimaryExpression : "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | AsmExpression | ("error" "." "Symbol") ArrayType : "[" option(Expression) "]" option("const") PrefixOpExpression @@ -143,7 +143,7 @@ GotoExpression: "goto" "Symbol" GroupedExpression : "(" Expression ")" -KeywordLiteral : "true" | "false" | "null" | "break" | "continue" | "undefined" +KeywordLiteral : "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" ``` ## Operator Precedence diff --git a/doc/vim/syntax/zig.vim b/doc/vim/syntax/zig.vim index b29b8c7530..debce00fc9 100644 --- a/doc/vim/syntax/zig.vim +++ b/doc/vim/syntax/zig.vim @@ -15,7 +15,7 @@ syn keyword zigRepeat while for syn keyword zigConstant null undefined syn keyword zigKeyword fn import -syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 void unreachable type +syn keyword zigType bool i8 u8 i16 u16 i32 u32 i64 u64 isize usize f32 f64 f128 void unreachable type error syn keyword zigBoolean true false diff --git a/example/cat/main.zig b/example/cat/main.zig index 7d38793e74..6d82b3b24e 100644 --- a/example/cat/main.zig +++ b/example/cat/main.zig @@ -10,6 +10,7 @@ import "std.zig"; // * %% binary operator // * %% prefix operator // * cast err type to string +// * string equality pub fn main(args: [][]u8) %void => { const exe = args[0]; @@ -39,7 +40,7 @@ pub fn main(args: [][]u8) %void => { fn usage(exe: []u8) %void => { %%stderr.print("Usage: {} [FILE]...\n", exe); - return %.Invalid; + return error.Invalid; } fn cat_stream(is: InputStream) %void => { diff --git a/example/guess_number/main.zig b/example/guess_number/main.zig index 6414542362..78a8c63a23 100644 --- a/example/guess_number/main.zig +++ b/example/guess_number/main.zig @@ -3,8 +3,8 @@ export executable "guess_number"; import "std.zig"; import "rand.zig"; -%.GetRandomFail; -%.ReadInputFail; +error GetRandomFail; +error ReadInputFail; pub fn main(args: [][]u8) %void => { print_str("Welcome to the Guess Number Game in Zig.\n"); @@ -15,7 +15,7 @@ pub fn main(args: [][]u8) %void => { if (err != @sizeof(u32)) { // TODO full error message fprint_str(stderr_fileno, "unable to get random bytes\n"); - return %.GetRandomFail; + return error.GetRandomFail; } var rand : Rand; @@ -31,7 +31,7 @@ pub fn main(args: [][]u8) %void => { if (readline(line_buf, &line_len) || line_len == line_buf.len) { // TODO full error message fprint_str(stderr_fileno, "unable to read input\n"); - return %.ReadInputFail; + return error.ReadInputFail; } var guess : u64; diff --git a/src/all_types.hpp b/src/all_types.hpp index af80b36f3f..89d3fd8ecf 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -133,7 +133,6 @@ enum NodeType { NodeTypeNumberLiteral, NodeTypeStringLiteral, NodeTypeCharLiteral, - NodeTypeErrorLiteral, NodeTypeSymbol, NodeTypePrefixOpExpr, NodeTypeFnCallExpr, @@ -161,6 +160,7 @@ enum NodeType { NodeTypeContainerInitExpr, NodeTypeStructValueField, NodeTypeArrayType, + NodeTypeErrorType, }; struct AstNodeRoot { @@ -315,6 +315,7 @@ enum CastOp { CastOpToUnknownSizeArray, CastOpMaybeWrap, CastOpErrorWrap, + CastOpPureErrorWrap, CastOpPointerReinterpret, CastOpErrToInt, }; @@ -584,13 +585,6 @@ struct AstNodeNumberLiteral { Expr resolved_expr; }; -struct AstNodeErrorLiteral { - Buf symbol; - - // populated by semantic analyzer - Expr resolved_expr; -}; - struct AstNodeStructValueField { Buf name; AstNode *expr; @@ -663,6 +657,11 @@ struct AstNodeArrayType { Expr resolved_expr; }; +struct AstNodeErrorType { + // populated by semantic analyzer + Expr resolved_expr; +}; + struct AstNode { enum NodeType type; int line; @@ -705,7 +704,6 @@ struct AstNode { AstNodeStringLiteral string_literal; AstNodeCharLiteral char_literal; AstNodeNumberLiteral number_literal; - AstNodeErrorLiteral error_literal; AstNodeContainerInitExpr container_init_expr; AstNodeStructValueField struct_val_field; AstNodeNullLiteral null_literal; @@ -715,6 +713,7 @@ struct AstNode { AstNodeBreakExpr break_expr; AstNodeContinueExpr continue_expr; AstNodeArrayType array_type; + AstNodeErrorType error_type; } data; }; @@ -820,7 +819,8 @@ enum TypeTableEntryId { TypeTableEntryIdNumLitInt, TypeTableEntryIdUndefLit, TypeTableEntryIdMaybe, - TypeTableEntryIdError, + TypeTableEntryIdErrorUnion, + TypeTableEntryIdPureError, TypeTableEntryIdEnum, TypeTableEntryIdFn, }; @@ -961,6 +961,7 @@ struct CodeGen { TypeTableEntry *entry_num_lit_int; TypeTableEntry *entry_num_lit_float; TypeTableEntry *entry_undef; + TypeTableEntry *entry_pure_error; } builtin_types; LLVMTargetDataRef target_data_ref; diff --git a/src/analyze.cpp b/src/analyze.cpp index 7553796ec3..8f5db687b6 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -19,6 +19,8 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE static TypeTableEntry *unwrapped_node_type(AstNode *node); static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, AstNode *node); +static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import, + BlockContext *context, AstNode *node, Buf *err_name); static AstNode *first_executing_node(AstNode *node) { switch (node->type) { @@ -47,7 +49,6 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeVariableDeclaration: case NodeTypeErrorValueDecl: case NodeTypeNumberLiteral: - case NodeTypeErrorLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeSymbol: @@ -71,6 +72,7 @@ static AstNode *first_executing_node(AstNode *node) { case NodeTypeSwitchExpr: case NodeTypeSwitchProng: case NodeTypeArrayType: + case NodeTypeErrorType: case NodeTypeContainerInitExpr: return node; } @@ -110,7 +112,8 @@ TypeTableEntry *new_type_table_entry(TypeTableEntryId id) { case TypeTableEntryIdNumLitInt: case TypeTableEntryIdMaybe: case TypeTableEntryIdFn: - case TypeTableEntryIdError: + case TypeTableEntryIdErrorUnion: + case TypeTableEntryIdPureError: case TypeTableEntryIdUndefLit: // nothing to init break; @@ -200,7 +203,7 @@ static TypeTableEntry *get_maybe_type(CodeGen *g, TypeTableEntry *child_type) { "val", di_file, line, child_type->size_in_bits, child_type->align_in_bits, 0, 0, child_type->di_type), LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), - "maybe", di_file, line, 8, 8, 8, 0, + "maybe", di_file, line, 8, 8, child_type->size_in_bits, 0, child_type->di_type), }; LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(g->dbuilder, @@ -223,7 +226,7 @@ static TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) { if (child_type->error_parent) { return child_type->error_parent; } else { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdError); + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdErrorUnion); assert(child_type->type_ref); assert(child_type->di_type); @@ -239,7 +242,38 @@ static TypeTableEntry *get_error_type(CodeGen *g, TypeTableEntry *child_type) { entry->di_type = g->err_tag_type->di_type; } else { - zig_panic("TODO get_error_type non-void"); + LLVMTypeRef elem_types[] = { + g->err_tag_type->type_ref, + child_type->type_ref, + }; + entry->type_ref = LLVMStructType(elem_types, 2, false); + entry->size_in_bits = g->err_tag_type->size_in_bits + child_type->size_in_bits; + entry->align_in_bits = g->err_tag_type->align_in_bits; + + LLVMZigDIScope *compile_unit_scope = LLVMZigCompileUnitToScope(g->compile_unit); + LLVMZigDIFile *di_file = nullptr; + unsigned line = 0; + entry->di_type = LLVMZigCreateReplaceableCompositeType(g->dbuilder, + LLVMZigTag_DW_structure_type(), buf_ptr(&entry->name), + compile_unit_scope, di_file, line); + + LLVMZigDIType *di_element_types[] = { + LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), + "tag", di_file, line, g->err_tag_type->size_in_bits, g->err_tag_type->align_in_bits, + 0, 0, child_type->di_type), + LLVMZigCreateDebugMemberType(g->dbuilder, LLVMZigTypeToScope(entry->di_type), + "value", di_file, line, child_type->size_in_bits, child_type->align_in_bits, + g->err_tag_type->size_in_bits, 0, child_type->di_type), + }; + + LLVMZigDIType *replacement_di_type = LLVMZigCreateDebugStructType(g->dbuilder, + compile_unit_scope, + buf_ptr(&entry->name), + di_file, line, entry->size_in_bits, entry->align_in_bits, 0, + nullptr, di_element_types, 2, 0, nullptr, ""); + + LLVMZigReplaceTemporary(g->dbuilder, entry->di_type, replacement_di_type); + entry->di_type = replacement_di_type; } child_type->error_parent = entry; @@ -1012,7 +1046,6 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeArrayAccessExpr: case NodeTypeSliceExpr: case NodeTypeNumberLiteral: - case NodeTypeErrorLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeBoolLiteral: @@ -1037,6 +1070,7 @@ static void resolve_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeStructValueField: case NodeTypeContainerInitExpr: case NodeTypeArrayType: + case NodeTypeErrorType: zig_unreachable(); } } @@ -1083,7 +1117,8 @@ static bool type_has_codegen_value(TypeTableEntryId id) { case TypeTableEntryIdArray: case TypeTableEntryIdStruct: case TypeTableEntryIdMaybe: - case TypeTableEntryIdError: + case TypeTableEntryIdErrorUnion: + case TypeTableEntryIdPureError: case TypeTableEntryIdEnum: case TypeTableEntryIdFn: return true; @@ -1166,8 +1201,8 @@ static bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTable } // error - if (expected_type->id == TypeTableEntryIdError && - actual_type->id == TypeTableEntryIdError) + if (expected_type->id == TypeTableEntryIdErrorUnion && + actual_type->id == TypeTableEntryIdErrorUnion) { return types_match_const_cast_only( expected_type->data.error.child_type, @@ -1224,11 +1259,11 @@ static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *pa prev_type = cur_type; prev_node = cur_node; } - } else if (prev_type->id == TypeTableEntryIdError && + } else if (prev_type->id == TypeTableEntryIdErrorUnion && types_match_const_cast_only(prev_type->data.error.child_type, cur_type)) { continue; - } else if (cur_type->id == TypeTableEntryIdError && + } else if (cur_type->id == TypeTableEntryIdErrorUnion && types_match_const_cast_only(cur_type->data.error.child_type, prev_type)) { prev_type = cur_type; @@ -1287,13 +1322,20 @@ static bool types_match_with_implicit_cast(CodeGen *g, TypeTableEntry *expected_ } // implicit conversion from error child type to error type - if (expected_type->id == TypeTableEntryIdError && + if (expected_type->id == TypeTableEntryIdErrorUnion && types_match_with_implicit_cast(g, expected_type->data.error.child_type, actual_type, literal_node, reported_err)) { return true; } + // implicit conversion from pure error to error union type + if (expected_type->id == TypeTableEntryIdErrorUnion && + actual_type->id == TypeTableEntryIdPureError) + { + return true; + } + // implicit widening conversion if (expected_type->id == TypeTableEntryIdInt && actual_type->id == TypeTableEntryIdInt && @@ -1695,12 +1737,11 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i AstNode *struct_expr_node = node->data.field_access_expr.struct_expr; TypeTableEntry *struct_type = analyze_expression(g, import, context, nullptr, struct_expr_node); + Buf *field_name = &node->data.field_access_expr.field_name; if (struct_type->id == TypeTableEntryIdStruct || (struct_type->id == TypeTableEntryIdPointer && struct_type->data.pointer.child_type->id == TypeTableEntryIdStruct)) { - Buf *field_name = &node->data.field_access_expr.field_name; - TypeTableEntry *bare_struct_type = (struct_type->id == TypeTableEntryIdStruct) ? struct_type : struct_type->data.pointer.child_type; @@ -1713,15 +1754,14 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i return g->builtin_types.entry_invalid; } } else if (struct_type->id == TypeTableEntryIdArray) { - Buf *name = &node->data.field_access_expr.field_name; - if (buf_eql_str(name, "len")) { + if (buf_eql_str(field_name, "len")) { return g->builtin_types.entry_isize; - } else if (buf_eql_str(name, "ptr")) { + } else if (buf_eql_str(field_name, "ptr")) { // TODO determine whether the pointer should be const return get_pointer_to_type(g, struct_type->data.array.child_type, false); } else { add_node_error(g, node, - buf_sprintf("no member named '%s' in '%s'", buf_ptr(name), + buf_sprintf("no member named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&struct_type->name))); return g->builtin_types.entry_invalid; } @@ -1731,8 +1771,9 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i if (enum_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } else if (enum_type->id == TypeTableEntryIdEnum) { - Buf *field_name = &node->data.field_access_expr.field_name; return analyze_enum_value_expr(g, import, context, node, nullptr, enum_type, field_name); + } else if (enum_type->id == TypeTableEntryIdPureError) { + return analyze_error_literal_expr(g, import, context, node, field_name); } else { add_node_error(g, node, buf_sprintf("type '%s' does not support field access", buf_ptr(&struct_type->name))); @@ -1846,7 +1887,7 @@ static TypeTableEntry *resolve_expr_const_val_as_err(CodeGen *g, AstNode *node, Expr *expr = get_resolved_expr(node); expr->const_val.ok = true; expr->const_val.data.x_err.err = err; - return get_error_type(g, g->builtin_types.entry_void); + return g->builtin_types.entry_pure_error; } static TypeTableEntry *resolve_expr_const_val_as_bool(CodeGen *g, AstNode *node, bool value) { @@ -1959,6 +2000,21 @@ static TypeTableEntry *resolve_expr_const_val_as_bignum_op(CodeGen *g, AstNode * return resolved_type; } +static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import, + BlockContext *context, AstNode *node, Buf *err_name) +{ + auto err_table_entry = import->block_context->error_table.maybe_get(err_name); + + if (err_table_entry) { + return resolve_expr_const_val_as_err(g, node, err_table_entry->value); + } + + add_node_error(g, node, + buf_sprintf("use of undeclared error value '%s'", buf_ptr(err_name))); + + return get_error_type(g, g->builtin_types.entry_void); +} + static TypeTableEntry *analyze_symbol_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) @@ -2564,23 +2620,6 @@ static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry } } -static TypeTableEntry *analyze_error_literal_expr(CodeGen *g, ImportTableEntry *import, - BlockContext *block_context, TypeTableEntry *expected_type, AstNode *node) -{ - Buf *err_name = &node->data.error_literal.symbol; - - auto err_table_entry = import->block_context->error_table.maybe_get(err_name); - - if (err_table_entry) { - return resolve_expr_const_val_as_err(g, node, err_table_entry->value); - } - - add_node_error(g, node, - buf_sprintf("use of undeclared error value '%s'", buf_ptr(err_name))); - - return get_error_type(g, g->builtin_types.entry_void); -} - static TypeTableEntry *analyze_array_type(CodeGen *g, ImportTableEntry *import, BlockContext *context, TypeTableEntry *expected_type, AstNode *node) { @@ -2906,6 +2945,10 @@ static void eval_const_expr_implicit_cast(CodeGen *g, AstNode *node, AstNode *ex const_val->data.x_err.payload = other_val; const_val->ok = true; break; + case CastOpPureErrorWrap: + const_val->data.x_err.err = other_val->data.x_err.err; + const_val->ok = true; + break; case CastOpErrToInt: { uint64_t value = other_val->data.x_err.err ? other_val->data.x_err.err->value : 0; @@ -3007,7 +3050,7 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B } // explicit cast from child type of error type to error type - if (wanted_type->id == TypeTableEntryIdError) { + if (wanted_type->id == TypeTableEntryIdErrorUnion) { if (types_match_const_cast_only(wanted_type->data.error.child_type, actual_type)) { node->data.fn_call_expr.cast_op = CastOpErrorWrap; eval_const_expr_implicit_cast(g, node, expr_node); @@ -3025,6 +3068,15 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B } } + // explicit cast from pure error to error union type + if (wanted_type->id == TypeTableEntryIdErrorUnion && + actual_type->id == TypeTableEntryIdPureError) + { + node->data.fn_call_expr.cast_op = CastOpPureErrorWrap; + eval_const_expr_implicit_cast(g, node, expr_node); + return wanted_type; + } + // explicit cast from number literal to another type if (actual_type->id == TypeTableEntryIdNumLitFloat || actual_type->id == TypeTableEntryIdNumLitInt) @@ -3039,8 +3091,10 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B } // explicit cast from %void to integer type which can fit it - if (actual_type->id == TypeTableEntryIdError && - actual_type->data.error.child_type->size_in_bits == 0 && + bool actual_type_is_void_err = actual_type->id == TypeTableEntryIdErrorUnion && + actual_type->data.error.child_type->size_in_bits == 0; + bool actual_type_is_pure_err = actual_type->id == TypeTableEntryIdPureError; + if ((actual_type_is_void_err || actual_type_is_pure_err) && wanted_type->id == TypeTableEntryIdInt) { BigNum bn; @@ -3754,9 +3808,6 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeNumberLiteral: return_type = analyze_number_literal_expr(g, import, context, expected_type, node); break; - case NodeTypeErrorLiteral: - return_type = analyze_error_literal_expr(g, import, context, expected_type, node); - break; case NodeTypeStringLiteral: return_type = analyze_string_literal_expr(g, import, context, expected_type, node); break; @@ -3794,6 +3845,9 @@ static TypeTableEntry *analyze_expression(CodeGen *g, ImportTableEntry *import, case NodeTypeArrayType: return_type = analyze_array_type(g, import, context, expected_type, node); break; + case NodeTypeErrorType: + return_type = resolve_expr_const_val_as_type(g, node, g->builtin_types.entry_pure_error); + break; case NodeTypeSwitchExpr: return_type = analyze_switch_expr(g, import, context, expected_type, node); break; @@ -3933,7 +3987,6 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeArrayAccessExpr: case NodeTypeSliceExpr: case NodeTypeNumberLiteral: - case NodeTypeErrorLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeBoolLiteral: @@ -3958,6 +4011,7 @@ static void analyze_top_level_decl(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeStructValueField: case NodeTypeContainerInitExpr: case NodeTypeArrayType: + case NodeTypeErrorType: zig_unreachable(); } } @@ -3967,7 +4021,6 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode { switch (node->type) { case NodeTypeNumberLiteral: - case NodeTypeErrorLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeBoolLiteral: @@ -3977,6 +4030,7 @@ static void collect_expr_decl_deps(CodeGen *g, ImportTableEntry *import, AstNode case NodeTypeBreak: case NodeTypeContinue: case NodeTypeErrorValueDecl: + case NodeTypeErrorType: // no dependencies on other top level declarations break; case NodeTypeSymbol: @@ -4286,7 +4340,6 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast case NodeTypeArrayAccessExpr: case NodeTypeSliceExpr: case NodeTypeNumberLiteral: - case NodeTypeErrorLiteral: case NodeTypeStringLiteral: case NodeTypeCharLiteral: case NodeTypeBoolLiteral: @@ -4311,6 +4364,7 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast case NodeTypeContainerInitExpr: case NodeTypeStructValueField: case NodeTypeArrayType: + case NodeTypeErrorType: zig_unreachable(); } } @@ -4419,7 +4473,14 @@ void semantic_analyze(CodeGen *g) { } } - g->err_tag_type = get_smallest_unsigned_int_type(g, g->error_value_count); + { + g->err_tag_type = get_smallest_unsigned_int_type(g, g->error_value_count); + + g->builtin_types.entry_pure_error->type_ref = g->err_tag_type->type_ref; + g->builtin_types.entry_pure_error->size_in_bits = g->err_tag_type->size_in_bits; + g->builtin_types.entry_pure_error->align_in_bits = g->err_tag_type->align_in_bits; + g->builtin_types.entry_pure_error->di_type = g->err_tag_type->di_type; + } { auto it = g->import_table.entry_iterator(); @@ -4493,8 +4554,6 @@ Expr *get_resolved_expr(AstNode *node) { return &node->data.container_init_expr.resolved_expr; case NodeTypeNumberLiteral: return &node->data.number_literal.resolved_expr; - case NodeTypeErrorLiteral: - return &node->data.error_literal.resolved_expr; case NodeTypeStringLiteral: return &node->data.string_literal.resolved_expr; case NodeTypeBlock: @@ -4521,6 +4580,8 @@ Expr *get_resolved_expr(AstNode *node) { return &node->data.label.resolved_expr; case NodeTypeArrayType: return &node->data.array_type.resolved_expr; + case NodeTypeErrorType: + return &node->data.error_type.resolved_expr; case NodeTypeSwitchExpr: return &node->data.switch_expr.resolved_expr; case NodeTypeSwitchProng: @@ -4554,7 +4615,6 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { case NodeTypeErrorValueDecl: return &node->data.error_value_decl.top_level_decl; case NodeTypeNumberLiteral: - case NodeTypeErrorLiteral: case NodeTypeReturnExpr: case NodeTypeBinOpExpr: case NodeTypePrefixOpExpr: @@ -4593,6 +4653,7 @@ TopLevelDecl *get_resolved_top_level_decl(AstNode *node) { case NodeTypeStructField: case NodeTypeStructValueField: case NodeTypeArrayType: + case NodeTypeErrorType: zig_unreachable(); } zig_unreachable(); diff --git a/src/codegen.cpp b/src/codegen.cpp index 786127cd42..be96fe1b9f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -306,7 +306,7 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { case CastOpNoop: return expr_val; case CastOpErrToInt: - assert(actual_type->id == TypeTableEntryIdError); + assert(actual_type->id == TypeTableEntryIdErrorUnion); if (actual_type->data.error.child_type->size_in_bits == 0) { return gen_widen_or_shorten(g, node, g->err_tag_type, wanted_type, expr_val); } else { @@ -330,12 +330,19 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) { return cast_expr->tmp_ptr; } case CastOpErrorWrap: - assert(wanted_type->id == TypeTableEntryIdError); + assert(wanted_type->id == TypeTableEntryIdErrorUnion); if (wanted_type->data.error.child_type->size_in_bits == 0) { return LLVMConstNull(g->err_tag_type->type_ref); } else { zig_panic("TODO"); } + case CastOpPureErrorWrap: + assert(wanted_type->id == TypeTableEntryIdErrorUnion); + if (wanted_type->data.error.child_type->size_in_bits == 0) { + return expr_val; + } else { + zig_panic("TODO"); + } case CastOpPtrToInt: add_debug_source_node(g, node); return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, ""); @@ -1292,7 +1299,7 @@ static LLVMValueRef gen_if_bool_expr_raw(CodeGen *g, AstNode *source_node, LLVMV return nullptr; } - assert(!use_expr_value || then_type->id == TypeTableEntryIdError); + assert(!use_expr_value || then_type->id == TypeTableEntryIdErrorUnion); LLVMBasicBlockRef then_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "Then"); LLVMBasicBlockRef endif_block = LLVMAppendBasicBlock(g->cur_fn->fn_value, "EndIf"); @@ -2011,7 +2018,6 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { return gen_container_init_expr(g, node); case NodeTypeSwitchExpr: return gen_switch_expr(g, node); - case NodeTypeErrorLiteral: case NodeTypeNumberLiteral: case NodeTypeBoolLiteral: case NodeTypeStringLiteral: @@ -2033,6 +2039,7 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) { case NodeTypeStructField: case NodeTypeStructValueField: case NodeTypeArrayType: + case NodeTypeErrorType: case NodeTypeSwitchProng: case NodeTypeSwitchRange: case NodeTypeErrorValueDecl: @@ -2063,6 +2070,9 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE if (type_entry->id == TypeTableEntryIdInt) { return LLVMConstInt(type_entry->type_ref, bignum_to_twos_complement(&const_val->data.x_bignum), false); + } else if (type_entry->id == TypeTableEntryIdPureError) { + assert(const_val->data.x_err.err); + return LLVMConstInt(g->builtin_types.entry_pure_error->type_ref, const_val->data.x_err.err->value, false); } else if (type_entry->id == TypeTableEntryIdFloat) { if (const_val->data.x_bignum.kind == BigNumKindFloat) { return LLVMConstReal(type_entry->type_ref, const_val->data.x_bignum.data.x_float); @@ -2148,12 +2158,26 @@ static LLVMValueRef gen_const_val(CodeGen *g, TypeTableEntry *type_entry, ConstE } else { return global_value; } - } else if (type_entry->id == TypeTableEntryIdError) { - if (type_entry->data.error.child_type->size_in_bits == 0) { + } else if (type_entry->id == TypeTableEntryIdErrorUnion) { + TypeTableEntry *child_type = type_entry->data.error.child_type; + if (child_type->size_in_bits == 0) { uint64_t value = const_val->data.x_err.err ? const_val->data.x_err.err->value : 0; return LLVMConstInt(g->err_tag_type->type_ref, value, false); } else { - zig_panic("TODO"); + LLVMValueRef err_tag_value; + LLVMValueRef err_payload_value; + if (const_val->data.x_err.err) { + err_tag_value = LLVMConstInt(g->err_tag_type->type_ref, const_val->data.x_err.err->value, false); + err_payload_value = LLVMConstNull(child_type->type_ref); + } else { + err_tag_value = LLVMConstNull(g->err_tag_type->type_ref); + err_payload_value = gen_const_val(g, child_type, const_val->data.x_err.payload); + } + LLVMValueRef fields[] = { + err_tag_value, + err_payload_value, + }; + return LLVMConstStruct(fields, 2, false); } } else { zig_unreachable(); @@ -2557,6 +2581,14 @@ static void define_builtin_types(CodeGen *g) { g->builtin_types.entry_type = entry; g->primitive_type_table.put(&entry->name, entry); } + { + // partially complete the error type. we complete it later after we know + // error_value_count. + TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdPureError); + buf_init_from_str(&entry->name, "error"); + g->builtin_types.entry_pure_error = entry; + g->primitive_type_table.put(&entry->name, entry); + } g->builtin_types.entry_u8 = get_int_type(g, false, 8); g->builtin_types.entry_u16 = get_int_type(g, false, 16); diff --git a/src/parser.cpp b/src/parser.cpp index 8370a022b5..523ad1a891 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -113,8 +113,6 @@ const char *node_type_str(NodeType node_type) { return "ErrorValueDecl"; case NodeTypeNumberLiteral: return "NumberLiteral"; - case NodeTypeErrorLiteral: - return "ErrorLiteral"; case NodeTypeStringLiteral: return "StringLiteral"; case NodeTypeCharLiteral: @@ -167,6 +165,8 @@ const char *node_type_str(NodeType node_type) { return "ContainerInitExpr"; case NodeTypeArrayType: return "ArrayType"; + case NodeTypeErrorType: + return "ErrorType"; } zig_unreachable(); } @@ -313,11 +313,6 @@ void ast_print(AstNode *node, int indent) { } break; } - case NodeTypeErrorLiteral: - { - fprintf(stderr, "%s '%s'", node_type_str(node->type), buf_ptr(&node->data.error_literal.symbol)); - break; - } case NodeTypeStringLiteral: { const char *c = node->data.string_literal.c ? "c" : ""; @@ -465,6 +460,9 @@ void ast_print(AstNode *node, int indent) { ast_print(node->data.array_type.child_type, indent + 2); break; } + case NodeTypeErrorType: + fprintf(stderr, "%s\n", node_type_str(node->type)); + break; } } @@ -1372,8 +1370,8 @@ static AstNode *ast_parse_asm_expr(ParseContext *pc, int *token_index, bool mand } /* -PrimaryExpression : "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | AsmExpression | ("%." "Symbol") -KeywordLiteral : "true" | "false" | "null" | "break" | "continue" | "undefined" +PrimaryExpression : "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | AsmExpression | ("error" "." "Symbol") +KeywordLiteral : "true" | "false" | "null" | "break" | "continue" | "undefined" | "error" */ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -1419,6 +1417,10 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool AstNode *node = ast_create_node(pc, NodeTypeUndefinedLiteral, token); *token_index += 1; return node; + } else if (token->id == TokenIdKeywordError) { + AstNode *node = ast_create_node(pc, NodeTypeErrorType, token); + *token_index += 1; + return node; } else if (token->id == TokenIdAtSign) { *token_index += 1; Token *name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); @@ -1448,12 +1450,6 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool ast_buf_from_token(pc, dest_symbol, &node->data.goto_expr.name); return node; - } else if (token->id == TokenIdPercentDot) { - *token_index += 1; - Token *symbol_tok = ast_eat_token(pc, token_index, TokenIdSymbol); - AstNode *node = ast_create_node(pc, NodeTypeErrorLiteral, token); - ast_buf_from_token(pc, symbol_tok, &node->data.error_literal.symbol); - return node; } AstNode *grouped_expr_node = ast_parse_grouped_expr(pc, token_index, false); @@ -2969,7 +2965,7 @@ static AstNode *ast_parse_struct_decl(ParseContext *pc, int *token_index) { } /* -ErrorValueDecl : option(FnVisibleMod) "%." "Symbol" +ErrorValueDecl : option(FnVisibleMod) "error" "Symbol" */ static AstNode *ast_parse_error_value_decl(ParseContext *pc, int *token_index, bool mandatory) { Token *first_token = &pc->tokens->at(*token_index); @@ -2978,7 +2974,7 @@ static AstNode *ast_parse_error_value_decl(ParseContext *pc, int *token_index, b if (first_token->id == TokenIdKeywordPub) { Token *next_token = &pc->tokens->at(*token_index + 1); - if (next_token->id == TokenIdPercentDot) { + if (next_token->id == TokenIdKeywordError) { visib_mod = VisibModPub; *token_index += 2; } else if (mandatory) { @@ -2988,7 +2984,7 @@ static AstNode *ast_parse_error_value_decl(ParseContext *pc, int *token_index, b } } else if (first_token->id == TokenIdKeywordExport) { Token *next_token = &pc->tokens->at(*token_index + 1); - if (next_token->id == TokenIdPercentDot) { + if (next_token->id == TokenIdKeywordError) { visib_mod = VisibModExport; *token_index += 2; } else if (mandatory) { @@ -2996,7 +2992,7 @@ static AstNode *ast_parse_error_value_decl(ParseContext *pc, int *token_index, b } else { return nullptr; } - } else if (first_token->id == TokenIdPercentDot) { + } else if (first_token->id == TokenIdKeywordError) { visib_mod = VisibModPrivate; *token_index += 1; } else if (mandatory) { @@ -3177,9 +3173,6 @@ void normalize_parent_ptrs(AstNode *node) { case NodeTypeCharLiteral: // none break; - case NodeTypeErrorLiteral: - // none - break; case NodeTypeSymbol: // none break; @@ -3290,5 +3283,8 @@ void normalize_parent_ptrs(AstNode *node) { set_field(&node->data.array_type.size); set_field(&node->data.array_type.child_type); break; + case NodeTypeErrorType: + // none + break; } } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index bb4a59fc79..97186a231c 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -247,6 +247,8 @@ static void end_token(Tokenize *t) { t->cur_tok->id = TokenIdKeywordSwitch; } else if (mem_eql_str(token_mem, token_len, "undefined")) { t->cur_tok->id = TokenIdKeywordUndefined; + } else if (mem_eql_str(token_mem, token_len, "error")) { + t->cur_tok->id = TokenIdKeywordError; } t->cur_tok = nullptr; @@ -1046,6 +1048,7 @@ const char * token_name(TokenId id) { case TokenIdKeywordNoAlias: return "noalias"; case TokenIdKeywordSwitch: return "switch"; case TokenIdKeywordUndefined: return "undefined"; + case TokenIdKeywordError: return "error"; case TokenIdLParen: return "("; case TokenIdRParen: return ")"; case TokenIdComma: return ","; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index be0c562a3a..0da25cfe0d 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -38,6 +38,7 @@ enum TokenId { TokenIdKeywordNoAlias, TokenIdKeywordSwitch, TokenIdKeywordUndefined, + TokenIdKeywordError, TokenIdLParen, TokenIdRParen, TokenIdComma, diff --git a/std/std.zig b/std/std.zig index 734b4e7ae8..8cc75a30fe 100644 --- a/std/std.zig +++ b/std/std.zig @@ -25,15 +25,15 @@ pub var stderr = OutStream { }; */ -pub %.Unexpected; -pub %.DiskQuota; -pub %.FileTooBig; -pub %.SigInterrupt; -pub %.Io; -pub %.NoSpaceLeft; -pub %.BadPerm; -pub %.PipeFail; -pub %.Invalid; +pub error Unexpected; +pub error DiskQuota; +pub error FileTooBig; +pub error SigInterrupt; +pub error Io; +pub error NoSpaceLeft; +pub error BadPerm; +pub error PipeFail; +pub error Invalid; const buffer_size = 4 * 1024; const max_u64_base10_digits = 20; @@ -100,14 +100,14 @@ pub struct OutStream { os.index = 0; switch (write(os.fd, os.buffer.ptr, amt_to_write)) { EINVAL => unreachable{}, - EDQUOT => %.DiskQuota, - EFBIG => %.FileTooBig, - EINTR => %.SigInterrupt, - EIO => %.Io, - ENOSPC => %.NoSpaceLeft, - EPERM => %.BadPerm, - EPIPE => %.PipeFail, - else => %.Unexpected, + EDQUOT => error.DiskQuota, + EFBIG => error.FileTooBig, + EINTR => error.SigInterrupt, + EIO => error.Io, + ENOSPC => error.NoSpaceLeft, + EPERM => error.BadPerm, + EPIPE => error.PipeFail, + else => error.Unexpected, } } } @@ -121,10 +121,10 @@ pub struct InStream { switch (-amt_read) { EINVAL => unreachable{}, EFAULT => unreachable{}, - EBADF => %.BadFd, - EINTR => %.SigInterrupt, - EIO => %.Io, - else => %.Unexpected, + EBADF => error.BadFd, + EINTR => error.SigInterrupt, + EIO => error.Io, + else => error.Unexpected, } } return amt_read; @@ -136,8 +136,8 @@ pub fn os_get_random_bytes(buf: []u8) %void => { switch (getrandom(buf.ptr, buf.len, 0)) { EINVAL => unreachable{}, EFAULT => unreachable{}, - EINTR => %.SigInterrupt, - else => %.Unexpected, + EINTR => error.SigInterrupt, + else => error.Unexpected, } } */ diff --git a/test/run_tests.cpp b/test/run_tests.cpp index e6f7d2a01e..5f7a9ec8e0 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -1213,11 +1213,11 @@ pub fn main(args: [][]u8) %void => { add_simple_case("error values", R"SOURCE( import "std.zig"; -%.err1; -%.err2; +error err1; +error err2; pub fn main(args: [][]u8) %void => { - const a = i32(%.err1); - const b = i32(%.err2); + const a = i32(error.err1); + const b = i32(error.err2); if (a == b) { print_str("BAD\n"); } @@ -1467,8 +1467,8 @@ enum A {} )SOURCE", 1, ".tmp_source.zig:3:1: error: redefinition of 'A'"); add_compile_fail_case("redefinition of error values", R"SOURCE( -%.A; -%.A; +error A; +error A; )SOURCE", 1, ".tmp_source.zig:3:1: error: redefinition of error 'A'"); add_compile_fail_case("redefinition of global variables", R"SOURCE(