add number literal type

it gets implicitly casted to whatever is needed.

closes #24
This commit is contained in:
Andrew Kelley 2015-12-14 02:46:37 -07:00
parent 3d8eb10897
commit e411467e1d
17 changed files with 892 additions and 235 deletions

View File

@ -200,10 +200,11 @@ as
### Numbers
Number literals | Example | Exponentiation
-------------------------------------------
Decimal integer | 98222 | N/A
Hex integer | 0xff | N/A
Octal integer | 0o77 | N/A
Binary integer | 0b11110000 | N/A
Floating-point | 123.0E+77 | Optional
Number literals | Example | Exponentiation
--------------------------------------------------
Decimal integer | 98222 | N/A
Hex integer | 0xff | N/A
Octal integer | 0o77 | N/A
Binary integer | 0b11110000 | N/A
Floating-point | 123.0E+77 | Optional
Hex floating point | TODO | TODO

View File

@ -1,15 +1,11 @@
export executable "arrays";
#link("c")
extern {
fn puts(s: *const u8) -> i32;
fn exit(code: i32) -> unreachable;
}
use "std.zig";
export fn _start() -> unreachable {
export fn main(argc: isize, argv: *mut *mut u8, env: *mut *mut u8) -> i32 {
let mut array : [i32; 5];
let mut i = 0;
let mut i : i32 = 0;
loop_start:
if i == 5 {
goto loop_end;
@ -21,21 +17,22 @@ loop_start:
loop_end:
i = 0;
let mut accumulator = 0;
let mut accumulator : i32 = 0;
loop_2_start:
if i == 5 {
goto loop_2_end;
}
accumulator = accumulator + array[i];
accumulator += array[i];
i = i + 1;
goto loop_2_start;
loop_2_end:
if accumulator == 15 {
puts("OK");
print_str("OK" as string);
}
exit(0);
return 0;
}

View File

@ -14,19 +14,19 @@ fn other_exit() -> unreachable {
export fn _start() -> unreachable {
let a : i32 = 1;
let b = 2;
let b = 2 as i32;
// let c : i32; // not yet support for const variables
// let d; // parse error
if (a + b == 3) {
let no_conflict = 5;
if (no_conflict == 5) { puts("OK 1"); }
let no_conflict : i32 = 5;
if (no_conflict == 5) { puts(c"OK 1"); }
}
let c = {
let no_conflict = 10;
let no_conflict : i32 = 10;
no_conflict
};
if (c == 10) { puts("OK 2"); }
if (c == 10) { puts(c"OK 2"); }
void_fun(1, void, 2);
@ -44,12 +44,12 @@ fn void_fun(a : i32, b : void, c : i32) -> void {
}
fn test_mutable_vars() {
let mut i = 0;
let mut i : i32 = 0;
loop_start:
if i == 3 {
goto done;
}
puts("loop");
puts(c"loop");
i = i + 1;
goto loop_start;
done:

View File

@ -7,6 +7,6 @@ extern {
}
export fn _start() -> unreachable {
printf("Hello, world!\n");
printf(c"Hello, world!\n");
exit(0);
}

View File

@ -3,7 +3,7 @@ use "libc.zig";
// purposefully conflicting function with main.zig
// but it's private so it should be OK
fn private_function() {
puts("it works!");
puts(c"it works!");
}
pub fn print_text() {

View File

@ -1,5 +1,5 @@
#link("c")
extern {
pub fn puts(s: *mut u8) -> i32;
pub fn puts(s: *const u8) -> i32;
pub fn exit(code: i32) -> unreachable;
}

View File

@ -22,6 +22,6 @@ struct Foo {
fn test_foo(foo : Foo) {
if foo.b {
print_str("OK" as string);
print_str("OK\n" as string);
}
}

View File

@ -122,7 +122,7 @@ TypeTableEntry *get_pointer_to_type(CodeGen *g, TypeTableEntry *child_type, bool
}
}
static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, int array_size) {
static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, uint64_t array_size) {
auto existing_entry = child_type->arrays_by_size.maybe_get(array_size);
if (existing_entry) {
return existing_entry->value;
@ -130,7 +130,7 @@ static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, in
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArray);
entry->type_ref = LLVMArrayType(child_type->type_ref, array_size);
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "[%s; %d]", buf_ptr(&child_type->name), array_size);
buf_appendf(&entry->name, "[%s; %" PRIu64 "]", buf_ptr(&child_type->name), array_size);
entry->size_in_bits = child_type->size_in_bits * array_size;
entry->align_in_bits = child_type->align_in_bits;
@ -145,11 +145,6 @@ static TypeTableEntry *get_array_type(CodeGen *g, TypeTableEntry *child_type, in
}
}
static int parse_int(Buf *number) {
// TODO: think about integer size of array sizes
return atoi(buf_ptr(number));
}
static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {
assert(node->type == NodeTypeType);
alloc_codegen_node(node);
@ -192,16 +187,15 @@ static TypeTableEntry *resolve_type(CodeGen *g, AstNode *node) {
}
AstNode *size_node = node->data.type.array_size;
int size; // TODO: think about integer size of array sizes
if (size_node->type != NodeTypeNumberLiteral) {
add_node_error(g, size_node,
buf_create_from_str("array size must be literal number"));
size = -1;
if (size_node->type == NodeTypeNumberLiteral &&
is_num_lit_unsigned(size_node->data.number_literal.kind))
{
type_node->entry = get_array_type(g, child_type, size_node->data.number_literal.data.x_uint);
} else {
size = parse_int(&size_node->data.number);
add_node_error(g, size_node,
buf_create_from_str("array size must be literal unsigned integer"));
type_node->entry = g->builtin_types.entry_invalid;
}
type_node->entry = get_array_type(g, child_type, size);
return type_node->entry;
}
}
@ -625,6 +619,44 @@ static void get_struct_field(TypeTableEntry *struct_type, Buf *name, TypeStructF
*out_i = -1;
}
static bool num_lit_fits_in_other_type(CodeGen *g, TypeTableEntry *literal_type, TypeTableEntry *other_type) {
NumLit num_lit = literal_type->data.num_lit.kind;
uint64_t lit_size_in_bits = num_lit_bit_count(num_lit);
switch (other_type->id) {
case TypeTableEntryIdInvalid:
case TypeTableEntryIdNumberLiteral:
zig_unreachable();
case TypeTableEntryIdVoid:
case TypeTableEntryIdBool:
case TypeTableEntryIdUnreachable:
case TypeTableEntryIdPointer:
case TypeTableEntryIdArray:
case TypeTableEntryIdStruct:
return false;
case TypeTableEntryIdInt:
if (is_num_lit_signed(num_lit)) {
if (!other_type->data.integral.is_signed) {
return false;
}
return lit_size_in_bits <= other_type->size_in_bits;
} else if (is_num_lit_unsigned(num_lit)) {
return lit_size_in_bits <= other_type->size_in_bits;
} else {
return false;
}
case TypeTableEntryIdFloat:
if (is_num_lit_float(num_lit)) {
return lit_size_in_bits <= other_type->size_in_bits;
} else {
return false;
}
}
}
static TypeTableEntry *analyze_field_access_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
AstNode *node)
{
@ -790,6 +822,16 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
cast_node->op = CastOpArrayToString;
context->cast_expr_alloca_list.append(node);
return wanted_type;
} else if (actual_type->id == TypeTableEntryIdNumberLiteral &&
num_lit_fits_in_other_type(g, actual_type, wanted_type))
{
AstNode *literal_node = node->data.cast_expr.expr;
assert(literal_node->codegen_node);
NumberLiteralNode *codegen_num_lit = &literal_node->codegen_node->data.num_lit_node;
assert(!codegen_num_lit->resolved_type);
codegen_num_lit->resolved_type = wanted_type;
cast_node->op = CastOpNothing;
return wanted_type;
} else {
add_node_error(g, node,
buf_sprintf("invalid cast from type '%s' to '%s'",
@ -799,6 +841,314 @@ static TypeTableEntry *analyze_cast_expr(CodeGen *g, ImportTableEntry *import, B
}
}
static TypeTableEntry * resolve_rhs_number_literal(CodeGen *g, AstNode *non_literal_node,
TypeTableEntry *non_literal_type, AstNode *literal_node, TypeTableEntry *literal_type)
{
assert(literal_node->codegen_node);
NumberLiteralNode *codegen_num_lit = &literal_node->codegen_node->data.num_lit_node;
if (num_lit_fits_in_other_type(g, literal_type, non_literal_type)) {
assert(!codegen_num_lit->resolved_type);
codegen_num_lit->resolved_type = non_literal_type;
return non_literal_type;
} else {
return nullptr;
}
}
static TypeTableEntry * resolve_number_literals(CodeGen *g, AstNode *node1, AstNode *node2) {
TypeTableEntry *type1 = node1->codegen_node->expr_node.type_entry;
TypeTableEntry *type2 = node2->codegen_node->expr_node.type_entry;
if (type1->id == TypeTableEntryIdNumberLiteral &&
type2->id == TypeTableEntryIdNumberLiteral)
{
assert(node1->codegen_node);
assert(node2->codegen_node);
NumberLiteralNode *codegen_num_lit_1 = &node1->codegen_node->data.num_lit_node;
NumberLiteralNode *codegen_num_lit_2 = &node2->codegen_node->data.num_lit_node;
assert(!codegen_num_lit_1->resolved_type);
assert(!codegen_num_lit_2->resolved_type);
if (is_num_lit_float(type1->data.num_lit.kind) &&
is_num_lit_float(type2->data.num_lit.kind))
{
codegen_num_lit_1->resolved_type = g->builtin_types.entry_f64;
codegen_num_lit_2->resolved_type = g->builtin_types.entry_f64;
return g->builtin_types.entry_f64;
} else if (is_num_lit_signed(type1->data.num_lit.kind) &&
is_num_lit_signed(type2->data.num_lit.kind))
{
codegen_num_lit_1->resolved_type = g->builtin_types.entry_i64;
codegen_num_lit_2->resolved_type = g->builtin_types.entry_i64;
return g->builtin_types.entry_i64;
} else if (is_num_lit_unsigned(type1->data.num_lit.kind) &&
is_num_lit_unsigned(type2->data.num_lit.kind))
{
codegen_num_lit_1->resolved_type = g->builtin_types.entry_u64;
codegen_num_lit_2->resolved_type = g->builtin_types.entry_u64;
return g->builtin_types.entry_u64;
} else {
return nullptr;
}
} else if (type1->id == TypeTableEntryIdNumberLiteral) {
return resolve_rhs_number_literal(g, node2, type2, node1, type1);
} else {
assert(type2->id == TypeTableEntryIdNumberLiteral);
return resolve_rhs_number_literal(g, node1, type1, node2, type2);
}
}
static TypeTableEntry *analyze_bin_op_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node)
{
switch (node->data.bin_op_expr.bin_op) {
case BinOpTypeAssign:
case BinOpTypeAssignTimes:
case BinOpTypeAssignDiv:
case BinOpTypeAssignMod:
case BinOpTypeAssignPlus:
case BinOpTypeAssignMinus:
case BinOpTypeAssignBitShiftLeft:
case BinOpTypeAssignBitShiftRight:
case BinOpTypeAssignBitAnd:
case BinOpTypeAssignBitXor:
case BinOpTypeAssignBitOr:
case BinOpTypeAssignBoolAnd:
case BinOpTypeAssignBoolOr:
{
AstNode *lhs_node = node->data.bin_op_expr.op1;
TypeTableEntry *expected_rhs_type = nullptr;
if (lhs_node->type == NodeTypeSymbol) {
Buf *name = &lhs_node->data.symbol;
LocalVariableTableEntry *var = find_local_variable(context, name);
if (var) {
if (var->is_const) {
add_node_error(g, lhs_node,
buf_sprintf("cannot assign to constant variable"));
} else {
if (!is_op_allowed(var->type, node->data.bin_op_expr.bin_op)) {
if (var->type->id != TypeTableEntryIdInvalid) {
add_node_error(g, lhs_node,
buf_sprintf("operator not allowed for type '%s'",
buf_ptr(&var->type->name)));
}
} else {
expected_rhs_type = var->type;
}
}
} else {
add_node_error(g, lhs_node,
buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name)));
}
} else if (lhs_node->type == NodeTypeArrayAccessExpr) {
expected_rhs_type = analyze_array_access_expr(g, import, context, lhs_node);
} else if (lhs_node->type == NodeTypeFieldAccessExpr) {
alloc_codegen_node(lhs_node);
expected_rhs_type = analyze_field_access_expr(g, import, context, lhs_node);
} else {
add_node_error(g, lhs_node,
buf_sprintf("assignment target must be variable, field, or array element"));
}
analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2);
return g->builtin_types.entry_void;
}
case BinOpTypeBoolOr:
case BinOpTypeBoolAnd:
analyze_expression(g, import, context, g->builtin_types.entry_bool,
node->data.bin_op_expr.op1);
analyze_expression(g, import, context, g->builtin_types.entry_bool,
node->data.bin_op_expr.op2);
return g->builtin_types.entry_bool;
case BinOpTypeCmpEq:
case BinOpTypeCmpNotEq:
case BinOpTypeCmpLessThan:
case BinOpTypeCmpGreaterThan:
case BinOpTypeCmpLessOrEq:
case BinOpTypeCmpGreaterOrEq:
{
AstNode *op1 = node->data.bin_op_expr.op1;
AstNode *op2 = node->data.bin_op_expr.op2;
TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, op1);
TypeTableEntry *rhs_type = analyze_expression(g, import, context, nullptr, op2);
bool cmp_ok = false;
if (lhs_type->id == TypeTableEntryIdInvalid || rhs_type->id == TypeTableEntryIdInvalid) {
cmp_ok = true;
} else if (lhs_type->id == TypeTableEntryIdNumberLiteral ||
rhs_type->id == TypeTableEntryIdNumberLiteral)
{
cmp_ok = resolve_number_literals(g, op1, op2);
} else if (lhs_type->id == TypeTableEntryIdInt) {
if (rhs_type->id == TypeTableEntryIdInt &&
lhs_type->data.integral.is_signed == rhs_type->data.integral.is_signed &&
lhs_type->size_in_bits == rhs_type->size_in_bits)
{
cmp_ok = true;
}
} else if (lhs_type->id == TypeTableEntryIdFloat) {
if (rhs_type->id == TypeTableEntryIdFloat &&
lhs_type->size_in_bits == rhs_type->size_in_bits)
{
cmp_ok = true;
}
}
if (!cmp_ok) {
add_node_error(g, node, buf_sprintf("unable to compare '%s' with '%s'",
buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name)));
}
return g->builtin_types.entry_bool;
}
case BinOpTypeBinOr:
case BinOpTypeBinXor:
case BinOpTypeBinAnd:
{
// TODO: don't require i32
analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
return g->builtin_types.entry_i32;
}
case BinOpTypeBitShiftLeft:
case BinOpTypeBitShiftRight:
{
// TODO: don't require i32
analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
return g->builtin_types.entry_i32;
}
case BinOpTypeAdd:
case BinOpTypeSub:
{
AstNode *op1 = node->data.bin_op_expr.op1;
AstNode *op2 = node->data.bin_op_expr.op2;
TypeTableEntry *lhs_type = analyze_expression(g, import, context, nullptr, op1);
TypeTableEntry *rhs_type = analyze_expression(g, import, context, nullptr, op2);
TypeTableEntry *return_type = nullptr;
if (lhs_type->id == TypeTableEntryIdInvalid || rhs_type->id == TypeTableEntryIdInvalid) {
return_type = g->builtin_types.entry_invalid;
} else if (lhs_type->id == TypeTableEntryIdNumberLiteral ||
rhs_type->id == TypeTableEntryIdNumberLiteral)
{
return_type = resolve_number_literals(g, op1, op2);
} else if (lhs_type->id == TypeTableEntryIdInt &&
lhs_type == rhs_type)
{
return_type = lhs_type;
} else if (lhs_type->id == TypeTableEntryIdFloat &&
lhs_type == rhs_type)
{
return_type = lhs_type;
}
if (!return_type) {
if (node->data.bin_op_expr.bin_op == BinOpTypeAdd) {
add_node_error(g, node, buf_sprintf("unable to add '%s' and '%s'",
buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name)));
} else {
add_node_error(g, node, buf_sprintf("unable to subtract '%s' and '%s'",
buf_ptr(&lhs_type->name), buf_ptr(&rhs_type->name)));
}
return g->builtin_types.entry_invalid;
}
return return_type;
}
case BinOpTypeMult:
case BinOpTypeDiv:
case BinOpTypeMod:
{
// TODO: don't require i32
analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
return g->builtin_types.entry_i32;
}
case BinOpTypeInvalid:
zig_unreachable();
}
zig_unreachable();
}
static TypeTableEntry *analyze_variable_declaration(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node)
{
AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;
TypeTableEntry *explicit_type = nullptr;
if (variable_declaration->type != nullptr) {
explicit_type = resolve_type(g, variable_declaration->type);
if (explicit_type->id == TypeTableEntryIdUnreachable) {
add_node_error(g, variable_declaration->type,
buf_sprintf("variable of type 'unreachable' not allowed"));
explicit_type = g->builtin_types.entry_invalid;
}
}
TypeTableEntry *implicit_type = nullptr;
if (variable_declaration->expr != nullptr) {
implicit_type = analyze_expression(g, import, context, explicit_type, variable_declaration->expr);
if (implicit_type->id == TypeTableEntryIdUnreachable) {
add_node_error(g, node,
buf_sprintf("variable initialization is unreachable"));
implicit_type = g->builtin_types.entry_invalid;
} else if (implicit_type->id == TypeTableEntryIdNumberLiteral) {
add_node_error(g, node,
buf_sprintf("unable to infer variable type"));
implicit_type = g->builtin_types.entry_invalid;
}
}
if (implicit_type == nullptr && variable_declaration->is_const) {
add_node_error(g, node, buf_sprintf("variables must have initial values or be declared 'mut'."));
implicit_type = g->builtin_types.entry_invalid;
}
TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type;
assert(type != nullptr); // should have been caught by the parser
LocalVariableTableEntry *existing_variable = find_local_variable(context, &variable_declaration->symbol);
if (existing_variable) {
add_node_error(g, node,
buf_sprintf("redeclaration of variable '%s'", buf_ptr(&variable_declaration->symbol)));
} else {
LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1);
buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol);
variable_entry->type = type;
variable_entry->is_const = variable_declaration->is_const;
variable_entry->is_ptr = true;
variable_entry->decl_node = node;
context->variable_table.put(&variable_entry->name, variable_entry);
}
return g->builtin_types.entry_void;
}
static TypeTableEntry *analyze_number_literal_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node)
{
TypeTableEntry *num_lit_type = g->num_lit_types[node->data.number_literal.kind];
if (node->data.number_literal.overflow) {
add_node_error(g, node,
buf_sprintf("number literal too large to be represented in any type"));
return g->builtin_types.entry_invalid;
} else if (expected_type) {
if (expected_type->id == TypeTableEntryIdInvalid) {
return g->builtin_types.entry_invalid;
} else if (num_lit_fits_in_other_type(g, num_lit_type, expected_type)) {
NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node;
assert(!codegen_num_lit->resolved_type);
codegen_num_lit->resolved_type = expected_type;
return expected_type;
} else {
add_node_error(g, node, buf_sprintf("expected type '%s', got '%s'",
buf_ptr(&expected_type->name), buf_ptr(&num_lit_type->name)));
return g->builtin_types.entry_invalid;
}
} else {
return num_lit_type;
}
}
static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import, BlockContext *context,
TypeTableEntry *expected_type, AstNode *node)
{
@ -854,51 +1204,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
break;
}
case NodeTypeVariableDeclaration:
{
AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration;;
TypeTableEntry *explicit_type = nullptr;
if (variable_declaration->type != nullptr) {
explicit_type = resolve_type(g, variable_declaration->type);
if (explicit_type->id == TypeTableEntryIdUnreachable) {
add_node_error(g, variable_declaration->type,
buf_sprintf("variable of type 'unreachable' not allowed"));
}
}
TypeTableEntry *implicit_type = nullptr;
if (variable_declaration->expr != nullptr) {
implicit_type = analyze_expression(g, import, context, explicit_type, variable_declaration->expr);
if (implicit_type->id == TypeTableEntryIdUnreachable) {
add_node_error(g, node,
buf_sprintf("variable initialization is unreachable"));
}
}
if (implicit_type == nullptr && variable_declaration->is_const) {
add_node_error(g, node, buf_sprintf("variables must have initial values or be declared 'mut'."));
}
TypeTableEntry *type = explicit_type != nullptr ? explicit_type : implicit_type;
assert(type != nullptr); // should have been caught by the parser
LocalVariableTableEntry *existing_variable = find_local_variable(context, &variable_declaration->symbol);
if (existing_variable) {
add_node_error(g, node,
buf_sprintf("redeclaration of variable '%s'", buf_ptr(&variable_declaration->symbol)));
} else {
LocalVariableTableEntry *variable_entry = allocate<LocalVariableTableEntry>(1);
buf_init_from_buf(&variable_entry->name, &variable_declaration->symbol);
variable_entry->type = type;
variable_entry->is_const = variable_declaration->is_const;
variable_entry->is_ptr = true;
variable_entry->decl_node = node;
context->variable_table.put(&variable_entry->name, variable_entry);
}
return_type = g->builtin_types.entry_void;
break;
}
return_type = analyze_variable_declaration(g, import, context, expected_type, node);
break;
case NodeTypeGoto:
{
FnTableEntry *fn_table_entry = get_context_fn_entry(context);
@ -928,120 +1235,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
break;
}
case NodeTypeBinOpExpr:
{
switch (node->data.bin_op_expr.bin_op) {
case BinOpTypeAssign:
case BinOpTypeAssignTimes:
case BinOpTypeAssignDiv:
case BinOpTypeAssignMod:
case BinOpTypeAssignPlus:
case BinOpTypeAssignMinus:
case BinOpTypeAssignBitShiftLeft:
case BinOpTypeAssignBitShiftRight:
case BinOpTypeAssignBitAnd:
case BinOpTypeAssignBitXor:
case BinOpTypeAssignBitOr:
case BinOpTypeAssignBoolAnd:
case BinOpTypeAssignBoolOr:
{
AstNode *lhs_node = node->data.bin_op_expr.op1;
TypeTableEntry *expected_rhs_type = nullptr;
if (lhs_node->type == NodeTypeSymbol) {
Buf *name = &lhs_node->data.symbol;
LocalVariableTableEntry *var = find_local_variable(context, name);
if (var) {
if (var->is_const) {
add_node_error(g, lhs_node,
buf_sprintf("cannot assign to constant variable"));
} else {
if (!is_op_allowed(var->type, node->data.bin_op_expr.bin_op)) {
add_node_error(g, lhs_node,
buf_sprintf("operator not allowed for type '%s'", buf_ptr(&var->type->name)));
} else {
expected_rhs_type = var->type;
}
}
} else {
add_node_error(g, lhs_node,
buf_sprintf("use of undeclared identifier '%s'", buf_ptr(name)));
}
} else if (lhs_node->type == NodeTypeArrayAccessExpr) {
expected_rhs_type = analyze_array_access_expr(g, import, context, lhs_node);
} else if (lhs_node->type == NodeTypeFieldAccessExpr) {
alloc_codegen_node(lhs_node);
expected_rhs_type = analyze_field_access_expr(g, import, context, lhs_node);
} else {
add_node_error(g, lhs_node,
buf_sprintf("assignment target must be variable, field, or array element"));
}
analyze_expression(g, import, context, expected_rhs_type, node->data.bin_op_expr.op2);
return_type = g->builtin_types.entry_void;
break;
}
case BinOpTypeBoolOr:
case BinOpTypeBoolAnd:
analyze_expression(g, import, context, g->builtin_types.entry_bool,
node->data.bin_op_expr.op1);
analyze_expression(g, import, context, g->builtin_types.entry_bool,
node->data.bin_op_expr.op2);
return_type = g->builtin_types.entry_bool;
break;
case BinOpTypeCmpEq:
case BinOpTypeCmpNotEq:
case BinOpTypeCmpLessThan:
case BinOpTypeCmpGreaterThan:
case BinOpTypeCmpLessOrEq:
case BinOpTypeCmpGreaterOrEq:
// TODO think how should type checking for these work?
analyze_expression(g, import, context, g->builtin_types.entry_i32,
node->data.bin_op_expr.op1);
analyze_expression(g, import, context, g->builtin_types.entry_i32,
node->data.bin_op_expr.op2);
return_type = g->builtin_types.entry_bool;
break;
case BinOpTypeBinOr:
case BinOpTypeBinXor:
case BinOpTypeBinAnd:
{
// TODO: don't require i32
analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
return_type = g->builtin_types.entry_i32;
break;
}
case BinOpTypeBitShiftLeft:
case BinOpTypeBitShiftRight:
{
// TODO: don't require i32
analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
return_type = g->builtin_types.entry_i32;
break;
}
case BinOpTypeAdd:
case BinOpTypeSub:
// TODO think how should type checking for these work?
analyze_expression(g, import, context, g->builtin_types.entry_i32,
node->data.bin_op_expr.op1);
analyze_expression(g, import, context, g->builtin_types.entry_i32,
node->data.bin_op_expr.op2);
return_type = g->builtin_types.entry_i32;
break;
case BinOpTypeMult:
case BinOpTypeDiv:
case BinOpTypeMod:
{
// TODO: don't require i32
analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op1);
analyze_expression(g, import, context, g->builtin_types.entry_i32, node->data.bin_op_expr.op2);
return_type = g->builtin_types.entry_i32;
break;
}
case BinOpTypeInvalid:
zig_unreachable();
}
break;
}
return_type = analyze_bin_op_expr(g, import, context, expected_type, node);
break;
case NodeTypeFnCallExpr:
{
@ -1116,10 +1311,8 @@ static TypeTableEntry * analyze_expression(CodeGen *g, ImportTableEntry *import,
return_type = analyze_field_access_expr(g, import, context, node);
break;
case NodeTypeNumberLiteral:
// TODO: generic literal int type
return_type = g->builtin_types.entry_i32;
return_type = analyze_number_literal_expr(g, import, context, expected_type, node);
break;
case NodeTypeStringLiteral:
if (node->data.string_literal.c) {
return_type = g->builtin_types.entry_c_string_literal;

View File

@ -42,6 +42,10 @@ struct TypeTableEntryStruct {
TypeStructField *fields;
};
struct TypeTableEntryNumLit {
NumLit kind;
};
enum TypeTableEntryId {
TypeTableEntryIdInvalid,
TypeTableEntryIdVoid,
@ -52,6 +56,7 @@ enum TypeTableEntryId {
TypeTableEntryIdPointer,
TypeTableEntryIdArray,
TypeTableEntryIdStruct,
TypeTableEntryIdNumberLiteral,
};
struct TypeTableEntry {
@ -69,12 +74,13 @@ struct TypeTableEntry {
TypeTableEntryInt integral;
TypeTableEntryArray array;
TypeTableEntryStruct structure;
TypeTableEntryNumLit num_lit;
} data;
// use these fields to make sure we don't duplicate type table entries for the same type
TypeTableEntry *pointer_const_parent;
TypeTableEntry *pointer_mut_parent;
HashMap<int, TypeTableEntry *, int_hash, int_eq> arrays_by_size;
HashMap<uint64_t, TypeTableEntry *, uint64_hash, uint64_eq> arrays_by_size;
};
@ -134,10 +140,13 @@ struct CodeGen {
struct {
TypeTableEntry *entry_bool;
TypeTableEntry *entry_u8;
TypeTableEntry *entry_u64;
TypeTableEntry *entry_i32;
TypeTableEntry *entry_i64;
TypeTableEntry *entry_isize;
TypeTableEntry *entry_usize;
TypeTableEntry *entry_f32;
TypeTableEntry *entry_f64;
TypeTableEntry *entry_c_string_literal;
TypeTableEntry *entry_string;
TypeTableEntry *entry_void;
@ -145,6 +154,8 @@ struct CodeGen {
TypeTableEntry *entry_invalid;
} builtin_types;
TypeTableEntry *num_lit_types[NumLitCount];
LLVMTargetDataRef target_data_ref;
unsigned pointer_size_bytes;
bool is_static;
@ -242,6 +253,7 @@ enum CastOp {
CastOpPtrToInt,
CastOpIntWidenOrShorten,
CastOpArrayToString,
CastOpNothing,
};
struct CastNode {
@ -251,6 +263,10 @@ struct CastNode {
LLVMValueRef ptr;
};
struct NumberLiteralNode {
TypeTableEntry *resolved_type;
};
struct CodeGenNode {
union {
TypeNode type_node; // for NodeTypeType
@ -262,6 +278,7 @@ struct CodeGenNode {
StructDeclNode struct_decl_node; // for NodeTypeStructDecl
FieldAccessNode field_access_node; // for NodeTypeFieldAccessExpr
CastNode cast_node; // for NodeTypeCastExpr
NumberLiteralNode num_lit_node; // for NodeTypeNumberLiteral
} data;
ExprNode expr_node; // for all the expression nodes
};

View File

@ -279,6 +279,8 @@ static LLVMValueRef gen_cast_expr(CodeGen *g, AstNode *node) {
CastNode *cast_node = &node->codegen_node->data.cast_node;
switch (cast_node->op) {
case CastOpNothing:
return expr_val;
case CastOpPtrToInt:
return LLVMBuildPtrToInt(g->builder, expr_val, wanted_type->type_ref, "");
case CastOpIntWidenOrShorten:
@ -901,11 +903,29 @@ static LLVMValueRef gen_expr(CodeGen *g, AstNode *node) {
return gen_asm_expr(g, node);
case NodeTypeNumberLiteral:
{
Buf *number_str = &node->data.number;
LLVMTypeRef number_type = LLVMInt32Type();
LLVMValueRef number_val = LLVMConstIntOfStringAndSize(number_type,
buf_ptr(number_str), buf_len(number_str), 10);
return number_val;
NumberLiteralNode *codegen_num_lit = &node->codegen_node->data.num_lit_node;
assert(codegen_num_lit);
TypeTableEntry *type_entry = codegen_num_lit->resolved_type;
assert(type_entry);
// TODO this is kinda iffy. make sure josh is on board with this
node->codegen_node->expr_node.type_entry = type_entry;
if (type_entry->id == TypeTableEntryIdInt) {
// here the union has int64_t and uint64_t and we purposefully read
// the uint64_t value in either case, because we want the twos
// complement representation
return LLVMConstInt(type_entry->type_ref,
node->data.number_literal.data.x_uint,
type_entry->data.integral.is_signed);
} else if (type_entry->id == TypeTableEntryIdFloat) {
return LLVMConstReal(type_entry->type_ref,
node->data.number_literal.data.x_float);
} else {
zig_panic("bad number literal type");
}
}
case NodeTypeStringLiteral:
{
@ -1186,6 +1206,20 @@ static void do_code_gen(CodeGen *g) {
#endif
}
static const NumLit num_lit_kinds[] = {
NumLitF32,
NumLitF64,
NumLitF128,
NumLitI8,
NumLitU8,
NumLitI16,
NumLitU16,
NumLitI32,
NumLitU32,
NumLitI64,
NumLitU64,
};
static void define_builtin_types(CodeGen *g) {
{
// if this type is anywhere in the AST, we should never hit codegen.
@ -1193,6 +1227,19 @@ static void define_builtin_types(CodeGen *g) {
buf_init_from_str(&entry->name, "(invalid)");
g->builtin_types.entry_invalid = entry;
}
assert(NumLitCount == array_length(num_lit_kinds));
for (int i = 0; i < NumLitCount; i += 1) {
NumLit num_lit_kind = num_lit_kinds[i];
// This type should just create a constant with whatever actual number
// type is expected at the time.
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdNumberLiteral);
buf_resize(&entry->name, 0);
buf_appendf(&entry->name, "(%s literal)", num_lit_str(num_lit_kind));
entry->data.num_lit.kind = num_lit_kind;
g->num_lit_types[i] = entry;
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdBool);
entry->type_ref = LLVMInt1Type();
@ -1217,6 +1264,19 @@ static void define_builtin_types(CodeGen *g) {
g->type_table.put(&entry->name, entry);
g->builtin_types.entry_u8 = entry;
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMInt64Type();
buf_init_from_str(&entry->name, "u64");
entry->size_in_bits = 64;
entry->align_in_bits = 64;
entry->data.integral.is_signed = false;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_unsigned());
g->type_table.put(&entry->name, entry);
g->builtin_types.entry_u64 = entry;
}
g->builtin_types.entry_c_string_literal = get_pointer_to_type(g, g->builtin_types.entry_u8, true);
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
@ -1231,6 +1291,19 @@ static void define_builtin_types(CodeGen *g) {
g->type_table.put(&entry->name, entry);
g->builtin_types.entry_i32 = entry;
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMInt64Type();
buf_init_from_str(&entry->name, "i64");
entry->size_in_bits = 64;
entry->align_in_bits = 64;
entry->data.integral.is_signed = true;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_signed());
g->type_table.put(&entry->name, entry);
g->builtin_types.entry_i64 = entry;
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdInt);
entry->type_ref = LLVMIntType(g->pointer_size_bytes * 8);
@ -1269,6 +1342,18 @@ static void define_builtin_types(CodeGen *g) {
g->type_table.put(&entry->name, entry);
g->builtin_types.entry_f32 = entry;
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdFloat);
entry->type_ref = LLVMFloatType();
buf_init_from_str(&entry->name, "f64");
entry->size_in_bits = 64;
entry->align_in_bits = 64;
entry->di_type = LLVMZigCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name),
entry->size_in_bits, entry->align_in_bits,
LLVMZigEncoding_DW_ATE_float());
g->type_table.put(&entry->name, entry);
g->builtin_types.entry_f64 = entry;
}
{
TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVoid);
entry->type_ref = LLVMVoidType();

View File

@ -544,7 +544,7 @@ void parse_h_file(const char *target_path, ZigList<const char *> *clang_argv, FI
char *ZIG_PARSEH_CFLAGS = getenv("ZIG_PARSEH_CFLAGS");
if (ZIG_PARSEH_CFLAGS) {
Buf tmp_buf = {0};
Buf tmp_buf = BUF_INIT;
char *start = ZIG_PARSEH_CFLAGS;
char *space = strstr(start, " ");
while (space) {

View File

@ -12,6 +12,7 @@
#include <stdarg.h>
#include <stdio.h>
static const char *bin_op_str(BinOpType bin_op) {
switch (bin_op) {
case BinOpTypeInvalid: return "(invalid)";
@ -273,9 +274,19 @@ void ast_print(AstNode *node, int indent) {
ast_print(node->data.prefix_op_expr.primary_expr, indent + 2);
break;
case NodeTypeNumberLiteral:
fprintf(stderr, "NumberLiteral %s\n",
buf_ptr(&node->data.number));
break;
{
NumLit num_lit = node->data.number_literal.kind;
const char *name = node_type_str(node->type);
const char *kind_str = num_lit_str(num_lit);
if (is_num_lit_signed(num_lit)) {
fprintf(stderr, "%s %s %" PRId64 "\n", name, kind_str, node->data.number_literal.data.x_int);
} else if (is_num_lit_unsigned(num_lit)) {
fprintf(stderr, "%s %s %" PRIu64 "\n", name, kind_str, node->data.number_literal.data.x_uint);
} else {
fprintf(stderr, "%s %s %f\n", name, kind_str, node->data.number_literal.data.x_float);
}
break;
}
case NodeTypeStringLiteral:
{
const char *c = node->data.string_literal.c ? "c" : "";
@ -574,6 +585,186 @@ static void parse_string_literal(ParseContext *pc, Token *token, Buf *buf, bool
if (offset_map) offset_map->append(pos);
}
enum ParseNumLitState {
ParseNumLitStateStart,
ParseNumLitStateBase,
ParseNumLitStateDigits,
ParseNumLitStateExpectFirstDigit,
ParseNumLitStateDecimal,
ParseNumLitStateESign,
ParseNumLitStateEDigit,
};
static void parse_number_literal(ParseContext *pc, Token *token, AstNodeNumberLiteral *num_lit) {
ParseNumLitState state = ParseNumLitStateStart;
unsigned long long base = 10;
bool negative = false;
int digits_start;
int digits_end;
int decimal_start = -1;
int decimal_end;
bool e_present = false;
bool e_positive;
int e_digit_start;
int e_digit_end;
for (int i = token->start_pos; i < token->end_pos; i += 1) {
uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i);
switch (state) {
case ParseNumLitStateStart:
if (c == '-') {
negative = true;
} else if (c == '0') {
state = ParseNumLitStateBase;
} else if (c >= '1' && c <= '9') {
digits_start = i;
state = ParseNumLitStateDigits;
} else {
zig_unreachable();
}
break;
case ParseNumLitStateBase:
if (c == 'x') {
base = 16;
state = ParseNumLitStateExpectFirstDigit;
} else if (c == 'o') {
base = 8;
state = ParseNumLitStateExpectFirstDigit;
} else if (c == 'b') {
base = 2;
state = ParseNumLitStateExpectFirstDigit;
} else {
zig_unreachable();
}
break;
case ParseNumLitStateExpectFirstDigit:
state = ParseNumLitStateDigits;
break;
case ParseNumLitStateDigits:
if (c == '.') {
assert(base == 10);
digits_end = i;
decimal_start = i + 1;
state = ParseNumLitStateDecimal;
}
break;
case ParseNumLitStateDecimal:
if (c == 'E') {
e_present = false;
decimal_end = i;
state = ParseNumLitStateESign;
}
break;
case ParseNumLitStateESign:
if (c == '+') {
e_positive = true;
e_digit_start = i + 1;
state = ParseNumLitStateEDigit;
} else if (c == '-') {
e_positive = false;
e_digit_start = i + 1;
state = ParseNumLitStateEDigit;
} else {
zig_unreachable();
}
break;
case ParseNumLitStateEDigit:
assert(c >= '0' && c <= '9');
break;
}
}
switch (state) {
case ParseNumLitStateDigits:
digits_end = token->end_pos;
break;
case ParseNumLitStateDecimal:
decimal_end = token->end_pos;
break;
case ParseNumLitStateEDigit:
e_digit_end = token->end_pos;
break;
case ParseNumLitStateBase:
num_lit->kind = NumLitU8;
num_lit->data.x_uint = 0;
return;
case ParseNumLitStateESign:
case ParseNumLitStateExpectFirstDigit:
case ParseNumLitStateStart:
zig_unreachable();
}
if (decimal_start >= 0) {
// float
double x;
(void)x;
zig_panic("TODO parse float");
} else {
// integer
unsigned long long x = 0;
unsigned long long mult = 1;
for (int i = digits_end - 1; ; i -= 1) {
uint8_t c = *((uint8_t*)buf_ptr(pc->buf) + i);
unsigned long long digit = (c - '0');
// digit *= mult
if (__builtin_umulll_overflow(digit, mult, &digit)) {
num_lit->overflow = true;
return;
}
// x += digit
if (__builtin_uaddll_overflow(x, digit, &x)) {
num_lit->overflow = true;
return;
}
if (i == digits_start)
break;
// mult *= base
if (__builtin_umulll_overflow(mult, base, &mult)) {
num_lit->overflow = true;
return;
}
}
if (negative) {
if (x <= 128ull) {
num_lit->kind = NumLitI8;
} else if (x <= 32768ull) {
num_lit->kind = NumLitI16;
} else if (x <= 2147483648ull) {
num_lit->kind = NumLitI32;
} else if (x <= 9223372036854775808ull) {
num_lit->kind = NumLitI64;
} else {
num_lit->overflow = true;
return;
}
num_lit->data.x_int = -((int64_t)x);
} else {
num_lit->data.x_uint = x;
if (x <= UINT8_MAX) {
num_lit->kind = NumLitU8;
} else if (x <= UINT16_MAX) {
num_lit->kind = NumLitU16;
} else if (x <= UINT32_MAX) {
num_lit->kind = NumLitU32;
} else {
num_lit->kind = NumLitU64;
}
}
}
}
__attribute__ ((noreturn))
static void ast_invalid_token_error(ParseContext *pc, Token *token) {
Buf token_value = BUF_INIT;
@ -829,7 +1020,7 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool
if (token->id == TokenIdNumberLiteral) {
AstNode *node = ast_create_node(pc, NodeTypeNumberLiteral, token);
ast_buf_from_token(pc, token, &node->data.number);
parse_number_literal(pc, token, &node->data.number_literal);
*token_index += 1;
return node;
} else if (token->id == TokenIdStringLiteral) {
@ -2152,3 +2343,121 @@ AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens, ImportTableEntry *owner, Er
pc.root = ast_parse_root(&pc, &token_index);
return pc.root;
}
const char *num_lit_str(NumLit num_lit) {
switch (num_lit) {
case NumLitF32:
return "f32";
case NumLitF64:
return "f64";
case NumLitF128:
return "f128";
case NumLitI8:
return "i8";
case NumLitI16:
return "i16";
case NumLitI32:
return "i32";
case NumLitI64:
return "i64";
case NumLitU8:
return "u8";
case NumLitU16:
return "u16";
case NumLitU32:
return "u32";
case NumLitU64:
return "u64";
case NumLitCount:
zig_unreachable();
}
zig_unreachable();
}
bool is_num_lit_signed(NumLit num_lit) {
switch (num_lit) {
case NumLitI8:
case NumLitI16:
case NumLitI32:
case NumLitI64:
return true;
case NumLitF32:
case NumLitF64:
case NumLitF128:
case NumLitU8:
case NumLitU16:
case NumLitU32:
case NumLitU64:
return false;
case NumLitCount:
zig_unreachable();
}
zig_unreachable();
}
bool is_num_lit_unsigned(NumLit num_lit) {
switch (num_lit) {
case NumLitF32:
case NumLitF64:
case NumLitF128:
case NumLitI8:
case NumLitI16:
case NumLitI32:
case NumLitI64:
return false;
case NumLitU8:
case NumLitU16:
case NumLitU32:
case NumLitU64:
return true;
case NumLitCount:
zig_unreachable();
}
zig_unreachable();
}
bool is_num_lit_float(NumLit num_lit) {
switch (num_lit) {
case NumLitF32:
case NumLitF64:
case NumLitF128:
return true;
case NumLitI8:
case NumLitI16:
case NumLitI32:
case NumLitI64:
case NumLitU8:
case NumLitU16:
case NumLitU32:
case NumLitU64:
return false;
case NumLitCount:
zig_unreachable();
}
zig_unreachable();
}
uint64_t num_lit_bit_count(NumLit num_lit) {
switch (num_lit) {
case NumLitI8:
case NumLitU8:
return 8;
case NumLitI16:
case NumLitU16:
return 16;
case NumLitI32:
case NumLitU32:
case NumLitF32:
return 32;
case NumLitI64:
case NumLitU64:
case NumLitF64:
return 64;
case NumLitF128:
return 128;
case NumLitCount:
zig_unreachable();
}
zig_unreachable();
}

View File

@ -267,6 +267,36 @@ struct AstNodeStringLiteral {
bool c;
};
enum NumLit {
NumLitF32,
NumLitF64,
NumLitF128,
NumLitI8,
NumLitU8,
NumLitI16,
NumLitU16,
NumLitI32,
NumLitU32,
NumLitI64,
NumLitU64,
NumLitCount
};
struct AstNodeNumberLiteral {
NumLit kind;
// overflow is true if when parsing the number, we discovered it would not
// fit without losing data in a uint64_t, int64_t, or double
bool overflow;
union {
uint64_t x_uint;
int64_t x_int;
double x_float;
} data;
};
struct AstNode {
enum NodeType type;
int line;
@ -300,7 +330,7 @@ struct AstNode {
AstNodeStructDecl struct_decl;
AstNodeStructField struct_field;
AstNodeStringLiteral string_literal;
Buf number;
AstNodeNumberLiteral number_literal;
Buf symbol;
bool bool_literal;
} data;
@ -329,4 +359,11 @@ const char *node_type_str(NodeType node_type);
void ast_print(AstNode *node, int indent);
const char *num_lit_str(NumLit num_lit);
bool is_num_lit_signed(NumLit num_lit);
bool is_num_lit_unsigned(NumLit num_lit);
bool is_num_lit_float(NumLit num_lit);
uint64_t num_lit_bit_count(NumLit num_lit);
#endif

View File

@ -21,8 +21,16 @@ void zig_panic(const char *format, ...) {
}
uint32_t int_hash(int i) {
return *reinterpret_cast<uint32_t*>(&i);
return (uint32_t)(i % UINT32_MAX);
}
bool int_eq(int a, int b) {
return a == b;
}
uint32_t uint64_hash(uint64_t i) {
return (uint32_t)(i % UINT32_MAX);
}
bool uint64_eq(uint64_t a, uint64_t b) {
return a == b;
}

View File

@ -81,5 +81,7 @@ static inline bool mem_eql_str(const char *mem, size_t mem_len, const char *str)
uint32_t int_hash(int i);
bool int_eq(int a, int b);
uint32_t uint64_hash(uint64_t i);
bool uint64_eq(uint64_t a, uint64_t b);
#endif

View File

@ -15,10 +15,9 @@ fn syscall3(number: isize, arg1: isize, arg2: isize, arg3: isize) -> isize {
// TODO error handling
// TODO handle buffering and flushing
// TODO non-i32 integer literals so we can remove the casts
// TODO constants for SYS_write and stdout_fileno
pub fn print_str(str : string) -> isize {
let SYS_write = 1;
let stdout_fileno = 1;
return syscall3(SYS_write as isize, stdout_fileno as isize, str.ptr as isize, str.len as isize);
let SYS_write : isize = 1;
let stdout_fileno : isize = 1;
return syscall3(SYS_write, stdout_fileno, str.ptr as isize, str.len as isize);
}

View File

@ -266,7 +266,7 @@ extern {
export fn _start() -> unreachable {
let a : i32 = 1;
let b = 2;
let b = 2 as i32;
if (a + b == 3) {
puts(c"OK");
}
@ -299,12 +299,12 @@ extern {
export fn _start() -> unreachable {
if (true) {
let no_conflict = 5;
let no_conflict : i32 = 5;
if (no_conflict == 5) { puts(c"OK 1"); }
}
let c = {
let no_conflict = 10;
let no_conflict = 10 as i32;
no_conflict
};
if (c == 10) { puts(c"OK 2"); }
@ -343,7 +343,7 @@ export fn _start() -> unreachable {
let mut zero : i32;
if (zero == 0) { puts(c"zero"); }
let mut i = 0;
let mut i = 0 as i32;
loop_start:
if i == 3 {
goto done;
@ -366,7 +366,7 @@ extern {
export fn _start() -> unreachable {
let mut array : [i32; 5];
let mut i = 0;
let mut i : i32 = 0;
loop_start:
if i == 5 {
goto loop_end;
@ -378,7 +378,7 @@ loop_start:
loop_end:
i = 0;
let mut accumulator = 0;
let mut accumulator = 0 as i32;
loop_2_start:
if i == 5 {
goto loop_2_end;
@ -611,7 +611,7 @@ fn f() -> i32 {
fn f() {
if (0) {}
}
)SOURCE", 1, ".tmp_source.zig:3:9: error: expected type 'bool', got 'i32'");
)SOURCE", 1, ".tmp_source.zig:3:9: error: expected type 'bool', got '(u8 literal)'");
add_compile_fail_case("assign unreachable", R"SOURCE(
fn f() {
@ -685,13 +685,12 @@ fn f(...) {}
}
static void print_compiler_invocation(TestCase *test_case, Buf *zig_stderr) {
static void print_compiler_invocation(TestCase *test_case) {
printf("%s", zig_exe);
for (int i = 0; i < test_case->compiler_args.length; i += 1) {
printf(" %s", test_case->compiler_args.at(i));
}
printf("\n");
printf("%s\n", buf_ptr(zig_stderr));
}
static void run_test(TestCase *test_case) {
@ -716,21 +715,24 @@ static void run_test(TestCase *test_case) {
printf("========= Expected this compile error: =========\n");
printf("%s\n", err_text);
printf("================================================\n");
print_compiler_invocation(test_case, &zig_stderr);
print_compiler_invocation(test_case);
printf("%s\n", buf_ptr(&zig_stderr));
exit(1);
}
}
return; // success
} else {
printf("\nCompile failed with return code 0 (Expected failure):\n");
print_compiler_invocation(test_case, &zig_stderr);
print_compiler_invocation(test_case);
printf("%s\n", buf_ptr(&zig_stderr));
exit(1);
}
}
if (return_code != 0) {
printf("\nCompile failed with return code %d:\n", return_code);
print_compiler_invocation(test_case, &zig_stderr);
print_compiler_invocation(test_case);
printf("%s\n", buf_ptr(&zig_stderr));
exit(1);
}
@ -740,6 +742,7 @@ static void run_test(TestCase *test_case) {
if (return_code != 0) {
printf("\nProgram exited with return code %d:\n", return_code);
print_compiler_invocation(test_case);
printf("%s", tmp_exe_path);
for (int i = 0; i < test_case->program_args.length; i += 1) {
printf(" %s", test_case->program_args.at(i));
@ -750,6 +753,12 @@ static void run_test(TestCase *test_case) {
}
if (!buf_eql_str(&program_stdout, test_case->output)) {
printf("\n");
print_compiler_invocation(test_case);
printf("%s", tmp_exe_path);
for (int i = 0; i < test_case->program_args.length; i += 1) {
printf(" %s", test_case->program_args.at(i));
}
printf("\n");
printf("==== Test failed. Expected output: ====\n");
printf("%s\n", test_case->output);