diff --git a/doc/langref.md b/doc/langref.md index 4117f4f7d08bec8591d279b41ca362e9d0eb0a8f..595287f5f61231c851e22e7d7c9ac0f8ba9615f3 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -69,7 +69,7 @@ UnwrapError = "%%" option("|" "Symbol" "|") Expression AssignmentExpression = UnwrapExpression AssignmentOperator UnwrapExpression | UnwrapExpression -AssignmentOperator = "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" | "&&=" | "||=" +AssignmentOperator = "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" | "&&=" | "||=" | "*%=" | "+%=" | "-%=" | "<<%=" BlockExpression = IfExpression | Block | WhileExpression | ForExpression | SwitchExpression @@ -111,17 +111,17 @@ BinaryAndExpression = BitShiftExpression "&" BinaryAndExpression | BitShiftExpre BitShiftExpression = AdditionExpression BitShiftOperator BitShiftExpression | AdditionExpression -BitShiftOperator = "<<" | ">>" +BitShiftOperator = "<<" | ">>" | "<<%" AdditionExpression = MultiplyExpression AdditionOperator AdditionExpression | MultiplyExpression -AdditionOperator = "+" | "-" | "++" +AdditionOperator = "+" | "-" | "++" | "+%" | "-%" MultiplyExpression = CurlySuffixExpression MultiplyOperator MultiplyExpression | CurlySuffixExpression CurlySuffixExpression = TypeExpr option(ContainerInitExpression) -MultiplyOperator = "*" | "/" | "%" | "**" +MultiplyOperator = "*" | "/" | "%" | "**" | "*%" PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression @@ -141,7 +141,7 @@ 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 | (option("extern") FnProto) | AsmExpression | ("error" "." "Symbol") @@ -181,26 +181,15 @@ x{} Type name C equivalent Description i8 int8_t signed 8-bit integer -u8 (none) unsigned 8-bit integer +u8 uint8_t unsigned 8-bit integer i16 int16_t signed 16-bit integer -u16 (none) unsigned 16-bit integer +u16 uint16_t unsigned 16-bit integer i32 int32_t signed 32-bit integer -u32 (none) unsigned 32-bit integer +u32 uint32_t unsigned 32-bit integer i64 int64_t signed 64-bit integer -u64 (none) unsigned 64-bit integer +u64 uint64_t unsigned 64-bit integer isize intptr_t signed pointer sized integer -usize (none) unsigned pointer sized integer - -i8w (none) wrapping signed 8-bit integer -u8w uint8_t wrapping unsigned 8-bit integer -i16w (none) wrapping signed 16-bit integer -u16w uint16_t wrapping unsigned 16-bit integer -i32w (none) wrapping signed 32-bit integer -u32w uint32_t wrapping unsigned 32-bit integer -i64w (none) wrapping signed 64-bit integer -u64w uint64_t wrapping unsigned 64-bit integer -isizew (none) wrapping signed pointer sized integer -usizew uintptr_t wrapping unsigned pointer sized integer +usize uintptr_t unsigned pointer sized integer c_short short for ABI compatibility with C c_ushort unsigned short for ABI compatibility with C @@ -213,8 +202,8 @@ c_ulonglong unsigned long long for ABI compatibility with C c_long_double long double for ABI compatibility with C c_void void for ABI compatibility with C -f32 float 32-bit IEE754 floating point -f64 double 64-bit IEE754 floating point +f32 float 32-bit floating point +f64 double 64-bit floating point ``` ### Boolean Type @@ -595,9 +584,8 @@ const b: u8 = @truncate(u8, a); This function, when semantically analyzed, causes a compile error with the message `msg`. There are several ways that code avoids being semantically checked, such as using `if` -or `switch` with compile variables, and inline functions. +or `switch` with compile time constants, and inline functions. -### @int_type(inline is_signed: bool, inline bit_count: u8, inline is_wrapping: bool) -> type +### @int_type(inline is_signed: bool, inline bit_count: u8) -> type -This function returns an integer type with the given signness, bit count, and -wrapping behavior. +This function returns an integer type with the given signness and bit count. diff --git a/doc/vim/syntax/zig.vim b/doc/vim/syntax/zig.vim index 5ce2dbea02acb6c92b8a14ff1d05c7d6b0efcc09..e14b6364e19d6c142ba06d4864aa7babbb8ca13a 100644 --- a/doc/vim/syntax/zig.vim +++ b/doc/vim/syntax/zig.vim @@ -17,7 +17,6 @@ syn keyword zigConstant null undefined syn keyword zigKeyword fn use syn keyword zigType bool f32 f64 void unreachable type error syn keyword zigType i8 u8 i16 u16 i32 u32 i64 u64 isize usize -syn keyword zigType i8w u8w i16w u16w i32w u32w i64w u64w isizew usizew syn keyword zigType c_short c_ushort c_int c_uint c_long c_ulong c_longlong c_ulonglong syn keyword zigBoolean true false diff --git a/src/all_types.hpp b/src/all_types.hpp index 60ce641de40653fe6b33a88b90ba8c11b7198c67..35981477006c3962333ced8f3a65e5623fa83028 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -315,11 +315,15 @@ enum BinOpType { BinOpTypeInvalid, BinOpTypeAssign, BinOpTypeAssignTimes, + BinOpTypeAssignTimesWrap, BinOpTypeAssignDiv, BinOpTypeAssignMod, BinOpTypeAssignPlus, + BinOpTypeAssignPlusWrap, BinOpTypeAssignMinus, + BinOpTypeAssignMinusWrap, BinOpTypeAssignBitShiftLeft, + BinOpTypeAssignBitShiftLeftWrap, BinOpTypeAssignBitShiftRight, BinOpTypeAssignBitAnd, BinOpTypeAssignBitXor, @@ -338,10 +342,14 @@ enum BinOpType { BinOpTypeBinXor, BinOpTypeBinAnd, BinOpTypeBitShiftLeft, + BinOpTypeBitShiftLeftWrap, BinOpTypeBitShiftRight, BinOpTypeAdd, + BinOpTypeAddWrap, BinOpTypeSub, + BinOpTypeSubWrap, BinOpTypeMult, + BinOpTypeMultWrap, BinOpTypeDiv, BinOpTypeMod, BinOpTypeUnwrapMaybe, @@ -449,6 +457,7 @@ enum PrefixOp { PrefixOpBoolNot, PrefixOpBinNot, PrefixOpNegation, + PrefixOpNegationWrap, PrefixOpAddressOf, PrefixOpConstAddressOf, PrefixOpDereference, @@ -860,7 +869,6 @@ struct TypeTableEntryPointer { struct TypeTableEntryInt { int bit_count; bool is_signed; - bool is_wrapping; }; struct TypeTableEntryFloat { @@ -1185,7 +1193,7 @@ struct CodeGen { struct { TypeTableEntry *entry_bool; - TypeTableEntry *entry_int[2][2][4]; // [signed,unsigned][wrapping,nonwrapping][8,16,32,64] + TypeTableEntry *entry_int[2][4]; // [signed,unsigned][8,16,32,64] TypeTableEntry *entry_c_int[CIntTypeCount]; TypeTableEntry *entry_c_long_double; TypeTableEntry *entry_c_void; diff --git a/src/analyze.cpp b/src/analyze.cpp index d736498eab7ad1c758daacee272a5c6abd95fe39..6bad7fab2d9148fb15155aada9dbde1ea6360502 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -245,7 +245,7 @@ static bool is_slice(TypeTableEntry *type) { } TypeTableEntry *get_smallest_unsigned_int_type(CodeGen *g, uint64_t x) { - return get_int_type(g, false, false, bits_needed_for_unsigned(x)); + return get_int_type(g, false, bits_needed_for_unsigned(x)); } static TypeTableEntry *get_generic_fn_type(CodeGen *g, AstNode *decl_node) { @@ -2037,25 +2037,13 @@ static TypeTableEntry *determine_peer_type_compatibility(CodeGen *g, AstNode *pa continue; } else if (prev_type->id == TypeTableEntryIdInt && cur_type->id == TypeTableEntryIdInt && - prev_type->data.integral.is_signed == cur_type->data.integral.is_signed && - (cur_type->data.integral.bit_count >= prev_type->data.integral.bit_count && - (cur_type->data.integral.is_wrapping || !prev_type->data.integral.is_wrapping))) + prev_type->data.integral.is_signed == cur_type->data.integral.is_signed) { if (cur_type->data.integral.bit_count > prev_type->data.integral.bit_count) { prev_type = cur_type; prev_node = cur_node; - } else if (cur_type->data.integral.is_wrapping && !prev_type->data.integral.is_wrapping) { - prev_type = cur_type; - prev_node = cur_node; } continue; - } else if (prev_type->id == TypeTableEntryIdInt && - cur_type->id == TypeTableEntryIdInt && - prev_type->data.integral.is_signed == cur_type->data.integral.is_signed && - (prev_type->data.integral.bit_count >= cur_type->data.integral.bit_count && - (prev_type->data.integral.is_wrapping || !cur_type->data.integral.is_wrapping))) - { - continue; } else if (prev_type->id == TypeTableEntryIdFloat && cur_type->id == TypeTableEntryIdFloat) { @@ -2726,9 +2714,6 @@ static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *i } else if (buf_eql_str(field_name, "is_signed")) { return resolve_expr_const_val_as_bool(g, node, child_type->data.integral.is_signed, depends_on_compile_var); - } else if (buf_eql_str(field_name, "is_wrapping")) { - return resolve_expr_const_val_as_bool(g, node, child_type->data.integral.is_wrapping, - depends_on_compile_var); } else { add_node_error(g, node, buf_sprintf("type '%s' has no member called '%s'", @@ -3122,15 +3107,19 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) { case BinOpTypeAssign: return true; case BinOpTypeAssignTimes: + case BinOpTypeAssignTimesWrap: case BinOpTypeAssignDiv: case BinOpTypeAssignMod: return type->id == TypeTableEntryIdInt || type->id == TypeTableEntryIdFloat; case BinOpTypeAssignPlus: + case BinOpTypeAssignPlusWrap: case BinOpTypeAssignMinus: + case BinOpTypeAssignMinusWrap: return type->id == TypeTableEntryIdInt || type->id == TypeTableEntryIdFloat || type->id == TypeTableEntryIdPointer; case BinOpTypeAssignBitShiftLeft: + case BinOpTypeAssignBitShiftLeftWrap: case BinOpTypeAssignBitShiftRight: case BinOpTypeAssignBitAnd: case BinOpTypeAssignBitXor: @@ -3153,10 +3142,14 @@ static bool is_op_allowed(TypeTableEntry *type, BinOpType op) { case BinOpTypeBinXor: case BinOpTypeBinAnd: case BinOpTypeBitShiftLeft: + case BinOpTypeBitShiftLeftWrap: case BinOpTypeBitShiftRight: case BinOpTypeAdd: + case BinOpTypeAddWrap: case BinOpTypeSub: + case BinOpTypeSubWrap: case BinOpTypeMult: + case BinOpTypeMultWrap: case BinOpTypeDiv: case BinOpTypeMod: case BinOpTypeUnwrapMaybe: @@ -3437,11 +3430,15 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, switch (bin_op_type) { case BinOpTypeAssign: case BinOpTypeAssignTimes: + case BinOpTypeAssignTimesWrap: case BinOpTypeAssignDiv: case BinOpTypeAssignMod: case BinOpTypeAssignPlus: + case BinOpTypeAssignPlusWrap: case BinOpTypeAssignMinus: + case BinOpTypeAssignMinusWrap: case BinOpTypeAssignBitShiftLeft: + case BinOpTypeAssignBitShiftLeftWrap: case BinOpTypeAssignBitShiftRight: case BinOpTypeAssignBitAnd: case BinOpTypeAssignBitXor: @@ -3481,10 +3478,14 @@ static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, case BinOpTypeBinXor: case BinOpTypeBinAnd: case BinOpTypeBitShiftLeft: + case BinOpTypeBitShiftLeftWrap: case BinOpTypeBitShiftRight: case BinOpTypeAdd: + case BinOpTypeAddWrap: case BinOpTypeSub: + case BinOpTypeSubWrap: case BinOpTypeMult: + case BinOpTypeMultWrap: case BinOpTypeDiv: case BinOpTypeMod: { @@ -4911,42 +4912,35 @@ static TypeTableEntry *analyze_int_type(CodeGen *g, ImportTableEntry *import, { AstNode **is_signed_node = &node->data.fn_call_expr.params.at(0); AstNode **bit_count_node = &node->data.fn_call_expr.params.at(1); - AstNode **is_wrap_node = &node->data.fn_call_expr.params.at(2); TypeTableEntry *bool_type = g->builtin_types.entry_bool; TypeTableEntry *usize_type = g->builtin_types.entry_usize; TypeTableEntry *is_signed_type = analyze_expression(g, import, context, bool_type, *is_signed_node); TypeTableEntry *bit_count_type = analyze_expression(g, import, context, usize_type, *bit_count_node); - TypeTableEntry *is_wrap_type = analyze_expression(g, import, context, bool_type, *is_wrap_node); if (is_signed_type->id == TypeTableEntryIdInvalid || - bit_count_type->id == TypeTableEntryIdInvalid || - is_wrap_type->id == TypeTableEntryIdInvalid) + bit_count_type->id == TypeTableEntryIdInvalid) { return g->builtin_types.entry_invalid; } ConstExprValue *is_signed_val = &get_resolved_expr(*is_signed_node)->const_val; ConstExprValue *bit_count_val = &get_resolved_expr(*bit_count_node)->const_val; - ConstExprValue *is_wrap_val = &get_resolved_expr(*is_wrap_node)->const_val; AstNode *bad_node = nullptr; if (!is_signed_val->ok) { bad_node = *is_signed_node; } else if (!bit_count_val->ok) { bad_node = *bit_count_node; - } else if (!is_wrap_val->ok) { - bad_node = *is_wrap_node; } if (bad_node) { add_node_error(g, bad_node, buf_sprintf("unable to evaluate constant expression")); return g->builtin_types.entry_invalid; } - bool depends_on_compile_var = is_signed_val->depends_on_compile_var || - bit_count_val->depends_on_compile_var || is_wrap_val->depends_on_compile_var; + bool depends_on_compile_var = is_signed_val->depends_on_compile_var || bit_count_val->depends_on_compile_var; - TypeTableEntry *int_type = get_int_type(g, is_signed_val->data.x_bool, is_wrap_val->data.x_bool, + TypeTableEntry *int_type = get_int_type(g, is_signed_val->data.x_bool, bit_count_val->data.x_bignum.data.x_uint); return resolve_expr_const_val_as_type(g, node, int_type, depends_on_compile_var); @@ -5727,15 +5721,17 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo // TODO const expr eval } case PrefixOpNegation: + case PrefixOpNegationWrap: { TypeTableEntry *expr_type = analyze_expression(g, import, context, nullptr, *expr_node); if (expr_type->id == TypeTableEntryIdInvalid) { return expr_type; } else if ((expr_type->id == TypeTableEntryIdInt && expr_type->data.integral.is_signed) || - expr_type->id == TypeTableEntryIdFloat || expr_type->id == TypeTableEntryIdNumLitInt || - expr_type->id == TypeTableEntryIdNumLitFloat) + ((expr_type->id == TypeTableEntryIdFloat || + expr_type->id == TypeTableEntryIdNumLitFloat) && + prefix_op != PrefixOpNegationWrap)) { ConstExprValue *target_const_val = &get_resolved_expr(*expr_node)->const_val; if (!target_const_val->ok) { @@ -5745,10 +5741,28 @@ static TypeTableEntry *analyze_prefix_op_expr(CodeGen *g, ImportTableEntry *impo const_val->ok = true; const_val->depends_on_compile_var = target_const_val->depends_on_compile_var; bignum_negate(&const_val->data.x_bignum, &target_const_val->data.x_bignum); + if (expr_type->id == TypeTableEntryIdFloat || + expr_type->id == TypeTableEntryIdNumLitFloat || + expr_type->id == TypeTableEntryIdNumLitInt) + { + return expr_type; + } + + bool overflow = !bignum_fits_in_bits(&const_val->data.x_bignum, + expr_type->data.integral.bit_count, expr_type->data.integral.is_signed); + if (prefix_op == PrefixOpNegationWrap) { + if (overflow) { + const_val->data.x_bignum.is_negative = true; + } + } else if (overflow) { + add_node_error(g, *expr_node, buf_sprintf("negation caused overflow")); + return g->builtin_types.entry_invalid; + } return expr_type; } else { - add_node_error(g, node, buf_sprintf("invalid negation type: '%s'", - buf_ptr(&expr_type->name))); + const char *fmt = (prefix_op == PrefixOpNegationWrap) ? + "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'"; + add_node_error(g, node, buf_sprintf(fmt, buf_ptr(&expr_type->name))); return g->builtin_types.entry_invalid; } } @@ -7059,7 +7073,7 @@ bool is_node_void_expr(AstNode *node) { return false; } -TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, bool is_wrapping, int size_in_bits) { +TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, int size_in_bits) { int index; if (size_in_bits == 8) { index = 0; @@ -7072,11 +7086,11 @@ TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, bool is_wrapping, } else { zig_unreachable(); } - return &g->builtin_types.entry_int[is_signed ? 0 : 1][is_wrapping ? 0 : 1][index]; + return &g->builtin_types.entry_int[is_signed ? 0 : 1][index]; } -TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, bool is_wrapping, int size_in_bits) { - return *get_int_type_ptr(g, is_signed, is_wrapping, size_in_bits); +TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits) { + return *get_int_type_ptr(g, is_signed, size_in_bits); } TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type) { diff --git a/src/analyze.hpp b/src/analyze.hpp index 44fc297f8da04bb1221f10df6e142170564cd3b1..75af917b9af19f1a024eb00790e9a26183ca1a8c 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -19,8 +19,8 @@ BlockContext *new_block_context(AstNode *node, BlockContext *parent); Expr *get_resolved_expr(AstNode *node); bool is_node_void_expr(AstNode *node); uint64_t type_size(CodeGen *g, TypeTableEntry *type_entry); -TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, bool is_wrapping, int size_in_bits); -TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, bool is_wrapping, int size_in_bits); +TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, int size_in_bits); +TypeTableEntry *get_int_type(CodeGen *g, bool is_signed, int size_in_bits); TypeTableEntry **get_c_int_type_ptr(CodeGen *g, CIntType c_int_type); TypeTableEntry *get_c_int_type(CodeGen *g, CIntType c_int_type); TypeTableEntry *get_typedecl_type(CodeGen *g, const char *name, TypeTableEntry *child_type); diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 9d9f56cb3c2770d5e9bd959acdd3fc06b204c328..c1d50ddfff90e3fb463497ed11021da59fda1b7c 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -4,41 +4,49 @@ static const char *bin_op_str(BinOpType bin_op) { switch (bin_op) { - case BinOpTypeInvalid: return "(invalid)"; - case BinOpTypeBoolOr: return "||"; - case BinOpTypeBoolAnd: return "&&"; - case BinOpTypeCmpEq: return "=="; - case BinOpTypeCmpNotEq: return "!="; - case BinOpTypeCmpLessThan: return "<"; - case BinOpTypeCmpGreaterThan: return ">"; - case BinOpTypeCmpLessOrEq: return "<="; - case BinOpTypeCmpGreaterOrEq: return ">="; - case BinOpTypeBinOr: return "|"; - case BinOpTypeBinXor: return "^"; - case BinOpTypeBinAnd: return "&"; - case BinOpTypeBitShiftLeft: return "<<"; - case BinOpTypeBitShiftRight: return ">>"; - case BinOpTypeAdd: return "+"; - case BinOpTypeSub: return "-"; - case BinOpTypeMult: return "*"; - case BinOpTypeDiv: return "/"; - case BinOpTypeMod: return "%"; - case BinOpTypeAssign: return "="; - case BinOpTypeAssignTimes: return "*="; - case BinOpTypeAssignDiv: return "/="; - case BinOpTypeAssignMod: return "%="; - case BinOpTypeAssignPlus: return "+="; - case BinOpTypeAssignMinus: return "-="; - case BinOpTypeAssignBitShiftLeft: return "<<="; - case BinOpTypeAssignBitShiftRight: return ">>="; - case BinOpTypeAssignBitAnd: return "&="; - case BinOpTypeAssignBitXor: return "^="; - case BinOpTypeAssignBitOr: return "|="; - case BinOpTypeAssignBoolAnd: return "&&="; - case BinOpTypeAssignBoolOr: return "||="; - case BinOpTypeUnwrapMaybe: return "??"; - case BinOpTypeArrayCat: return "++"; - case BinOpTypeArrayMult: return "**"; + case BinOpTypeInvalid: return "(invalid)"; + case BinOpTypeBoolOr: return "||"; + case BinOpTypeBoolAnd: return "&&"; + case BinOpTypeCmpEq: return "=="; + case BinOpTypeCmpNotEq: return "!="; + case BinOpTypeCmpLessThan: return "<"; + case BinOpTypeCmpGreaterThan: return ">"; + case BinOpTypeCmpLessOrEq: return "<="; + case BinOpTypeCmpGreaterOrEq: return ">="; + case BinOpTypeBinOr: return "|"; + case BinOpTypeBinXor: return "^"; + case BinOpTypeBinAnd: return "&"; + case BinOpTypeBitShiftLeft: return "<<"; + case BinOpTypeBitShiftLeftWrap: return "<<%"; + case BinOpTypeBitShiftRight: return ">>"; + case BinOpTypeAdd: return "+"; + case BinOpTypeAddWrap: return "+%"; + case BinOpTypeSub: return "-"; + case BinOpTypeSubWrap: return "-%"; + case BinOpTypeMult: return "*"; + case BinOpTypeMultWrap: return "*%"; + case BinOpTypeDiv: return "/"; + case BinOpTypeMod: return "%"; + case BinOpTypeAssign: return "="; + case BinOpTypeAssignTimes: return "*="; + case BinOpTypeAssignTimesWrap: return "*%="; + case BinOpTypeAssignDiv: return "/="; + case BinOpTypeAssignMod: return "%="; + case BinOpTypeAssignPlus: return "+="; + case BinOpTypeAssignPlusWrap: return "+%="; + case BinOpTypeAssignMinus: return "-="; + case BinOpTypeAssignMinusWrap: return "-%="; + case BinOpTypeAssignBitShiftLeft: return "<<="; + case BinOpTypeAssignBitShiftLeftWrap: return "<<%="; + case BinOpTypeAssignBitShiftRight: return ">>="; + case BinOpTypeAssignBitAnd: return "&="; + case BinOpTypeAssignBitXor: return "^="; + case BinOpTypeAssignBitOr: return "|="; + case BinOpTypeAssignBoolAnd: return "&&="; + case BinOpTypeAssignBoolOr: return "||="; + case BinOpTypeUnwrapMaybe: return "??"; + case BinOpTypeArrayCat: return "++"; + case BinOpTypeArrayMult: return "**"; } zig_unreachable(); } @@ -47,6 +55,7 @@ static const char *prefix_op_str(PrefixOp prefix_op) { switch (prefix_op) { case PrefixOpInvalid: return "(invalid)"; case PrefixOpNegation: return "-"; + case PrefixOpNegationWrap: return "-%"; case PrefixOpBoolNot: return "!"; case PrefixOpBinNot: return "~"; case PrefixOpAddressOf: return "&"; diff --git a/src/codegen.cpp b/src/codegen.cpp index 59ce94e02e1a108cf5bf173630db01ae6077a7e8..718e909b06c9f6dbfb3da769cb6d1e789424579d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1564,17 +1564,20 @@ static LLVMValueRef gen_prefix_op_expr(CodeGen *g, AstNode *node) { AstNode *expr_node = node->data.prefix_op_expr.primary_expr; TypeTableEntry *expr_type = get_expr_type(expr_node); - switch (node->data.prefix_op_expr.prefix_op) { + PrefixOp op = node->data.prefix_op_expr.prefix_op; + + switch (op) { case PrefixOpInvalid: zig_unreachable(); case PrefixOpNegation: + case PrefixOpNegationWrap: { LLVMValueRef expr = gen_expr(g, expr_node); set_debug_source_node(g, node); if (expr_type->id == TypeTableEntryIdFloat) { return LLVMBuildFNeg(g->builder, expr, ""); } else if (expr_type->id == TypeTableEntryIdInt) { - if (expr_type->data.integral.is_wrapping) { + if (op == PrefixOpNegationWrap) { return LLVMBuildNeg(g->builder, expr, ""); } else if (want_debug_safety(g, expr_node)) { LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(expr)); @@ -1796,17 +1799,23 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, set_debug_source_node(g, source_node); return LLVMBuildAnd(g->builder, val1, val2, ""); case BinOpTypeBitShiftLeft: + case BinOpTypeBitShiftLeftWrap: case BinOpTypeAssignBitShiftLeft: - set_debug_source_node(g, source_node); - assert(op1_type->id == TypeTableEntryIdInt); - if (op1_type->data.integral.is_wrapping) { - return LLVMBuildShl(g->builder, val1, val2, ""); - } else if (want_debug_safety(g, source_node)) { - return gen_overflow_shl_op(g, op1_type, val1, val2); - } else if (op1_type->data.integral.is_signed) { - return ZigLLVMBuildNSWShl(g->builder, val1, val2, ""); - } else { - return ZigLLVMBuildNUWShl(g->builder, val1, val2, ""); + case BinOpTypeAssignBitShiftLeftWrap: + { + set_debug_source_node(g, source_node); + assert(op1_type->id == TypeTableEntryIdInt); + bool is_wrapping = (bin_op == BinOpTypeBitShiftLeftWrap) || + (bin_op == BinOpTypeAssignBitShiftLeftWrap); + if (is_wrapping) { + return LLVMBuildShl(g->builder, val1, val2, ""); + } else if (want_debug_safety(g, source_node)) { + return gen_overflow_shl_op(g, op1_type, val1, val2); + } else if (op1_type->data.integral.is_signed) { + return ZigLLVMBuildNSWShl(g->builder, val1, val2, ""); + } else { + return ZigLLVMBuildNUWShl(g->builder, val1, val2, ""); + } } case BinOpTypeBitShiftRight: case BinOpTypeAssignBitShiftRight: @@ -1820,12 +1829,15 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, return LLVMBuildLShr(g->builder, val1, val2, ""); } case BinOpTypeAdd: + case BinOpTypeAddWrap: case BinOpTypeAssignPlus: + case BinOpTypeAssignPlusWrap: set_debug_source_node(g, source_node); if (op1_type->id == TypeTableEntryIdFloat) { return LLVMBuildFAdd(g->builder, val1, val2, ""); } else if (op1_type->id == TypeTableEntryIdInt) { - if (op1_type->data.integral.is_wrapping) { + bool is_wrapping = (bin_op == BinOpTypeAddWrap) || (bin_op == BinOpTypeAssignPlusWrap); + if (is_wrapping) { return LLVMBuildAdd(g->builder, val1, val2, ""); } else if (want_debug_safety(g, source_node)) { return gen_overflow_op(g, op1_type, AddSubMulAdd, val1, val2); @@ -1838,12 +1850,15 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, zig_unreachable(); } case BinOpTypeSub: + case BinOpTypeSubWrap: case BinOpTypeAssignMinus: + case BinOpTypeAssignMinusWrap: set_debug_source_node(g, source_node); if (op1_type->id == TypeTableEntryIdFloat) { return LLVMBuildFSub(g->builder, val1, val2, ""); } else if (op1_type->id == TypeTableEntryIdInt) { - if (op1_type->data.integral.is_wrapping) { + bool is_wrapping = (bin_op == BinOpTypeSubWrap || bin_op == BinOpTypeAssignMinusWrap); + if (is_wrapping) { return LLVMBuildSub(g->builder, val1, val2, ""); } else if (want_debug_safety(g, source_node)) { return gen_overflow_op(g, op1_type, AddSubMulSub, val1, val2); @@ -1856,12 +1871,15 @@ static LLVMValueRef gen_arithmetic_bin_op(CodeGen *g, AstNode *source_node, zig_unreachable(); } case BinOpTypeMult: + case BinOpTypeMultWrap: case BinOpTypeAssignTimes: + case BinOpTypeAssignTimesWrap: set_debug_source_node(g, source_node); if (op1_type->id == TypeTableEntryIdFloat) { return LLVMBuildFMul(g->builder, val1, val2, ""); } else if (op1_type->id == TypeTableEntryIdInt) { - if (op1_type->data.integral.is_wrapping) { + bool is_wrapping = (bin_op == BinOpTypeMultWrap || bin_op == BinOpTypeAssignTimesWrap); + if (is_wrapping) { return LLVMBuildMul(g->builder, val1, val2, ""); } else if (want_debug_safety(g, source_node)) { return gen_overflow_op(g, op1_type, AddSubMulMul, val1, val2); @@ -2214,11 +2232,15 @@ static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { zig_unreachable(); case BinOpTypeAssign: case BinOpTypeAssignTimes: + case BinOpTypeAssignTimesWrap: case BinOpTypeAssignDiv: case BinOpTypeAssignMod: case BinOpTypeAssignPlus: + case BinOpTypeAssignPlusWrap: case BinOpTypeAssignMinus: + case BinOpTypeAssignMinusWrap: case BinOpTypeAssignBitShiftLeft: + case BinOpTypeAssignBitShiftLeftWrap: case BinOpTypeAssignBitShiftRight: case BinOpTypeAssignBitAnd: case BinOpTypeAssignBitXor: @@ -2243,10 +2265,14 @@ static LLVMValueRef gen_bin_op_expr(CodeGen *g, AstNode *node) { case BinOpTypeBinXor: case BinOpTypeBinAnd: case BinOpTypeBitShiftLeft: + case BinOpTypeBitShiftLeftWrap: case BinOpTypeBitShiftRight: case BinOpTypeAdd: + case BinOpTypeAddWrap: case BinOpTypeSub: + case BinOpTypeSubWrap: case BinOpTypeMult: + case BinOpTypeMultWrap: case BinOpTypeDiv: case BinOpTypeMod: return gen_arithmetic_bin_op_expr(g, node); @@ -4188,17 +4214,7 @@ static const CIntTypeInfo c_int_type_infos[] = { {CIntTypeULongLong, "c_ulonglong", false}, }; -struct SignWrap { - bool is_signed; - bool is_wrapping; -}; - -static const SignWrap sign_wrap_list[] = { - {false, false}, - {false, true}, - {true, false}, - {true, true}, -}; +static const bool is_signed_list[] = { false, true, }; static void define_builtin_types(CodeGen *g) { { @@ -4238,18 +4254,16 @@ static void define_builtin_types(CodeGen *g) { for (int int_size_i = 0; int_size_i < array_length(int_sizes_in_bits); int_size_i += 1) { int size_in_bits = int_sizes_in_bits[int_size_i]; - for (int sign_wrap_i = 0; sign_wrap_i < array_length(sign_wrap_list); sign_wrap_i += 1) { - bool is_signed = sign_wrap_list[sign_wrap_i].is_signed; - bool is_wrapping = sign_wrap_list[sign_wrap_i].is_wrapping; + for (int is_sign_i = 0; is_sign_i < array_length(is_signed_list); is_sign_i += 1) { + bool is_signed = is_signed_list[is_sign_i]; TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); entry->type_ref = LLVMIntType(size_in_bits); entry->deep_const = true; const char u_or_i = is_signed ? 'i' : 'u'; - const char *w_or_none = is_wrapping ? "w" : ""; buf_resize(&entry->name, 0); - buf_appendf(&entry->name, "%c%d%s", u_or_i, size_in_bits, w_or_none); + buf_appendf(&entry->name, "%c%d", u_or_i, size_in_bits); unsigned dwarf_tag; if (is_signed) { @@ -4271,11 +4285,10 @@ static void define_builtin_types(CodeGen *g) { entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), debug_size_in_bits, debug_align_in_bits, dwarf_tag); entry->data.integral.is_signed = is_signed; - entry->data.integral.is_wrapping = is_wrapping; entry->data.integral.bit_count = size_in_bits; g->primitive_type_table.put(&entry->name, entry); - get_int_type_ptr(g, is_signed, is_wrapping, size_in_bits)[0] = entry; + get_int_type_ptr(g, is_signed, size_in_bits)[0] = entry; } } @@ -4297,7 +4310,6 @@ static void define_builtin_types(CodeGen *g) { debug_align_in_bits, is_signed ? LLVMZigEncoding_DW_ATE_signed() : LLVMZigEncoding_DW_ATE_unsigned()); entry->data.integral.is_signed = is_signed; - entry->data.integral.is_wrapping = !is_signed; entry->data.integral.bit_count = size_in_bits; g->primitive_type_table.put(&entry->name, entry); @@ -4319,21 +4331,18 @@ static void define_builtin_types(CodeGen *g) { g->primitive_type_table.put(&entry->name, entry); } - for (int sign_wrap_i = 0; sign_wrap_i < array_length(sign_wrap_list); sign_wrap_i += 1) { - bool is_signed = sign_wrap_list[sign_wrap_i].is_signed; - bool is_wrapping = sign_wrap_list[sign_wrap_i].is_wrapping; + for (int sign_i = 0; sign_i < array_length(is_signed_list); sign_i += 1) { + bool is_signed = is_signed_list[sign_i]; TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt); entry->deep_const = true; entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8); const char u_or_i = is_signed ? 'i' : 'u'; - const char *w_or_none = is_wrapping ? "w" : ""; buf_resize(&entry->name, 0); - buf_appendf(&entry->name, "%csize%s", u_or_i, w_or_none); + buf_appendf(&entry->name, "%csize", u_or_i); entry->data.integral.is_signed = is_signed; - entry->data.integral.is_wrapping = is_wrapping; entry->data.integral.bit_count = g->pointer_size_bytes * 8; uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->type_ref); @@ -4344,9 +4353,9 @@ static void define_builtin_types(CodeGen *g) { is_signed ? LLVMZigEncoding_DW_ATE_signed() : LLVMZigEncoding_DW_ATE_unsigned()); g->primitive_type_table.put(&entry->name, entry); - if (is_signed && !is_wrapping) { + if (is_signed) { g->builtin_types.entry_isize = entry; - } else if (!is_signed && !is_wrapping) { + } else { g->builtin_types.entry_usize = entry; } } @@ -4430,14 +4439,14 @@ static void define_builtin_types(CodeGen *g) { g->primitive_type_table.put(&entry->name, entry); } - g->builtin_types.entry_u8 = get_int_type(g, false, false, 8); - g->builtin_types.entry_u16 = get_int_type(g, false, false, 16); - g->builtin_types.entry_u32 = get_int_type(g, false, false, 32); - g->builtin_types.entry_u64 = get_int_type(g, false, false, 64); - g->builtin_types.entry_i8 = get_int_type(g, true, false, 8); - g->builtin_types.entry_i16 = get_int_type(g, true, false, 16); - g->builtin_types.entry_i32 = get_int_type(g, true, false, 32); - g->builtin_types.entry_i64 = get_int_type(g, true, false, 64); + g->builtin_types.entry_u8 = get_int_type(g, false, 8); + g->builtin_types.entry_u16 = get_int_type(g, false, 16); + g->builtin_types.entry_u32 = get_int_type(g, false, 32); + g->builtin_types.entry_u64 = get_int_type(g, false, 64); + g->builtin_types.entry_i8 = get_int_type(g, true, 8); + g->builtin_types.entry_i16 = get_int_type(g, true, 16); + g->builtin_types.entry_i32 = get_int_type(g, true, 32); + g->builtin_types.entry_i64 = get_int_type(g, true, 64); { g->builtin_types.entry_c_void = get_typedecl_type(g, "c_void", g->builtin_types.entry_u8); @@ -4703,7 +4712,7 @@ static void define_builtin_fns(CodeGen *g) { create_builtin_fn_with_arg_count(g, BuiltinFnIdDivExact, "div_exact", 2); create_builtin_fn_with_arg_count(g, BuiltinFnIdTruncate, "truncate", 2); create_builtin_fn_with_arg_count(g, BuiltinFnIdCompileErr, "compile_err", 1); - create_builtin_fn_with_arg_count(g, BuiltinFnIdIntType, "int_type", 3); + create_builtin_fn_with_arg_count(g, BuiltinFnIdIntType, "int_type", 2); } static void init(CodeGen *g, Buf *source_path) { diff --git a/src/eval.cpp b/src/eval.cpp index e8ebc110accde72a8ed0a41774c4317e65e18282..a8cecc47b17395f6ea6615d63953b8a997d63085 100644 --- a/src/eval.cpp +++ b/src/eval.cpp @@ -146,7 +146,7 @@ static int64_t min_signed_val(TypeTableEntry *type_entry) { static int eval_const_expr_bin_op_bignum(ConstExprValue *op1_val, ConstExprValue *op2_val, ConstExprValue *out_val, bool (*bignum_fn)(BigNum *, BigNum *, BigNum *), - TypeTableEntry *type) + TypeTableEntry *type, bool wrapping_op) { bool overflow = bignum_fn(&out_val->data.x_bignum, &op1_val->data.x_bignum, &op2_val->data.x_bignum); if (overflow) { @@ -156,7 +156,7 @@ static int eval_const_expr_bin_op_bignum(ConstExprValue *op1_val, ConstExprValue if (type->id == TypeTableEntryIdInt && !bignum_fits_in_bits(&out_val->data.x_bignum, type->data.integral.bit_count, type->data.integral.is_signed)) { - if (type->data.integral.is_wrapping) { + if (wrapping_op) { if (type->data.integral.is_signed) { out_val->data.x_bignum.data.x_uint = max_unsigned_val(type) - out_val->data.x_bignum.data.x_uint + 1; out_val->data.x_bignum.is_negative = !out_val->data.x_bignum.is_negative; @@ -187,11 +187,15 @@ int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, switch (bin_op) { case BinOpTypeAssign: case BinOpTypeAssignTimes: + case BinOpTypeAssignTimesWrap: case BinOpTypeAssignDiv: case BinOpTypeAssignMod: case BinOpTypeAssignPlus: + case BinOpTypeAssignPlusWrap: case BinOpTypeAssignMinus: + case BinOpTypeAssignMinusWrap: case BinOpTypeAssignBitShiftLeft: + case BinOpTypeAssignBitShiftLeftWrap: case BinOpTypeAssignBitShiftRight: case BinOpTypeAssignBitAnd: case BinOpTypeAssignBitXor: @@ -256,21 +260,29 @@ int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, return 0; } case BinOpTypeAdd: - return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_add, op1_type); + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_add, op1_type, false); + case BinOpTypeAddWrap: + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_add, op1_type, true); case BinOpTypeBinOr: - return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_or, op1_type); + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_or, op1_type, false); case BinOpTypeBinXor: - return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_xor, op1_type); + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_xor, op1_type, false); case BinOpTypeBinAnd: - return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_and, op1_type); + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_and, op1_type, false); case BinOpTypeBitShiftLeft: - return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_shl, op1_type); + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_shl, op1_type, false); + case BinOpTypeBitShiftLeftWrap: + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_shl, op1_type, true); case BinOpTypeBitShiftRight: - return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_shr, op1_type); + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_shr, op1_type, false); case BinOpTypeSub: - return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_sub, op1_type); + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_sub, op1_type, false); + case BinOpTypeSubWrap: + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_sub, op1_type, true); case BinOpTypeMult: - return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_mul, op1_type); + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_mul, op1_type, false); + case BinOpTypeMultWrap: + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_mul, op1_type, true); case BinOpTypeDiv: { bool is_int = false; @@ -289,11 +301,11 @@ int eval_const_expr_bin_op(ConstExprValue *op1_val, TypeTableEntry *op1_type, { return ErrorDivByZero; } else { - return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_div, op1_type); + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_div, op1_type, false); } } case BinOpTypeMod: - return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_mod, op1_type); + return eval_const_expr_bin_op_bignum(op1_val, op2_val, out_val, bignum_mod, op1_type, false); case BinOpTypeUnwrapMaybe: zig_panic("TODO"); case BinOpTypeArrayCat: @@ -314,11 +326,15 @@ static bool eval_bin_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) switch (bin_op) { case BinOpTypeAssign: case BinOpTypeAssignTimes: + case BinOpTypeAssignTimesWrap: case BinOpTypeAssignDiv: case BinOpTypeAssignMod: case BinOpTypeAssignPlus: + case BinOpTypeAssignPlusWrap: case BinOpTypeAssignMinus: + case BinOpTypeAssignMinusWrap: case BinOpTypeAssignBitShiftLeft: + case BinOpTypeAssignBitShiftLeftWrap: case BinOpTypeAssignBitShiftRight: case BinOpTypeAssignBitAnd: case BinOpTypeAssignBitXor: @@ -338,10 +354,14 @@ static bool eval_bin_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_val) case BinOpTypeBinXor: case BinOpTypeBinAnd: case BinOpTypeBitShiftLeft: + case BinOpTypeBitShiftLeftWrap: case BinOpTypeBitShiftRight: case BinOpTypeAdd: + case BinOpTypeAddWrap: case BinOpTypeSub: + case BinOpTypeSubWrap: case BinOpTypeMult: + case BinOpTypeMultWrap: case BinOpTypeDiv: case BinOpTypeMod: case BinOpTypeUnwrapMaybe: @@ -1098,13 +1118,14 @@ static bool eval_prefix_op_expr(EvalFn *ef, AstNode *node, ConstExprValue *out_v break; } case PrefixOpNegation: + case PrefixOpNegationWrap: if (expr_type->id == TypeTableEntryIdInt) { assert(expr_type->data.integral.is_signed); bignum_negate(&out_val->data.x_bignum, &expr_val.data.x_bignum); out_val->ok = true; bool overflow = !bignum_fits_in_bits(&out_val->data.x_bignum, expr_type->data.integral.bit_count, expr_type->data.integral.is_signed); - if (expr_type->data.integral.is_wrapping) { + if (prefix_op == PrefixOpNegationWrap) { if (overflow) { out_val->data.x_bignum.is_negative = true; } diff --git a/src/parser.cpp b/src/parser.cpp index 6ff43217c0178c6d9564ebdd7a256ef9420f210b..fa77bd6dc33e1eedca0f81a7f43293bcb1de2c53 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1384,6 +1384,7 @@ static PrefixOp tok_to_prefix_op(Token *token) { switch (token->id) { case TokenIdBang: return PrefixOpBoolNot; case TokenIdDash: return PrefixOpNegation; + case TokenIdMinusPercent: return PrefixOpNegationWrap; case TokenIdTilde: return PrefixOpBinNot; case TokenIdAmpersand: return PrefixOpAddressOf; case TokenIdStar: return PrefixOpDereference; @@ -1399,7 +1400,7 @@ static PrefixOp tok_to_prefix_op(Token *token) { /* PrefixOpExpression : PrefixOp PrefixOpExpression | SuffixOpExpression -PrefixOp : token(Not) | token(Dash) | token(Tilde) | token(Star) | (token(Ampersand) option(token(Const))) +PrefixOp = "!" | "-" | "~" | "*" | ("&" option("const")) | "?" | "%" | "%%" | "??" | "-%" */ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -1458,16 +1459,17 @@ static AstNode *ast_parse_prefix_op_expr(ParseContext *pc, int *token_index, boo static BinOpType tok_to_mult_op(Token *token) { switch (token->id) { - case TokenIdStar: return BinOpTypeMult; - case TokenIdStarStar: return BinOpTypeArrayMult; - case TokenIdSlash: return BinOpTypeDiv; - case TokenIdPercent: return BinOpTypeMod; - default: return BinOpTypeInvalid; + case TokenIdStar: return BinOpTypeMult; + case TokenIdTimesPercent: return BinOpTypeMultWrap; + case TokenIdStarStar: return BinOpTypeArrayMult; + case TokenIdSlash: return BinOpTypeDiv; + case TokenIdPercent: return BinOpTypeMod; + default: return BinOpTypeInvalid; } } /* -MultiplyOperator = "*" | "/" | "%" | "**" +MultiplyOperator = "*" | "/" | "%" | "**" | "*%" */ static BinOpType ast_parse_mult_op(ParseContext *pc, int *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -1511,15 +1513,17 @@ static AstNode *ast_parse_mult_expr(ParseContext *pc, int *token_index, bool man static BinOpType tok_to_add_op(Token *token) { switch (token->id) { - case TokenIdPlus: return BinOpTypeAdd; - case TokenIdDash: return BinOpTypeSub; - case TokenIdPlusPlus: return BinOpTypeArrayCat; - default: return BinOpTypeInvalid; + case TokenIdPlus: return BinOpTypeAdd; + case TokenIdPlusPercent: return BinOpTypeAddWrap; + case TokenIdDash: return BinOpTypeSub; + case TokenIdMinusPercent: return BinOpTypeSubWrap; + case TokenIdPlusPlus: return BinOpTypeArrayCat; + default: return BinOpTypeInvalid; } } /* -AdditionOperator : "+" | "-" | "++" +AdditionOperator = "+" | "-" | "++" | "+%" | "-%" */ static BinOpType ast_parse_add_op(ParseContext *pc, int *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -1563,14 +1567,15 @@ static AstNode *ast_parse_add_expr(ParseContext *pc, int *token_index, bool mand static BinOpType tok_to_bit_shift_op(Token *token) { switch (token->id) { - case TokenIdBitShiftLeft: return BinOpTypeBitShiftLeft; - case TokenIdBitShiftRight: return BinOpTypeBitShiftRight; + case TokenIdBitShiftLeft: return BinOpTypeBitShiftLeft; + case TokenIdBitShiftLeftPercent: return BinOpTypeBitShiftLeftWrap; + case TokenIdBitShiftRight: return BinOpTypeBitShiftRight; default: return BinOpTypeInvalid; } } /* -BitShiftOperator : token(BitShiftLeft) | token(BitShiftRight) +BitShiftOperator = "<<" | ">>" | "<<%" */ static BinOpType ast_parse_bit_shift_op(ParseContext *pc, int *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); @@ -2230,11 +2235,15 @@ static BinOpType tok_to_ass_op(Token *token) { switch (token->id) { case TokenIdEq: return BinOpTypeAssign; case TokenIdTimesEq: return BinOpTypeAssignTimes; + case TokenIdTimesPercentEq: return BinOpTypeAssignTimesWrap; case TokenIdDivEq: return BinOpTypeAssignDiv; case TokenIdModEq: return BinOpTypeAssignMod; case TokenIdPlusEq: return BinOpTypeAssignPlus; + case TokenIdPlusPercentEq: return BinOpTypeAssignPlusWrap; case TokenIdMinusEq: return BinOpTypeAssignMinus; + case TokenIdMinusPercentEq: return BinOpTypeAssignMinusWrap; case TokenIdBitShiftLeftEq: return BinOpTypeAssignBitShiftLeft; + case TokenIdBitShiftLeftPercentEq: return BinOpTypeAssignBitShiftLeftWrap; case TokenIdBitShiftRightEq: return BinOpTypeAssignBitShiftRight; case TokenIdBitAndEq: return BinOpTypeAssignBitAnd; case TokenIdBitXorEq: return BinOpTypeAssignBitXor; @@ -2246,7 +2255,7 @@ static BinOpType tok_to_ass_op(Token *token) { } /* -AssignmentOperator : token(Eq) | token(TimesEq) | token(DivEq) | token(ModEq) | token(PlusEq) | token(MinusEq) | token(BitShiftLeftEq) | token(BitShiftRightEq) | token(BitAndEq) | token(BitXorEq) | token(BitOrEq) | token(BoolAndEq) | token(BoolOrEq) +AssignmentOperator = "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" | "&&=" | "||=" | "*%=" | "+%=" | "-%=" | "<<%=" */ static BinOpType ast_parse_ass_op(ParseContext *pc, int *token_index, bool mandatory) { Token *token = &pc->tokens->at(*token_index); diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index a911640f03d14314b324e5d860e8f688f6c2a86e..99850c1414312f1bbb798ac7e8736a335a5620b8 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -154,10 +154,13 @@ enum TokenizeState { TokenizeStateCharLiteral, TokenizeStateCharLiteralEnd, TokenizeStateSawStar, + TokenizeStateSawStarPercent, TokenizeStateSawSlash, TokenizeStateSawPercent, TokenizeStateSawPlus, + TokenizeStateSawPlusPercent, TokenizeStateSawDash, + TokenizeStateSawMinusPercent, TokenizeStateSawAmpersand, TokenizeStateSawAmpersandAmpersand, TokenizeStateSawCaret, @@ -171,6 +174,7 @@ enum TokenizeState { TokenizeStateSawBang, TokenizeStateSawLessThan, TokenizeStateSawLessThanLessThan, + TokenizeStateSawShiftLeftPercent, TokenizeStateSawGreaterThan, TokenizeStateSawGreaterThanGreaterThan, TokenizeStateSawDot, @@ -596,6 +600,24 @@ void tokenize(Buf *buf, Tokenization *out) { end_token(&t); t.state = TokenizeStateStart; break; + case '%': + t.cur_tok->id = TokenIdBitShiftLeftPercent; + t.state = TokenizeStateSawShiftLeftPercent; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawShiftLeftPercent: + switch (c) { + case '=': + t.cur_tok->id = TokenIdBitShiftLeftPercentEq; + end_token(&t); + t.state = TokenizeStateStart; + break; default: t.pos -= 1; end_token(&t); @@ -648,6 +670,24 @@ void tokenize(Buf *buf, Tokenization *out) { end_token(&t); t.state = TokenizeStateStart; break; + case '%': + t.cur_tok->id = TokenIdTimesPercent; + t.state = TokenizeStateSawStarPercent; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawStarPercent: + switch (c) { + case '=': + t.cur_tok->id = TokenIdTimesPercentEq; + end_token(&t); + t.state = TokenizeStateStart; + break; default: t.pos -= 1; end_token(&t); @@ -691,6 +731,24 @@ void tokenize(Buf *buf, Tokenization *out) { end_token(&t); t.state = TokenizeStateStart; break; + case '%': + t.cur_tok->id = TokenIdPlusPercent; + t.state = TokenizeStateSawPlusPercent; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawPlusPercent: + switch (c) { + case '=': + t.cur_tok->id = TokenIdPlusPercentEq; + end_token(&t); + t.state = TokenizeStateStart; + break; default: t.pos -= 1; end_token(&t); @@ -1181,6 +1239,24 @@ void tokenize(Buf *buf, Tokenization *out) { end_token(&t); t.state = TokenizeStateStart; break; + case '%': + t.cur_tok->id = TokenIdMinusPercent; + t.state = TokenizeStateSawMinusPercent; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawMinusPercent: + switch (c) { + case '=': + t.cur_tok->id = TokenIdMinusPercentEq; + end_token(&t); + t.state = TokenizeStateStart; + break; default: t.pos -= 1; end_token(&t); @@ -1252,6 +1328,10 @@ void tokenize(Buf *buf, Tokenization *out) { case TokenizeStateSawDot: case TokenizeStateSawQuestionMark: case TokenizeStateSawAtSign: + case TokenizeStateSawStarPercent: + case TokenizeStateSawPlusPercent: + case TokenizeStateSawMinusPercent: + case TokenizeStateSawShiftLeftPercent: end_token(&t); break; case TokenizeStateSawDotDot: @@ -1372,6 +1452,14 @@ const char * token_name(TokenId id) { case TokenIdMaybeAssign: return "?="; case TokenIdAtSign: return "@"; case TokenIdPercentDot: return "%."; + case TokenIdTimesPercent: return "*%"; + case TokenIdTimesPercentEq: return "*%="; + case TokenIdPlusPercent: return "+%"; + case TokenIdPlusPercentEq: return "+%="; + case TokenIdMinusPercent: return "-%"; + case TokenIdMinusPercentEq: return "-%="; + case TokenIdBitShiftLeftPercent: return "<<%"; + case TokenIdBitShiftLeftPercentEq: return "<<%="; } return "(invalid token)"; } diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index db59f47305cf4e676fb359bbe0d584588c0e68e1..44ed8a14dd71a3fcb25b5e40b8bf1df3a05b1ac9 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -70,11 +70,19 @@ enum TokenId { TokenIdBinXor, TokenIdEq, TokenIdTimesEq, + TokenIdTimesPercent, + TokenIdTimesPercentEq, TokenIdDivEq, TokenIdModEq, TokenIdPlusEq, + TokenIdPlusPercent, + TokenIdPlusPercentEq, TokenIdMinusEq, + TokenIdMinusPercent, + TokenIdMinusPercentEq, TokenIdBitShiftLeftEq, + TokenIdBitShiftLeftPercent, + TokenIdBitShiftLeftPercentEq, TokenIdBitShiftRightEq, TokenIdBitAndEq, TokenIdBitXorEq, diff --git a/std/io.zig b/std/io.zig index 748ba5c91c7057826bb28a8c8856705d20adcd7d..9daa0fa6b42e1b3011fb5e707d7d237543e83f19 100644 --- a/std/io.zig +++ b/std/io.zig @@ -235,7 +235,7 @@ fn char_to_digit(c: u8, radix: u8) -> %u8 { } pub fn buf_print_signed(inline T: type, out_buf: []u8, x: T) -> usize { - const uint = @int_type(false, T.bit_count, false); + const uint = @int_type(false, T.bit_count); if (x < 0) { out_buf[0] = '-'; return 1 + buf_print_unsigned(uint, out_buf[1...], uint(-(x + 1)) + 1); diff --git a/std/rand.zig b/std/rand.zig index 848f5a98aebeb4cac62184040add6d88c362bf58..bbbe70f7b08dee551f3852aac51925f0633b28d0 100644 --- a/std/rand.zig +++ b/std/rand.zig @@ -77,7 +77,7 @@ pub struct Rand { /// Get a floating point value in the range 0.0..1.0. pub fn float(r: &Rand, inline T: type) -> T { - const int_type = @int_type(false, @sizeof(T) * 8, false); + const int_type = @int_type(false, @sizeof(T) * 8); // TODO switch statement for constant values const precision = if (T == f32) { 16777216 @@ -99,7 +99,6 @@ struct MersenneTwister( l: int, f: int) { const Self = MersenneTwister(int, n, m, r, a, u, d, s, b, t, c, l, f); - const intw = @int_type(int.is_signed, int.bit_count, true); array: [n]int, index: usize, @@ -113,7 +112,7 @@ struct MersenneTwister( var prev_value = seed; mt.array[0] = prev_value; {var i: usize = 1; while (i < n; i += 1) { - prev_value = intw(i) + intw(f) * intw(prev_value ^ (prev_value >> (int.bit_count - 2))); + prev_value = int(i) +% f *% (prev_value ^ (prev_value >> (int.bit_count - 2))); mt.array[i] = prev_value; }}; @@ -152,12 +151,12 @@ struct MersenneTwister( mt.index = 0; } - var x: intw = mt.array[mt.index]; + var x = mt.array[mt.index]; mt.index += 1; x ^= ((x >> u) & d); - x ^= ((x << s) & b); - x ^= ((x << t) & c); + x ^= ((x <<% s) & b); + x ^= ((x <<% t) & c); x ^= (x >> l); return x; diff --git a/test/run_tests.cpp b/test/run_tests.cpp index 1cb7fddc3bfd280fa9702f209694fad7595555ab..d03d8d0d5295e2b4b0c4c20acc1ea269dacbd144 100644 --- a/test/run_tests.cpp +++ b/test/run_tests.cpp @@ -1362,12 +1362,6 @@ fn mul(a: u16, b: u16) -> u16 { ".tmp_source.zig:3:18: note: called from here", ".tmp_source.zig:6:7: note: overflow occurred here"); - add_compile_fail_case("add incompatible int types", R"SOURCE( -fn add(x: i8w, y: i32) { - const z = x + y; -} - )SOURCE", 1, ".tmp_source.zig:3:17: error: incompatible types: 'i8w' and 'i32'"); - add_compile_fail_case("truncate sign mismatch", R"SOURCE( fn f() { const x: u32 = 10; diff --git a/test/self_hosted.zig b/test/self_hosted.zig index faeb3cdcebad1dae9ab737c98bf16d8f275a36f5..294887f00c37123963220363aa6c163e18857152 100644 --- a/test/self_hosted.zig +++ b/test/self_hosted.zig @@ -1458,17 +1458,17 @@ fn unsigned_wrapping() { test_unsigned_wrapping_eval(@max_value(u32)); test_unsigned_wrapping_noeval(@max_value(u32)); } -fn test_unsigned_wrapping_eval(x: u32w) { - const zero = x + 1; +fn test_unsigned_wrapping_eval(x: u32) { + const zero = x +% 1; assert(zero == 0); - const orig = zero - 1; + const orig = zero -% 1; assert(orig == @max_value(u32)); } #static_eval_enable(false) -fn test_unsigned_wrapping_noeval(x: u32w) { - const zero = x + 1; +fn test_unsigned_wrapping_noeval(x: u32) { + const zero = x +% 1; assert(zero == 0); - const orig = zero - 1; + const orig = zero -% 1; assert(orig == @max_value(u32)); } @@ -1477,17 +1477,17 @@ fn signed_wrapping() { test_signed_wrapping_eval(@max_value(i32)); test_signed_wrapping_noeval(@max_value(i32)); } -fn test_signed_wrapping_eval(x: i32w) { - const min_val = x + 1; +fn test_signed_wrapping_eval(x: i32) { + const min_val = x +% 1; assert(min_val == @min_value(i32)); - const max_val = min_val - 1; + const max_val = min_val -% 1; assert(max_val == @max_value(i32)); } #static_eval_enable(false) -fn test_signed_wrapping_noeval(x: i32w) { - const min_val = x + 1; +fn test_signed_wrapping_noeval(x: i32) { + const min_val = x +% 1; assert(min_val == @min_value(i32)); - const max_val = min_val - 1; + const max_val = min_val -% 1; assert(max_val == @max_value(i32)); } @@ -1496,15 +1496,15 @@ fn negation_wrapping() { test_negation_wrapping_eval(@min_value(i16)); test_negation_wrapping_noeval(@min_value(i16)); } -fn test_negation_wrapping_eval(x: i16w) { +fn test_negation_wrapping_eval(x: i16) { assert(x == -32768); - const neg = -x; + const neg = -%x; assert(neg == -32768); } #static_eval_enable(false) -fn test_negation_wrapping_noeval(x: i16w) { +fn test_negation_wrapping_noeval(x: i16) { assert(x == -32768); - const neg = -x; + const neg = -%x; assert(neg == -32768); } @@ -1513,13 +1513,13 @@ fn shl_wrapping() { test_shl_wrapping_eval(@max_value(u16)); test_shl_wrapping_noeval(@max_value(u16)); } -fn test_shl_wrapping_eval(x: u16w) { - const shifted = x << 1; +fn test_shl_wrapping_eval(x: u16) { + const shifted = x <<% 1; assert(shifted == 65534); } #static_eval_enable(false) -fn test_shl_wrapping_noeval(x: u16w) { - const shifted = x << 1; +fn test_shl_wrapping_noeval(x: u16) { + const shifted = x <<% 1; assert(shifted == 65534); } @@ -1531,23 +1531,6 @@ fn shl_with_overflow() { assert(result == 0b1011111111111100); } -#attribute("test") -fn combine_non_wrap_with_wrap() { - const x: i32 = 123; - const y: i32w = 456; - const z = x + y; - const z2 = y + x; - assert(@typeof(z) == i32w); - assert(@typeof(z2) == i32w); - - const a: i8 = 123; - const b: i32w = 456; - const c = b + a; - const d = a + b; - assert(@typeof(c) == i32w); - assert(@typeof(d) == i32w); -} - #attribute("test") fn c_string_concatenation() { const a = c"OK" ++ c" IT " ++ c"WORKED"; @@ -1687,41 +1670,21 @@ struct DivResult { #attribute("test") fn int_type_builtin() { - assert(@int_type(true, 8, false) == i8); - assert(@int_type(true, 16, false) == i16); - assert(@int_type(true, 32, false) == i32); - assert(@int_type(true, 64, false) == i64); + assert(@int_type(true, 8) == i8); + assert(@int_type(true, 16) == i16); + assert(@int_type(true, 32) == i32); + assert(@int_type(true, 64) == i64); - assert(@int_type(false, 8, false) == u8); - assert(@int_type(false, 16, false) == u16); - assert(@int_type(false, 32, false) == u32); - assert(@int_type(false, 64, false) == u64); - - assert(@int_type(true, 8, true) == i8w); - assert(@int_type(true, 16, true) == i16w); - assert(@int_type(true, 32, true) == i32w); - assert(@int_type(true, 64, true) == i64w); - - assert(@int_type(false, 8, true) == u8w); - assert(@int_type(false, 16, true) == u16w); - assert(@int_type(false, 32, true) == u32w); - assert(@int_type(false, 64, true) == u64w); + assert(@int_type(false, 8) == u8); + assert(@int_type(false, 16) == u16); + assert(@int_type(false, 32) == u32); + assert(@int_type(false, 64) == u64); assert(i8.bit_count == 8); assert(i16.bit_count == 16); assert(i32.bit_count == 32); assert(i64.bit_count == 64); - assert(!i8.is_wrapping); - assert(!i16.is_wrapping); - assert(!i32.is_wrapping); - assert(!i64.is_wrapping); - - assert(i8w.is_wrapping); - assert(i16w.is_wrapping); - assert(i32w.is_wrapping); - assert(i64w.is_wrapping); - assert(i8.is_signed); assert(i16.is_signed); assert(i32.is_signed);