explicit wrapping integer operations

instead of wrapping integer types

closes #159
This commit is contained in:
Andrew Kelley 2016-07-27 23:04:24 -07:00
parent 3eb5afd245
commit 8552d7fd19
15 changed files with 369 additions and 260 deletions

View File

@ -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.

View File

@ -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

View File

@ -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;

View File

@ -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) {

View File

@ -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);

View File

@ -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 "&";

View File

@ -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) {

View File

@ -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;
}

View File

@ -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);

View File

@ -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)";
}

View File

@ -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,

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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);