From db18d38a43cdf826ba920b482be07198e70e8cae Mon Sep 17 00:00:00 2001 From: Joshua Olson <0joshuaolson1@gmail.com> Date: Sun, 4 Mar 2018 15:46:17 -0700 Subject: [PATCH 01/21] Fix Linux gcc requirement (#807) g++ may be a separate package. I had this problem on Fedora. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9836dba805..066cb3ba69 100644 --- a/README.md +++ b/README.md @@ -124,7 +124,7 @@ libc. Create demo games using Zig. ##### POSIX * cmake >= 2.8.5 - * gcc >= 5.0.0 or clang >= 3.6.0 + * g++ >= 5.0.0 or clang >= 3.6.0 * LLVM, Clang, LLD libraries == 5.0.1, compiled with the same gcc or clang version above ##### Windows From c787837ce5023fdef579bf92097216ce9dfacf34 Mon Sep 17 00:00:00 2001 From: Joshua Olson <0joshuaolson1@gmail.com> Date: Sun, 4 Mar 2018 17:26:16 -0700 Subject: [PATCH 02/21] Clarify what is meant by 'libraries' (#808) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 066cb3ba69..252a73e9d1 100644 --- a/README.md +++ b/README.md @@ -125,13 +125,13 @@ libc. Create demo games using Zig. * cmake >= 2.8.5 * g++ >= 5.0.0 or clang >= 3.6.0 - * LLVM, Clang, LLD libraries == 5.0.1, compiled with the same gcc or clang version above + * LLVM, Clang, LLD development libraries == 5.0.1, compiled with the same gcc or clang version above ##### Windows * cmake >= 2.8.5 * Microsoft Visual Studio 2015 - * LLVM, Clang, LLD libraries == 5.0.1, compiled with the same MSVC version above + * LLVM, Clang, LLD development libraries == 5.0.1, compiled with the same MSVC version above #### Instructions From 8fd7e9115c95c78c7f0fdcf749c3109124c1605f Mon Sep 17 00:00:00 2001 From: Jacob Dufault Date: Mon, 5 Mar 2018 21:42:01 -0800 Subject: [PATCH 03/21] Make Utf8View public, add comments, and make iterator lowercase. --- std/unicode.zig | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/std/unicode.zig b/std/unicode.zig index 81bbc2aab6..66779f401a 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -96,7 +96,15 @@ pub fn utf8ValidateSlice(s: []const u8) bool { return true; } -const Utf8View = struct { +/// Utf8View makes it easy to iterate the code points of a utf-8 encoded string. +/// +/// ``` +/// var utf8 = (try std.unicode.Utf8View.init("hi there")).iterator(); +/// while (utf8.nextCodepointSlice()) |codepoint| { +/// std.debug.warn("got codepoint {}\n", codepoint); +/// } +/// ``` +pub const Utf8View = struct { bytes: []const u8, pub fn init(s: []const u8) !Utf8View { @@ -124,7 +132,7 @@ const Utf8View = struct { } } - pub fn Iterator(s: &const Utf8View) Utf8Iterator { + pub fn iterator(s: &const Utf8View) Utf8Iterator { return Utf8Iterator { .bytes = s.bytes, .i = 0, @@ -165,13 +173,13 @@ const Utf8Iterator = struct { test "utf8 iterator on ascii" { const s = Utf8View.initComptime("abc"); - var it1 = s.Iterator(); + var it1 = s.iterator(); debug.assert(std.mem.eql(u8, "a", ??it1.nextCodepointSlice())); debug.assert(std.mem.eql(u8, "b", ??it1.nextCodepointSlice())); debug.assert(std.mem.eql(u8, "c", ??it1.nextCodepointSlice())); debug.assert(it1.nextCodepointSlice() == null); - var it2 = s.Iterator(); + var it2 = s.iterator(); debug.assert(??it2.nextCodepoint() == 'a'); debug.assert(??it2.nextCodepoint() == 'b'); debug.assert(??it2.nextCodepoint() == 'c'); @@ -189,13 +197,13 @@ test "utf8 view bad" { test "utf8 view ok" { const s = Utf8View.initComptime("東京市"); - var it1 = s.Iterator(); + var it1 = s.iterator(); debug.assert(std.mem.eql(u8, "東", ??it1.nextCodepointSlice())); debug.assert(std.mem.eql(u8, "京", ??it1.nextCodepointSlice())); debug.assert(std.mem.eql(u8, "市", ??it1.nextCodepointSlice())); debug.assert(it1.nextCodepointSlice() == null); - var it2 = s.Iterator(); + var it2 = s.iterator(); debug.assert(??it2.nextCodepoint() == 0x6771); debug.assert(??it2.nextCodepoint() == 0x4eac); debug.assert(??it2.nextCodepoint() == 0x5e02); From ed1386eeff0a3d0fbec74bbf775544104874cead Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Tue, 6 Mar 2018 11:13:10 +0100 Subject: [PATCH 04/21] Simple translation of UO_LNot --- src/translate_c.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 9bea476352..ea3beb3548 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -1907,17 +1907,23 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc return nullptr; } } + case UO_LNot: case UO_Not: { Expr *op_expr = stmt->getSubExpr(); AstNode *sub_node = trans_expr(c, ResultUsedYes, scope, op_expr, TransRValue); if (sub_node == nullptr) return nullptr; - return trans_create_node_prefix_op(c, PrefixOpBinNot, sub_node); + + switch (stmt->getOpcode()) { + case UO_LNot: + return trans_create_node_prefix_op(c, PrefixOpBoolNot, sub_node); + case UO_Not: + return trans_create_node_prefix_op(c, PrefixOpBinNot, sub_node); + default: + zig_unreachable(); + } } - case UO_LNot: - emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_LNot"); - return nullptr; case UO_Real: emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Real"); return nullptr; From 61ecc486717944ad652cd9442fe35a4cfb9ae1ec Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Tue, 6 Mar 2018 11:15:13 +0100 Subject: [PATCH 05/21] Added appropriate TODO comment to UO_LNot --- src/translate_c.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index ea3beb3548..9a1ef7ffd6 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -1917,6 +1917,7 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc switch (stmt->getOpcode()) { case UO_LNot: + // TODO: Handle int, float, pointer negation return trans_create_node_prefix_op(c, PrefixOpBoolNot, sub_node); case UO_Not: return trans_create_node_prefix_op(c, PrefixOpBinNot, sub_node); From bf47cf418af785550f298a519b0dbfa2efcdd3cb Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Tue, 6 Mar 2018 11:57:51 +0100 Subject: [PATCH 06/21] expr to bool is now it's own function. * Now while and for loops work on ints and floats, like if statements * This fixes the loop problem in #813 --- src/translate_c.cpp | 161 ++++++++++++++++++++++--------------------- test/translate_c.zig | 65 ++++++++++++++--- 2 files changed, 139 insertions(+), 87 deletions(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 9a1ef7ffd6..bee867c4ab 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -2204,10 +2204,88 @@ static int trans_local_declaration(Context *c, TransScope *scope, const DeclStmt return ErrorNone; } +static AstNode *trans_to_bool_expr(Context *c, TransScope *scope, AstNode *expr) { + switch (expr->type) { + case NodeTypeBinOpExpr: + switch (expr->data.bin_op_expr.bin_op) { + case BinOpTypeBoolOr: + case BinOpTypeBoolAnd: + case BinOpTypeCmpEq: + case BinOpTypeCmpNotEq: + case BinOpTypeCmpLessThan: + case BinOpTypeCmpGreaterThan: + case BinOpTypeCmpLessOrEq: + case BinOpTypeCmpGreaterOrEq: + return expr; + default: + goto convert_to_bitcast; + } + + case NodeTypePrefixOpExpr: + switch (expr->data.prefix_op_expr.prefix_op) { + case PrefixOpBoolNot: + return expr; + default: + goto convert_to_bitcast; + } + + case NodeTypeBoolLiteral: + return expr; + + default: { + // In Zig, float, int and pointer does not implicitly cast to bool. + // To make it work, we bitcast any value we get to an int of the right size + // and comp it to 0 + // TODO: This doesn't work for pointers, as they become nullable on + // translate + // c: expr + // zig: __to_bool_expr: { + // zig: const _tmp = cond; + // zig: break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0; + // zig: } + convert_to_bitcast: + TransScopeBlock *child_scope = trans_scope_block_create(c, scope); + Buf *label_name = buf_create_from_str("__to_bool_expr"); + child_scope->node->data.block.name = label_name; + + // const _tmp = cond; + // TODO: avoid name collisions with generated variable names + Buf *tmp_var_name = buf_create_from_str("_tmp"); + AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, expr); + child_scope->node->data.block.statements.append(tmp_var_decl); + + // @sizeOf(@typeOf(_tmp)) * 8 + AstNode *typeof_tmp = trans_create_node_builtin_fn_call_str(c, "typeOf"); + typeof_tmp->data.fn_call_expr.params.append(trans_create_node_symbol(c, tmp_var_name)); + AstNode *sizeof_tmp = trans_create_node_builtin_fn_call_str(c, "sizeOf"); + sizeof_tmp->data.fn_call_expr.params.append(typeof_tmp); + AstNode *sizeof_tmp_in_bits = trans_create_node_bin_op( + c, sizeof_tmp, BinOpTypeMult, + trans_create_node_unsigned_negative(c, 8, false)); + + // @IntType(false, @sizeOf(@typeOf(_tmp)) * 8) + AstNode *int_type = trans_create_node_builtin_fn_call_str(c, "IntType"); + int_type->data.fn_call_expr.params.append(trans_create_node_bool(c, false)); + int_type->data.fn_call_expr.params.append(sizeof_tmp_in_bits); + + // @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) + AstNode *bit_cast = trans_create_node_builtin_fn_call_str(c, "bitCast"); + bit_cast->data.fn_call_expr.params.append(int_type); + bit_cast->data.fn_call_expr.params.append(trans_create_node_symbol(c, tmp_var_name)); + + // break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0 + AstNode *not_eql_zero = trans_create_node_bin_op(c, bit_cast, BinOpTypeCmpNotEq, trans_create_node_unsigned_negative(c, 0, false)); + child_scope->node->data.block.statements.append(trans_create_node_break(c, label_name, not_eql_zero)); + + return child_scope->node; + } + } +} + static AstNode *trans_while_loop(Context *c, TransScope *scope, const WhileStmt *stmt) { TransScopeWhile *while_scope = trans_scope_while_create(c, scope); - while_scope->node->data.while_expr.condition = trans_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue); + while_scope->node->data.while_expr.condition = trans_to_bool_expr(c, scope, trans_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue)); if (while_scope->node->data.while_expr.condition == nullptr) return nullptr; @@ -2238,83 +2316,8 @@ static AstNode *trans_if_statement(Context *c, TransScope *scope, const IfStmt * if (condition_node == nullptr) return nullptr; - switch (condition_node->type) { - case NodeTypeBinOpExpr: - switch (condition_node->data.bin_op_expr.bin_op) { - case BinOpTypeBoolOr: - case BinOpTypeBoolAnd: - case BinOpTypeCmpEq: - case BinOpTypeCmpNotEq: - case BinOpTypeCmpLessThan: - case BinOpTypeCmpGreaterThan: - case BinOpTypeCmpLessOrEq: - case BinOpTypeCmpGreaterOrEq: - if_node->data.if_bool_expr.condition = condition_node; - return if_node; - default: - goto convert_to_bitcast; - } - - case NodeTypePrefixOpExpr: - switch (condition_node->data.prefix_op_expr.prefix_op) { - case PrefixOpBoolNot: - if_node->data.if_bool_expr.condition = condition_node; - return if_node; - default: - goto convert_to_bitcast; - } - - case NodeTypeBoolLiteral: - if_node->data.if_bool_expr.condition = condition_node; - return if_node; - - default: { - // In Zig, float, int and pointer does not work in if statements. - // To make it work, we bitcast any value we get to an int of the right size - // and comp it to 0 - // TODO: This doesn't work for pointers, as they become nullable on - // translate - // c: if (cond) { } - // zig: { - // zig: const _tmp = cond; - // zig: if (@bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0) { } - // zig: } - convert_to_bitcast: - TransScopeBlock *child_scope = trans_scope_block_create(c, scope); - - // const _tmp = cond; - // TODO: avoid name collisions with generated variable names - Buf* tmp_var_name = buf_create_from_str("_tmp"); - AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, condition_node); - child_scope->node->data.block.statements.append(tmp_var_decl); - - // @sizeOf(@typeOf(_tmp)) * 8 - AstNode *typeof_tmp = trans_create_node_builtin_fn_call_str(c, "typeOf"); - typeof_tmp->data.fn_call_expr.params.append(trans_create_node_symbol(c, tmp_var_name)); - AstNode *sizeof_tmp = trans_create_node_builtin_fn_call_str(c, "sizeOf"); - sizeof_tmp->data.fn_call_expr.params.append(typeof_tmp); - AstNode *sizeof_tmp_in_bits = trans_create_node_bin_op( - c, sizeof_tmp, BinOpTypeMult, - trans_create_node_unsigned_negative(c, 8, false)); - - // @IntType(false, @sizeOf(@typeOf(_tmp)) * 8) - AstNode *int_type = trans_create_node_builtin_fn_call_str(c, "IntType"); - int_type->data.fn_call_expr.params.append(trans_create_node_bool(c, false)); - int_type->data.fn_call_expr.params.append(sizeof_tmp_in_bits); - - // @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) - AstNode *bit_cast = trans_create_node_builtin_fn_call_str(c, "bitCast"); - bit_cast->data.fn_call_expr.params.append(int_type); - bit_cast->data.fn_call_expr.params.append(trans_create_node_symbol(c, tmp_var_name)); - - // if (@bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0) { } - AstNode *not_eql_zero = trans_create_node_bin_op(c, bit_cast, BinOpTypeCmpNotEq, trans_create_node_unsigned_negative(c, 0, false)); - if_node->data.if_bool_expr.condition = not_eql_zero; - child_scope->node->data.block.statements.append(if_node); - - return child_scope->node; - } - } + if_node->data.if_bool_expr.condition = trans_to_bool_expr(c, scope, condition_node); + return if_node; } static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope *scope, const CallExpr *stmt) { @@ -2503,6 +2506,8 @@ static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForSt &while_scope->node->data.while_expr.condition); if (end_cond_scope == nullptr) return nullptr; + + while_scope->node->data.while_expr.condition = trans_to_bool_expr(c, cond_scope, while_scope->node->data.while_expr.condition); } const Stmt *inc_stmt = stmt->getInc(); diff --git a/test/translate_c.zig b/test/translate_c.zig index e6270842b1..23bb1759c0 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1124,15 +1124,62 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ } \\} , - \\pub fn if_int(i: c_int) c_int { - \\ { - \\ const _tmp = i; - \\ if (@bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0) { - \\ return 0; - \\ } else { - \\ return 1; - \\ }; - \\ }; + \\pub fn if_int(i: c_int) c_int { + \\ if (__to_bool_expr: { + \\ const _tmp = i; + \\ break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0; + \\ }) { + \\ return 0; + \\ } else { + \\ return 1; + \\ }; + \\} + ); + + cases.add("while on int", + \\int while_int(int i) { + \\ while (i) { + \\ return 0; + \\ } \\} + , + \\pub fn while_int(i: c_int) c_int { + \\ while (__to_bool_expr: { + \\ const _tmp = i; + \\ break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0; + \\ }) { + \\ return 0; + \\ }; + \\} + ); + + cases.add("for on int", + \\int for_int(int i) { + \\ for (;i;) { + \\ return 0; + \\ } + \\ + \\ for (int j = 4;j;j--) { + \\ return 0; + \\ } + \\} + , + \\pub fn for_int(i: c_int) c_int { + \\ while (__to_bool_expr: { + \\ const _tmp = i; + \\ break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0; + \\ }) { + \\ return 0; + \\ }; + \\ { + \\ var j: c_int = 4; + \\ while (__to_bool_expr: { + \\ const _tmp = j; + \\ break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0; + \\ }) : (j -= 1) { + \\ return 0; + \\ }; + \\ }; + \\} ); } From 5ab25798e313105bf783934ac3c18b2930c8da5e Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Tue, 6 Mar 2018 12:04:14 +0100 Subject: [PATCH 07/21] We now also use trans_to_bool_expr on bool not --- src/translate_c.cpp | 14 +++++++------- test/translate_c.zig | 17 ++++++++++++++++- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index bee867c4ab..3035a6682d 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -118,7 +118,7 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, static TransScope *trans_stmt(Context *c, TransScope *scope, const Stmt *stmt, AstNode **out_node); static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr, TransLRValue lrval); static AstNode *trans_qual_type(Context *c, QualType qt, const SourceLocation &source_loc); - +static AstNode *trans_to_bool_expr(Context *c, TransScope *scope, AstNode *expr); ATTRIBUTE_PRINTF(3, 4) static void emit_warning(Context *c, const SourceLocation &sl, const char *format, ...) { @@ -632,7 +632,7 @@ static bool c_is_signed_integer(Context *c, QualType qt) { case BuiltinType::Int128: case BuiltinType::WChar_S: return true; - default: + default: return false; } } @@ -653,7 +653,7 @@ static bool c_is_unsigned_integer(Context *c, QualType qt) { case BuiltinType::UInt128: case BuiltinType::WChar_U: return true; - default: + default: return false; } } @@ -678,7 +678,7 @@ static bool c_is_float(Context *c, QualType qt) { case BuiltinType::Float128: case BuiltinType::LongDouble: return true; - default: + default: return false; } } @@ -1389,7 +1389,7 @@ static AstNode *trans_create_compound_assign_shift(Context *c, ResultUsed result if (result_used == ResultUsedYes) { // break :x *_ref child_scope->node->data.block.statements.append( - trans_create_node_break(c, label_name, + trans_create_node_break(c, label_name, trans_create_node_prefix_op(c, PrefixOpDereference, trans_create_node_symbol(c, tmp_var_name)))); } @@ -1918,7 +1918,7 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc switch (stmt->getOpcode()) { case UO_LNot: // TODO: Handle int, float, pointer negation - return trans_create_node_prefix_op(c, PrefixOpBoolNot, sub_node); + return trans_create_node_prefix_op(c, PrefixOpBoolNot, trans_to_bool_expr(c, scope, sub_node)); case UO_Not: return trans_create_node_prefix_op(c, PrefixOpBinNot, sub_node); default: @@ -2291,7 +2291,7 @@ static AstNode *trans_while_loop(Context *c, TransScope *scope, const WhileStmt TransScope *body_scope = trans_stmt(c, &while_scope->base, stmt->getBody(), &while_scope->node->data.while_expr.body); - if (body_scope == nullptr) + if (body_scope == nullptr) return nullptr; return while_scope->node; diff --git a/test/translate_c.zig b/test/translate_c.zig index 23bb1759c0..e1aa648d73 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1083,6 +1083,21 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\} ); + cases.add("bool not", + \\int foo(int x) { + \\ return !(x == 0); + \\ return !x; + \\} + , + \\pub fn foo(x: c_int) c_int { + \\ return !(x == 0); + \\ return !__to_bool_expr: { + \\ const _tmp = x; + \\ break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0; + \\ }; + \\} + ); + cases.add("primitive types included in defined symbols", \\int foo(int u32) { \\ return u32; @@ -1110,7 +1125,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { ); cases.add("macro pointer cast", - \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE) + \\#define NRF_GPIO ((NRF_GPIO_Type *) NRF_GPIO_BASE) , \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast(&NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr(&NRF_GPIO_Type, NRF_GPIO_BASE) else (&NRF_GPIO_Type)(NRF_GPIO_BASE); ); From 1d378d8f261603dde69055646b1806ada393b1e0 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Tue, 6 Mar 2018 12:33:09 +0100 Subject: [PATCH 08/21] Removed fixed todo --- src/translate_c.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 3035a6682d..183f3928e0 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -1917,7 +1917,6 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc switch (stmt->getOpcode()) { case UO_LNot: - // TODO: Handle int, float, pointer negation return trans_create_node_prefix_op(c, PrefixOpBoolNot, trans_to_bool_expr(c, scope, sub_node)); case UO_Not: return trans_create_node_prefix_op(c, PrefixOpBinNot, sub_node); From c3807dfb34e3d0696517f3adf1f10ed096bd5fc3 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 6 Mar 2018 10:41:07 -0500 Subject: [PATCH 09/21] remove value judgement from std lib API docs documentation should be purely technical, and not contain opinions about how easy or hard something is. --- std/unicode.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/std/unicode.zig b/std/unicode.zig index 66779f401a..356df824f0 100644 --- a/std/unicode.zig +++ b/std/unicode.zig @@ -96,7 +96,7 @@ pub fn utf8ValidateSlice(s: []const u8) bool { return true; } -/// Utf8View makes it easy to iterate the code points of a utf-8 encoded string. +/// Utf8View iterates the code points of a utf-8 encoded string. /// /// ``` /// var utf8 = (try std.unicode.Utf8View.init("hi there")).iterator(); From 07e47c058c480914d45ec0b1f9c61b76f59e6299 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 6 Mar 2018 16:37:03 -0500 Subject: [PATCH 10/21] ptrCast builtin now gives an error for removing const qualifier closes #384 --- src/analyze.cpp | 13 +++++++++++++ src/analyze.hpp | 1 + src/ir.cpp | 5 +++++ std/buf_map.zig | 4 +--- std/buf_set.zig | 4 +--- std/os/index.zig | 2 +- std/special/compiler_rt/udivmod.zig | 4 ++-- test/cases/cast.zig | 2 +- test/cases/misc.zig | 2 +- test/compile_errors.zig | 8 ++++++++ 10 files changed, 34 insertions(+), 11 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 1e16a8dbd1..fb7f04d6d2 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -3753,6 +3753,19 @@ uint32_t get_ptr_align(TypeTableEntry *type) { } } +bool get_ptr_const(TypeTableEntry *type) { + TypeTableEntry *ptr_type = get_codegen_ptr_type(type); + if (ptr_type->id == TypeTableEntryIdPointer) { + return ptr_type->data.pointer.is_const; + } else if (ptr_type->id == TypeTableEntryIdFn) { + return true; + } else if (ptr_type->id == TypeTableEntryIdPromise) { + return true; + } else { + zig_unreachable(); + } +} + AstNode *get_param_decl_node(FnTableEntry *fn_entry, size_t index) { if (fn_entry->param_source_nodes) return fn_entry->param_source_nodes[index]; diff --git a/src/analyze.hpp b/src/analyze.hpp index e9f89aa638..9ed24f4e46 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -55,6 +55,7 @@ bool type_is_codegen_pointer(TypeTableEntry *type); TypeTableEntry *get_codegen_ptr_type(TypeTableEntry *type); uint32_t get_ptr_align(TypeTableEntry *type); +bool get_ptr_const(TypeTableEntry *type); TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEntry *type_entry); TypeTableEntry *container_ref_type(TypeTableEntry *type_entry); bool type_is_complete(TypeTableEntry *type_entry); diff --git a/src/ir.cpp b/src/ir.cpp index 3d1926298d..251c55df4f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -16816,6 +16816,11 @@ static TypeTableEntry *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstruc return ira->codegen->builtin_types.entry_invalid; } + if (get_ptr_const(src_type) && !get_ptr_const(dest_type)) { + ir_add_error(ira, &instruction->base, buf_sprintf("cast discards const qualifier")); + return ira->codegen->builtin_types.entry_invalid; + } + if (instr_is_comptime(ptr)) { ConstExprValue *val = ir_resolve_const(ira, ptr, UndefOk); if (!val) diff --git a/std/buf_map.zig b/std/buf_map.zig index d7f81cf2cc..a58df4b2db 100644 --- a/std/buf_map.zig +++ b/std/buf_map.zig @@ -62,9 +62,7 @@ pub const BufMap = struct { } fn free(self: &BufMap, value: []const u8) void { - // remove the const - const mut_value = @ptrCast(&u8, value.ptr)[0..value.len]; - self.hash_map.allocator.free(mut_value); + self.hash_map.allocator.free(value); } fn copy(self: &BufMap, value: []const u8) ![]const u8 { diff --git a/std/buf_set.zig b/std/buf_set.zig index 4fa16762b6..618b985c41 100644 --- a/std/buf_set.zig +++ b/std/buf_set.zig @@ -50,9 +50,7 @@ pub const BufSet = struct { } fn free(self: &BufSet, value: []const u8) void { - // remove the const - const mut_value = @ptrCast(&u8, value.ptr)[0..value.len]; - self.hash_map.allocator.free(mut_value); + self.hash_map.allocator.free(value); } fn copy(self: &BufSet, value: []const u8) ![]const u8 { diff --git a/std/os/index.zig b/std/os/index.zig index 7e9a12c62b..7feaae0ff1 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -1634,7 +1634,7 @@ pub fn argsFree(allocator: &mem.Allocator, args_alloc: []const []u8) void { for (args_alloc) |arg| { total_bytes += @sizeOf([]u8) + arg.len; } - const unaligned_allocated_buf = @ptrCast(&u8, args_alloc.ptr)[0..total_bytes]; + const unaligned_allocated_buf = @ptrCast(&const u8, args_alloc.ptr)[0..total_bytes]; const aligned_allocated_buf = @alignCast(@alignOf([]u8), unaligned_allocated_buf); return allocator.free(aligned_allocated_buf); } diff --git a/std/special/compiler_rt/udivmod.zig b/std/special/compiler_rt/udivmod.zig index 82751d5993..07eaef583c 100644 --- a/std/special/compiler_rt/udivmod.zig +++ b/std/special/compiler_rt/udivmod.zig @@ -11,8 +11,8 @@ pub fn udivmod(comptime DoubleInt: type, a: DoubleInt, b: DoubleInt, maybe_rem: const SignedDoubleInt = @IntType(true, DoubleInt.bit_count); const Log2SingleInt = @import("../../math/index.zig").Log2Int(SingleInt); - const n = *@ptrCast(&[2]SingleInt, &a); // TODO issue #421 - const d = *@ptrCast(&[2]SingleInt, &b); // TODO issue #421 + const n = *@ptrCast(&const [2]SingleInt, &a); // TODO issue #421 + const d = *@ptrCast(&const [2]SingleInt, &b); // TODO issue #421 var q: [2]SingleInt = undefined; var r: [2]SingleInt = undefined; var sr: c_uint = undefined; diff --git a/test/cases/cast.zig b/test/cases/cast.zig index d2671680c8..024ece0055 100644 --- a/test/cases/cast.zig +++ b/test/cases/cast.zig @@ -16,7 +16,7 @@ test "integer literal to pointer cast" { test "pointer reinterpret const float to int" { const float: f64 = 5.99999999999994648725e-01; const float_ptr = &float; - const int_ptr = @ptrCast(&i32, float_ptr); + const int_ptr = @ptrCast(&const i32, float_ptr); const int_val = *int_ptr; assert(int_val == 858993411); } diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 5e453fcbc1..976dc5965c 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -261,7 +261,7 @@ test "generic malloc free" { const a = memAlloc(u8, 10) catch unreachable; memFree(u8, a); } -const some_mem : [100]u8 = undefined; +var some_mem : [100]u8 = undefined; fn memAlloc(comptime T: type, n: usize) error![]T { return @ptrCast(&T, &some_mem[0])[0..n]; } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index a3ac4e2344..be2750253c 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,14 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) void { + cases.add("@ptrCast discards const qualifier", + \\export fn entry() void { + \\ const x: i32 = 1234; + \\ const y = @ptrCast(&i32, &x); + \\} + , + ".tmp_source.zig:3:15: error: cast discards const qualifier"); + cases.add("comptime slice of undefined pointer non-zero len", \\export fn entry() void { \\ const slice = (&i32)(undefined)[0..1]; From 5d5820029d4ba44cf9e908f56f4bedae25ffe105 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 6 Mar 2018 16:46:45 -0500 Subject: [PATCH 11/21] fix broken tests from previous commit --- test/compare_output.zig | 4 ++-- test/compile_errors.zig | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/compare_output.zig b/test/compare_output.zig index 2ca2ba262e..9595bf8259 100644 --- a/test/compare_output.zig +++ b/test/compare_output.zig @@ -285,8 +285,8 @@ pub fn addCases(cases: &tests.CompareOutputContext) void { \\const c = @cImport(@cInclude("stdlib.h")); \\ \\export fn compare_fn(a: ?&const c_void, b: ?&const c_void) c_int { - \\ const a_int = @ptrCast(&align(1) i32, a ?? unreachable); - \\ const b_int = @ptrCast(&align(1) i32, b ?? unreachable); + \\ const a_int = @ptrCast(&align(1) const i32, a ?? unreachable); + \\ const b_int = @ptrCast(&align(1) const i32, b ?? unreachable); \\ if (*a_int < *b_int) { \\ return -1; \\ } else if (*a_int > *b_int) { diff --git a/test/compile_errors.zig b/test/compile_errors.zig index be2750253c..61234864bb 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2440,7 +2440,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) void { \\const Derp = @OpaqueType(); \\extern fn bar(d: &Derp) void; \\export fn foo() void { - \\ const x = u8(1); + \\ var x = u8(1); \\ bar(@ptrCast(&c_void, &x)); \\} , From cc0f660ad245b0482981b8dcc2b200c7d68b943d Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 6 Mar 2018 16:57:41 -0500 Subject: [PATCH 12/21] unless hf is specified in target environ, assume soft floating point closes #804 --- src/target.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/target.cpp b/src/target.cpp index b16650dc25..182da84e09 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -761,7 +761,7 @@ static FloatAbi get_float_abi(ZigTarget *target) { { return FloatAbiHard; } else { - zig_panic("TODO: user needs to input if they want hard or soft floating point"); + return FloatAbiSoft; } } From 44ae891bd79cc8b2f9040a39c176317ebf4a4ef8 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 6 Mar 2018 17:19:45 -0500 Subject: [PATCH 13/21] fix assertion when taking slice of zero-length array closes #788 --- src/ir.cpp | 6 +++++- test/cases/misc.zig | 10 ++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/ir.cpp b/src/ir.cpp index 251c55df4f..61f04fef3d 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -15821,9 +15821,13 @@ static TypeTableEntry *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstructio TypeTableEntry *return_type; if (array_type->id == TypeTableEntryIdArray) { + uint32_t byte_alignment = ptr_type->data.pointer.alignment; + if (array_type->data.array.len == 0 && byte_alignment == 0) { + byte_alignment = get_abi_alignment(ira->codegen, array_type->data.array.child_type); + } TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type, ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - ptr_type->data.pointer.alignment, 0, 0); + byte_alignment, 0, 0); return_type = get_slice_type(ira->codegen, slice_ptr_type); } else if (array_type->id == TypeTableEntryIdPointer) { TypeTableEntry *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.pointer.child_type, diff --git a/test/cases/misc.zig b/test/cases/misc.zig index 976dc5965c..905f11a848 100644 --- a/test/cases/misc.zig +++ b/test/cases/misc.zig @@ -650,3 +650,13 @@ test "packed struct, enum, union parameters in extern function" { export fn testPackedStuff(a: &const PackedStruct, b: &const PackedUnion, c: PackedEnum) void { } + + +test "slicing zero length array" { + const s1 = ""[0..]; + const s2 = ([]u32{})[0..]; + assert(s1.len == 0); + assert(s2.len == 0); + assert(mem.eql(u8, s1, "")); + assert(mem.eql(u32, s2, []u32{})); +} From eff3530dfab5ecb4e480e0516ed57a8f564543f5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 6 Mar 2018 18:24:49 -0500 Subject: [PATCH 14/21] var is no longer a pseudo-type, it is syntax closes #779 --- doc/langref.html.in | 6 +- src/all_types.hpp | 9 +-- src/analyze.cpp | 46 ++++++--------- src/ast_render.cpp | 23 ++++---- src/codegen.cpp | 10 ---- src/ir.cpp | 122 ++++++++++++++++------------------------ src/parser.cpp | 47 ++++++++-------- test/compile_errors.zig | 7 +++ 8 files changed, 116 insertions(+), 154 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 38cbd63b2a..1183e25c2f 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -5733,19 +5733,19 @@ UseDecl = "use" Expression ";" ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";" -FnProto = option("nakedcc" | "stdcallcc" | "extern" | ("async" option("(" Expression ")"))) "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") TypeExpr +FnProto = option("nakedcc" | "stdcallcc" | "extern" | ("async" option("(" Expression ")"))) "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") (TypeExpr | "var") FnDef = option("inline" | "export") FnProto Block ParamDeclList = "(" list(ParamDecl, ",") ")" -ParamDecl = option("noalias" | "comptime") option(Symbol ":") (TypeExpr | "...") +ParamDecl = option("noalias" | "comptime") option(Symbol ":") (TypeExpr | "var" | "...") Block = option(Symbol ":") "{" many(Statement) "}" Statement = LocalVarDecl ";" | Defer(Block) | Defer(Expression) ";" | BlockExpression(Block) | Expression ";" | ";" -TypeExpr = ErrorSetExpr | "var" +TypeExpr = ErrorSetExpr ErrorSetExpr = (PrefixOpExpression "!" PrefixOpExpression) | PrefixOpExpression diff --git a/src/all_types.hpp b/src/all_types.hpp index 72ec860556..6f36d34bfd 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -16,6 +16,7 @@ #include "bigint.hpp" #include "bigfloat.hpp" #include "target.hpp" +#include "tokenizer.hpp" struct AstNode; struct ImportTableEntry; @@ -399,7 +400,6 @@ enum NodeType { NodeTypeStructValueField, NodeTypeArrayType, NodeTypeErrorType, - NodeTypeVarLiteral, NodeTypeIfErrorExpr, NodeTypeTestExpr, NodeTypeErrorSetDecl, @@ -427,6 +427,7 @@ struct AstNodeFnProto { Buf *name; ZigList params; AstNode *return_type; + Token *return_var_token; bool is_var_args; bool is_extern; bool is_export; @@ -456,6 +457,7 @@ struct AstNodeFnDecl { struct AstNodeParamDecl { Buf *name; AstNode *type; + Token *var_token; bool is_noalias; bool is_inline; bool is_var_args; @@ -866,9 +868,6 @@ struct AstNodeUnreachableExpr { struct AstNodeErrorType { }; -struct AstNodeVarLiteral { -}; - struct AstNodeAwaitExpr { AstNode *expr; }; @@ -933,7 +932,6 @@ struct AstNode { AstNodeUnreachableExpr unreachable_expr; AstNodeArrayType array_type; AstNodeErrorType error_type; - AstNodeVarLiteral var_literal; AstNodeErrorSetDecl err_set_decl; AstNodeCancelExpr cancel_expr; AstNodeResumeExpr resume_expr; @@ -1134,7 +1132,6 @@ struct TypeTableEntryPromise { enum TypeTableEntryId { TypeTableEntryIdInvalid, - TypeTableEntryIdVar, TypeTableEntryIdMetaType, TypeTableEntryIdVoid, TypeTableEntryIdBool, diff --git a/src/analyze.cpp b/src/analyze.cpp index fb7f04d6d2..42bf2c8d4f 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -200,7 +200,6 @@ static uint8_t bits_needed_for_unsigned(uint64_t x) { bool type_is_complete(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: zig_unreachable(); case TypeTableEntryIdStruct: return type_entry->data.structure.complete; @@ -239,7 +238,6 @@ bool type_is_complete(TypeTableEntry *type_entry) { bool type_has_zero_bits_known(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: zig_unreachable(); case TypeTableEntryIdStruct: return type_entry->data.structure.zero_bits_known; @@ -1281,7 +1279,6 @@ static bool analyze_const_string(CodeGen *g, Scope *scope, AstNode *node, Buf ** static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: zig_unreachable(); case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: @@ -1324,7 +1321,6 @@ static bool type_allowed_in_packed_struct(TypeTableEntry *type_entry) { static bool type_allowed_in_extern(CodeGen *g, TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: zig_unreachable(); case TypeTableEntryIdMetaType: case TypeTableEntryIdNumLitFloat: @@ -1428,6 +1424,14 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c calling_convention_name(fn_type_id.cc))); return g->builtin_types.entry_invalid; } + } else if (param_node->data.param_decl.var_token != nullptr) { + if (!calling_convention_allows_zig_types(fn_type_id.cc)) { + add_node_error(g, param_node->data.param_decl.type, + buf_sprintf("parameter of type 'var' not allowed in function with calling convention '%s'", + calling_convention_name(fn_type_id.cc))); + return g->builtin_types.entry_invalid; + } + return get_generic_fn_type(g, &fn_type_id); } TypeTableEntry *type_entry = analyze_type_expr(g, child_scope, param_node->data.param_decl.type); @@ -1463,14 +1467,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c add_node_error(g, param_node->data.param_decl.type, buf_sprintf("parameter of type '%s' not allowed", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; - case TypeTableEntryIdVar: - if (!calling_convention_allows_zig_types(fn_type_id.cc)) { - add_node_error(g, param_node->data.param_decl.type, - buf_sprintf("parameter of type 'var' not allowed in function with calling convention '%s'", - calling_convention_name(fn_type_id.cc))); - return g->builtin_types.entry_invalid; - } - return get_generic_fn_type(g, &fn_type_id); case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdNamespace: @@ -1527,6 +1523,16 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c fn_type_id.return_type = specified_return_type; } + if (fn_proto->return_var_token != nullptr) { + if (!calling_convention_allows_zig_types(fn_type_id.cc)) { + add_node_error(g, fn_proto->return_type, + buf_sprintf("return type 'var' not allowed in function with calling convention '%s'", + calling_convention_name(fn_type_id.cc))); + return g->builtin_types.entry_invalid; + } + return get_generic_fn_type(g, &fn_type_id); + } + if (!calling_convention_allows_zig_types(fn_type_id.cc) && !type_allowed_in_extern(g, fn_type_id.return_type)) { add_node_error(g, fn_proto->return_type, buf_sprintf("return type '%s' not allowed in function with calling convention '%s'", @@ -1552,7 +1558,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: if (!calling_convention_allows_zig_types(fn_type_id.cc)) { add_node_error(g, fn_proto->return_type, @@ -3226,7 +3231,6 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { case NodeTypeStructValueField: case NodeTypeArrayType: case NodeTypeErrorType: - case NodeTypeVarLiteral: case NodeTypeIfErrorExpr: case NodeTypeTestExpr: case NodeTypeErrorSetDecl: @@ -3262,7 +3266,6 @@ TypeTableEntry *validate_var_type(CodeGen *g, AstNode *source_node, TypeTableEnt case TypeTableEntryIdInvalid: return g->builtin_types.entry_invalid; case TypeTableEntryIdUnreachable: - case TypeTableEntryIdVar: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: @@ -3641,7 +3644,6 @@ TypeEnumField *find_enum_field_by_tag(TypeTableEntry *enum_type, const BigInt *t static bool is_container(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: zig_unreachable(); case TypeTableEntryIdStruct: case TypeTableEntryIdEnum: @@ -3716,7 +3718,6 @@ void resolve_container_type(CodeGen *g, TypeTableEntry *type_entry) { case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: case TypeTableEntryIdPromise: @@ -4216,7 +4217,6 @@ bool handle_is_ptr(TypeTableEntry *type_entry) { case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdVar: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: zig_unreachable(); @@ -4515,7 +4515,6 @@ static uint32_t hash_const_val(ConstExprValue *const_val) { case TypeTableEntryIdBoundFn: case TypeTableEntryIdInvalid: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdVar: zig_unreachable(); } zig_unreachable(); @@ -4613,7 +4612,6 @@ bool type_has_bits(TypeTableEntry *type_entry) { bool type_requires_comptime(TypeTableEntry *type_entry) { switch (type_entry->id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: case TypeTableEntryIdOpaque: zig_unreachable(); case TypeTableEntryIdNumLitFloat: @@ -5109,7 +5107,6 @@ bool const_values_equal(ConstExprValue *a, ConstExprValue *b) { case TypeTableEntryIdBoundFn: case TypeTableEntryIdInvalid: case TypeTableEntryIdUnreachable: - case TypeTableEntryIdVar: case TypeTableEntryIdPromise: zig_unreachable(); } @@ -5189,9 +5186,6 @@ void render_const_value(CodeGen *g, Buf *buf, ConstExprValue *const_val) { case TypeTableEntryIdInvalid: buf_appendf(buf, "(invalid)"); return; - case TypeTableEntryIdVar: - buf_appendf(buf, "(var)"); - return; case TypeTableEntryIdVoid: buf_appendf(buf, "{}"); return; @@ -5427,7 +5421,6 @@ TypeTableEntry *make_int_type(CodeGen *g, bool is_signed, uint32_t size_in_bits) uint32_t type_id_hash(TypeId x) { switch (x.id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: case TypeTableEntryIdOpaque: case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: @@ -5474,7 +5467,6 @@ bool type_id_eql(TypeId a, TypeId b) { return false; switch (a.id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: @@ -5629,7 +5621,6 @@ size_t type_id_len() { size_t type_id_index(TypeTableEntryId id) { switch (id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: zig_unreachable(); case TypeTableEntryIdMetaType: return 0; @@ -5688,7 +5679,6 @@ size_t type_id_index(TypeTableEntryId id) { const char *type_id_name(TypeTableEntryId id) { switch (id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: zig_unreachable(); case TypeTableEntryIdMetaType: return "Type"; diff --git a/src/ast_render.cpp b/src/ast_render.cpp index 6318ba3cff..ef1fe6e231 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -236,8 +236,6 @@ static const char *node_type_str(NodeType node_type) { return "ArrayType"; case NodeTypeErrorType: return "ErrorType"; - case NodeTypeVarLiteral: - return "VarLiteral"; case NodeTypeIfErrorExpr: return "IfErrorExpr"; case NodeTypeTestExpr: @@ -436,6 +434,8 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { } if (param_decl->data.param_decl.is_var_args) { fprintf(ar->f, "..."); + } else if (param_decl->data.param_decl.var_token != nullptr) { + fprintf(ar->f, "var"); } else { render_node_grouped(ar, param_decl->data.param_decl.type); } @@ -456,13 +456,17 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { fprintf(ar->f, ")"); } - AstNode *return_type_node = node->data.fn_proto.return_type; - assert(return_type_node != nullptr); - fprintf(ar->f, " "); - if (node->data.fn_proto.auto_err_set) { - fprintf(ar->f, "!"); + if (node->data.fn_proto.return_var_token != nullptr) { + fprintf(ar->f, "var"); + } else { + AstNode *return_type_node = node->data.fn_proto.return_type; + assert(return_type_node != nullptr); + fprintf(ar->f, " "); + if (node->data.fn_proto.auto_err_set) { + fprintf(ar->f, "!"); + } + render_node_grouped(ar, return_type_node); } - render_node_grouped(ar, return_type_node); break; } case NodeTypeFnDef: @@ -768,9 +772,6 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { case NodeTypeErrorType: fprintf(ar->f, "error"); break; - case NodeTypeVarLiteral: - fprintf(ar->f, "var"); - break; case NodeTypeAsmExpr: { AstNodeAsmExpr *asm_expr = &node->data.asm_expr; diff --git a/src/codegen.cpp b/src/codegen.cpp index 3aa8f63100..4a7b8f628f 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -4508,7 +4508,6 @@ static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, Con assert(!type_entry->zero_bits); switch (type_entry->id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: case TypeTableEntryIdNumLitFloat: @@ -4960,7 +4959,6 @@ static LLVMValueRef gen_const_val(CodeGen *g, ConstExprValue *const_val, const c case TypeTableEntryIdNamespace: case TypeTableEntryIdBlock: case TypeTableEntryIdBoundFn: - case TypeTableEntryIdVar: case TypeTableEntryIdArgTuple: case TypeTableEntryIdOpaque: case TypeTableEntryIdPromise: @@ -5611,11 +5609,6 @@ static void define_builtin_types(CodeGen *g) { entry->zero_bits = true; g->builtin_types.entry_null = entry; } - { - TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdVar); - buf_init_from_str(&entry->name, "(var)"); - g->builtin_types.entry_var = entry; - } { TypeTableEntry *entry = new_type_table_entry(TypeTableEntryIdArgTuple); buf_init_from_str(&entry->name, "(args)"); @@ -6444,7 +6437,6 @@ static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, TypeTableEntry switch (type_entry->id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: @@ -6639,7 +6631,6 @@ static void get_c_type(CodeGen *g, GenH *gen_h, TypeTableEntry *type_entry, Buf case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: - case TypeTableEntryIdVar: case TypeTableEntryIdArgTuple: case TypeTableEntryIdPromise: zig_unreachable(); @@ -6781,7 +6772,6 @@ static void gen_h_file(CodeGen *g) { TypeTableEntry *type_entry = gen_h->types_to_declare.at(type_i); switch (type_entry->id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: case TypeTableEntryIdBool: diff --git a/src/ir.cpp b/src/ir.cpp index 61f04fef3d..6fc1322c30 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -2147,7 +2147,7 @@ static IrInstruction *ir_build_fn_proto(IrBuilder *irb, Scope *scope, AstNode *s size_t param_count = source_node->data.fn_proto.params.length; if (is_var_args) param_count -= 1; for (size_t i = 0; i < param_count; i += 1) { - ir_ref_instruction(param_types[i], irb->current_basic_block); + if (param_types[i] != nullptr) ir_ref_instruction(param_types[i], irb->current_basic_block); } if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); ir_ref_instruction(return_type, irb->current_basic_block); @@ -3305,12 +3305,6 @@ static IrInstruction *ir_gen_null_literal(IrBuilder *irb, Scope *scope, AstNode return ir_build_const_null(irb, scope, node); } -static IrInstruction *ir_gen_var_literal(IrBuilder *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeVarLiteral); - - return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_var); -} - static IrInstruction *ir_gen_symbol(IrBuilder *irb, Scope *scope, AstNode *node, LVal lval) { assert(node->type == NodeTypeSymbol); @@ -5916,11 +5910,15 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo is_var_args = true; break; } - AstNode *type_node = param_node->data.param_decl.type; - IrInstruction *type_value = ir_gen_node(irb, type_node, parent_scope); - if (type_value == irb->codegen->invalid_instruction) - return irb->codegen->invalid_instruction; - param_types[i] = type_value; + if (param_node->data.param_decl.var_token == nullptr) { + AstNode *type_node = param_node->data.param_decl.type; + IrInstruction *type_value = ir_gen_node(irb, type_node, parent_scope); + if (type_value == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + param_types[i] = type_value; + } else { + param_types[i] = nullptr; + } } IrInstruction *align_value = nullptr; @@ -5931,12 +5929,16 @@ static IrInstruction *ir_gen_fn_proto(IrBuilder *irb, Scope *parent_scope, AstNo } IrInstruction *return_type; - if (node->data.fn_proto.return_type == nullptr) { - return_type = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_void); + if (node->data.fn_proto.return_var_token == nullptr) { + if (node->data.fn_proto.return_type == nullptr) { + return_type = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_void); + } else { + return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope); + if (return_type == irb->codegen->invalid_instruction) + return irb->codegen->invalid_instruction; + } } else { - return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope); - if (return_type == irb->codegen->invalid_instruction) - return irb->codegen->invalid_instruction; + return_type = nullptr; } return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, return_type, is_var_args); @@ -6189,8 +6191,6 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop return ir_lval_wrap(irb, scope, ir_gen_asm_expr(irb, scope, node), lval); case NodeTypeNullLiteral: return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval); - case NodeTypeVarLiteral: - return ir_lval_wrap(irb, scope, ir_gen_var_literal(irb, scope, node), lval); case NodeTypeIfErrorExpr: return ir_lval_wrap(irb, scope, ir_gen_if_err_expr(irb, scope, node), lval); case NodeTypeTestExpr: @@ -7515,11 +7515,6 @@ static ImplicitCastMatchResult ir_types_match_with_implicit_cast(IrAnalyze *ira, return ImplicitCastMatchResultReportedError; } - // implicit conversion from anything to var - if (expected_type->id == TypeTableEntryIdVar) { - return ImplicitCastMatchResultYes; - } - // implicit conversion from non maybe type to maybe type if (expected_type->id == TypeTableEntryIdMaybe && ir_types_match_with_implicit_cast(ira, expected_type->data.maybe.child_type, actual_type, value)) @@ -9341,9 +9336,6 @@ static IrInstruction *ir_analyze_cast(IrAnalyze *ira, IrInstruction *source_inst return ira->codegen->invalid_instruction; } - if (wanted_type->id == TypeTableEntryIdVar) - return value; - // explicit match or non-const to const if (types_match_const_cast_only(ira, wanted_type, actual_type, source_node).id == ConstCastResultIdOk) { return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop, false); @@ -10311,9 +10303,6 @@ static TypeTableEntry *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstructionBinOp ir_add_error_node(ira, source_node, buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); return ira->codegen->builtin_types.entry_invalid; - - case TypeTableEntryIdVar: - zig_unreachable(); } IrInstruction *casted_op1 = ir_implicit_cast(ira, op1, resolved_type); @@ -11106,7 +11095,6 @@ static VarClassRequired get_var_class_required(TypeTableEntry *type_entry) { case TypeTableEntryIdInvalid: zig_unreachable(); case TypeTableEntryIdUnreachable: - case TypeTableEntryIdVar: return VarClassRequiredIllegal; case TypeTableEntryIdBool: case TypeTableEntryIdInt: @@ -11279,7 +11267,6 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi switch (target->value.type->id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: case TypeTableEntryIdUnreachable: zig_unreachable(); case TypeTableEntryIdFn: { @@ -11332,7 +11319,6 @@ static TypeTableEntry *ir_analyze_instruction_export(IrAnalyze *ira, IrInstructi TypeTableEntry *type_value = target->value.data.x_type; switch (type_value->id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: zig_unreachable(); case TypeTableEntryIdStruct: if (is_slice(type_value)) { @@ -11543,14 +11529,20 @@ static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node { AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(*next_proto_i); assert(param_decl_node->type == NodeTypeParamDecl); - AstNode *param_type_node = param_decl_node->data.param_decl.type; - TypeTableEntry *param_type = analyze_type_expr(ira->codegen, *exec_scope, param_type_node); - if (type_is_invalid(param_type)) - return false; - IrInstruction *casted_arg = ir_implicit_cast(ira, arg, param_type); - if (type_is_invalid(casted_arg->value.type)) - return false; + IrInstruction *casted_arg; + if (param_decl_node->data.param_decl.var_token == nullptr) { + AstNode *param_type_node = param_decl_node->data.param_decl.type; + TypeTableEntry *param_type = analyze_type_expr(ira->codegen, *exec_scope, param_type_node); + if (type_is_invalid(param_type)) + return false; + + casted_arg = ir_implicit_cast(ira, arg, param_type); + if (type_is_invalid(casted_arg->value.type)) + return false; + } else { + casted_arg = arg; + } ConstExprValue *arg_val = ir_resolve_const(ira, casted_arg, UndefBad); if (!arg_val) @@ -11579,19 +11571,18 @@ static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_nod arg_part_of_generic_id = true; casted_arg = ir_implicit_byval_const_ref_cast(ira, arg); } else { - AstNode *param_type_node = param_decl_node->data.param_decl.type; - TypeTableEntry *param_type = analyze_type_expr(ira->codegen, *child_scope, param_type_node); - if (type_is_invalid(param_type)) - return false; + if (param_decl_node->data.param_decl.var_token == nullptr) { + AstNode *param_type_node = param_decl_node->data.param_decl.type; + TypeTableEntry *param_type = analyze_type_expr(ira->codegen, *child_scope, param_type_node); + if (type_is_invalid(param_type)) + return false; - bool is_var_type = (param_type->id == TypeTableEntryIdVar); - if (is_var_type) { - arg_part_of_generic_id = true; - casted_arg = ir_implicit_byval_const_ref_cast(ira, arg); - } else { casted_arg = ir_implicit_cast(ira, arg, param_type); if (type_is_invalid(casted_arg->value.type)) return false; + } else { + arg_part_of_generic_id = true; + casted_arg = ir_implicit_byval_const_ref_cast(ira, arg); } } @@ -12304,7 +12295,6 @@ static TypeTableEntry *ir_analyze_maybe(IrAnalyze *ira, IrInstructionUnOp *un_op return ira->codegen->builtin_types.entry_invalid; switch (type_entry->id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: zig_unreachable(); case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: @@ -13539,10 +13529,6 @@ static TypeTableEntry *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstructi switch (type_entry->id) { case TypeTableEntryIdInvalid: zig_unreachable(); // handled above - case TypeTableEntryIdVar: - ir_add_error_node(ira, expr_value->source_node, - buf_sprintf("type '%s' not eligible for @typeOf", buf_ptr(&type_entry->name))); - return ira->codegen->builtin_types.entry_invalid; case TypeTableEntryIdNumLitFloat: case TypeTableEntryIdNumLitInt: case TypeTableEntryIdUndefLit: @@ -13807,7 +13793,6 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira, switch (child_type->id) { case TypeTableEntryIdInvalid: // handled above zig_unreachable(); - case TypeTableEntryIdVar: case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: @@ -13916,7 +13901,6 @@ static TypeTableEntry *ir_analyze_instruction_array_type(IrAnalyze *ira, switch (child_type->id) { case TypeTableEntryIdInvalid: // handled above zig_unreachable(); - case TypeTableEntryIdVar: case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: @@ -13968,7 +13952,6 @@ static TypeTableEntry *ir_analyze_instruction_size_of(IrAnalyze *ira, switch (type_entry->id) { case TypeTableEntryIdInvalid: // handled above zig_unreachable(); - case TypeTableEntryIdVar: case TypeTableEntryIdUnreachable: case TypeTableEntryIdUndefLit: case TypeTableEntryIdNullLit: @@ -14316,7 +14299,6 @@ static TypeTableEntry *ir_analyze_instruction_switch_target(IrAnalyze *ira, switch (target_type->id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: zig_unreachable(); case TypeTableEntryIdMetaType: case TypeTableEntryIdVoid: @@ -14911,7 +14893,6 @@ static TypeTableEntry *ir_analyze_min_max(IrAnalyze *ira, IrInstruction *source_ } case TypeTableEntryIdEnum: zig_panic("TODO min/max value for enum type"); - case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: case TypeTableEntryIdPointer: @@ -16159,7 +16140,6 @@ static TypeTableEntry *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstruc switch (type_entry->id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: zig_unreachable(); case TypeTableEntryIdMetaType: case TypeTableEntryIdUnreachable: @@ -16461,21 +16441,23 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc zig_unreachable(); } } - IrInstruction *param_type_value = instruction->param_types[fn_type_id.next_param_index]->other; - if (type_is_invalid(param_type_value->value.type)) - return ira->codegen->builtin_types.entry_invalid; - FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index]; param_info->is_noalias = param_node->data.param_decl.is_noalias; - param_info->type = ir_resolve_type(ira, param_type_value); - if (type_is_invalid(param_info->type)) - return ira->codegen->builtin_types.entry_invalid; - if (param_info->type->id == TypeTableEntryIdVar) { + if (instruction->param_types[fn_type_id.next_param_index] == nullptr) { + param_info->type = nullptr; ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = get_generic_fn_type(ira->codegen, &fn_type_id); return ira->codegen->builtin_types.entry_type; + } else { + IrInstruction *param_type_value = instruction->param_types[fn_type_id.next_param_index]->other; + if (type_is_invalid(param_type_value->value.type)) + return ira->codegen->builtin_types.entry_invalid; + param_info->type = ir_resolve_type(ira, param_type_value); + if (type_is_invalid(param_info->type)) + return ira->codegen->builtin_types.entry_invalid; } + } if (instruction->align_value != nullptr) { @@ -16869,7 +16851,6 @@ static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue assert(val->special == ConstValSpecialStatic); switch (val->type->id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: case TypeTableEntryIdOpaque: case TypeTableEntryIdBoundFn: @@ -16937,7 +16918,6 @@ static void buf_read_value_bytes(CodeGen *codegen, uint8_t *buf, ConstExprValue assert(val->special == ConstValSpecialStatic); switch (val->type->id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: case TypeTableEntryIdOpaque: case TypeTableEntryIdBoundFn: @@ -17014,7 +16994,6 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc switch (src_type->id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: case TypeTableEntryIdOpaque: case TypeTableEntryIdBoundFn: @@ -17041,7 +17020,6 @@ static TypeTableEntry *ir_analyze_instruction_bit_cast(IrAnalyze *ira, IrInstruc switch (dest_type->id) { case TypeTableEntryIdInvalid: - case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: case TypeTableEntryIdOpaque: case TypeTableEntryIdBoundFn: diff --git a/src/parser.cpp b/src/parser.cpp index 38994c79fc..738d19a1c1 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -263,21 +263,14 @@ static AstNode *ast_parse_error_set_expr(ParseContext *pc, size_t *token_index, } /* -TypeExpr = ErrorSetExpr | "var" +TypeExpr = ErrorSetExpr */ static AstNode *ast_parse_type_expr(ParseContext *pc, size_t *token_index, bool mandatory) { - Token *token = &pc->tokens->at(*token_index); - if (token->id == TokenIdKeywordVar) { - AstNode *node = ast_create_node(pc, NodeTypeVarLiteral, token); - *token_index += 1; - return node; - } else { - return ast_parse_error_set_expr(pc, token_index, mandatory); - } + return ast_parse_error_set_expr(pc, token_index, mandatory); } /* -ParamDecl = option("noalias" | "comptime") option(Symbol ":") (TypeExpr | "...") +ParamDecl = option("noalias" | "comptime") option(Symbol ":") (TypeExpr | "var" | "...") */ static AstNode *ast_parse_param_decl(ParseContext *pc, size_t *token_index) { Token *token = &pc->tokens->at(*token_index); @@ -308,6 +301,9 @@ static AstNode *ast_parse_param_decl(ParseContext *pc, size_t *token_index) { if (ellipsis_tok->id == TokenIdEllipsis3) { *token_index += 1; node->data.param_decl.is_var_args = true; + } else if (ellipsis_tok->id == TokenIdKeywordVar) { + *token_index += 1; + node->data.param_decl.var_token = ellipsis_tok; } else { node->data.param_decl.type = ast_parse_type_expr(pc, token_index, true); } @@ -2421,7 +2417,7 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand } /* -FnProto = option("nakedcc" | "stdcallcc" | "extern" | ("async" option("(" Expression ")"))) "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") TypeExpr +FnProto = option("nakedcc" | "stdcallcc" | "extern" | ("async" option("(" Expression ")"))) "fn" option(Symbol) ParamDeclList option("align" "(" Expression ")") option("section" "(" Expression ")") option("!") (TypeExpr | "var") */ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) { Token *first_token = &pc->tokens->at(*token_index); @@ -2507,19 +2503,25 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m ast_eat_token(pc, token_index, TokenIdRParen); next_token = &pc->tokens->at(*token_index); } - if (next_token->id == TokenIdKeywordError) { - Token *maybe_lbrace_tok = &pc->tokens->at(*token_index + 1); - if (maybe_lbrace_tok->id == TokenIdLBrace) { - *token_index += 1; - node->data.fn_proto.return_type = ast_create_node(pc, NodeTypeErrorType, next_token); - return node; - } - } else if (next_token->id == TokenIdBang) { + if (next_token->id == TokenIdKeywordVar) { + node->data.fn_proto.return_var_token = next_token; *token_index += 1; - node->data.fn_proto.auto_err_set = true; next_token = &pc->tokens->at(*token_index); + } else { + if (next_token->id == TokenIdKeywordError) { + Token *maybe_lbrace_tok = &pc->tokens->at(*token_index + 1); + if (maybe_lbrace_tok->id == TokenIdLBrace) { + *token_index += 1; + node->data.fn_proto.return_type = ast_create_node(pc, NodeTypeErrorType, next_token); + return node; + } + } else if (next_token->id == TokenIdBang) { + *token_index += 1; + node->data.fn_proto.auto_err_set = true; + next_token = &pc->tokens->at(*token_index); + } + node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, true); } - node->data.fn_proto.return_type = ast_parse_type_expr(pc, token_index, true); return node; } @@ -3069,9 +3071,6 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont case NodeTypeErrorType: // none break; - case NodeTypeVarLiteral: - // none - break; case NodeTypeAddrOfExpr: visit_field(&node->data.addr_of_expr.align_expr, visit, context); visit_field(&node->data.addr_of_expr.op_expr, visit, context); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 61234864bb..0ad6787552 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,13 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) void { + cases.add("var not allowed in structs", + \\export fn entry() void { + \\ var s = (struct{v: var}){.v=i32(10)}; + \\} + , + ".tmp_source.zig:2:23: error: invalid token: 'var'"); + cases.add("@ptrCast discards const qualifier", \\export fn entry() void { \\ const x: i32 = 1234; From 6b5cfd9d9963d2f1e91dfdb40f26c2ad11beb3c4 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 6 Mar 2018 20:41:49 -0500 Subject: [PATCH 15/21] turn assertion into compile error for using var as return type closes #758 --- src/analyze.cpp | 23 +++++++++++++---------- src/ir.cpp | 2 +- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 42bf2c8d4f..801852f2cf 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -1510,6 +1510,19 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c } } + if (fn_proto->return_var_token != nullptr) { + if (!calling_convention_allows_zig_types(fn_type_id.cc)) { + add_node_error(g, fn_proto->return_type, + buf_sprintf("return type 'var' not allowed in function with calling convention '%s'", + calling_convention_name(fn_type_id.cc))); + return g->builtin_types.entry_invalid; + } + add_node_error(g, proto_node, + buf_sprintf("TODO implement inferred return types https://github.com/zig-lang/zig/issues/447")); + return g->builtin_types.entry_invalid; + //return get_generic_fn_type(g, &fn_type_id); + } + TypeTableEntry *specified_return_type = analyze_type_expr(g, child_scope, fn_proto->return_type); if (type_is_invalid(specified_return_type)) { fn_type_id.return_type = g->builtin_types.entry_invalid; @@ -1523,16 +1536,6 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c fn_type_id.return_type = specified_return_type; } - if (fn_proto->return_var_token != nullptr) { - if (!calling_convention_allows_zig_types(fn_type_id.cc)) { - add_node_error(g, fn_proto->return_type, - buf_sprintf("return type 'var' not allowed in function with calling convention '%s'", - calling_convention_name(fn_type_id.cc))); - return g->builtin_types.entry_invalid; - } - return get_generic_fn_type(g, &fn_type_id); - } - if (!calling_convention_allows_zig_types(fn_type_id.cc) && !type_allowed_in_extern(g, fn_type_id.return_type)) { add_node_error(g, fn_proto->return_type, buf_sprintf("return type '%s' not allowed in function with calling convention '%s'", diff --git a/src/ir.cpp b/src/ir.cpp index 6fc1322c30..c52ab7388f 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -12019,7 +12019,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal inst_fn_type_id.alignment = align_bytes; } - { + if (fn_proto_node->data.fn_proto.return_var_token == nullptr) { AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; TypeTableEntry *specified_return_type = analyze_type_expr(ira->codegen, impl_fn->child_scope, return_type_node); if (type_is_invalid(specified_return_type)) From d96dd5bc329b69c410ef4d4def763ddb2bab13f0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 6 Mar 2018 21:44:27 -0500 Subject: [PATCH 16/21] fix missing compile error for returning error from void async function closes #799 --- src/analyze.cpp | 28 +++++++++++++++++----------- src/ir.cpp | 29 +++++++++++------------------ test/compile_errors.zig | 11 +++++++++++ 3 files changed, 39 insertions(+), 29 deletions(-) diff --git a/src/analyze.cpp b/src/analyze.cpp index 801852f2cf..66d2963425 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -464,9 +464,8 @@ TypeTableEntry *get_promise_frame_type(CodeGen *g, TypeTableEntry *return_type) TypeTableEntry *result_ptr_type = get_pointer_to_type(g, return_type, false); const char *field_names[] = {AWAITER_HANDLE_FIELD_NAME, RESULT_FIELD_NAME, RESULT_PTR_FIELD_NAME}; TypeTableEntry *field_types[] = {awaiter_handle_type, return_type, result_ptr_type}; - size_t field_count = type_has_bits(result_ptr_type) ? 3 : 1; Buf *name = buf_sprintf("AsyncFramePromise(%s)", buf_ptr(&return_type->name)); - TypeTableEntry *entry = get_struct_type(g, buf_ptr(name), field_names, field_types, field_count); + TypeTableEntry *entry = get_struct_type(g, buf_ptr(name), field_names, field_types, 3); return_type->promise_frame_parent = entry; return entry; @@ -1715,7 +1714,7 @@ TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *f buf_init_from_str(&struct_type->name, type_name); struct_type->data.structure.src_field_count = field_count; - struct_type->data.structure.gen_field_count = field_count; + struct_type->data.structure.gen_field_count = 0; struct_type->data.structure.zero_bits_known = true; struct_type->data.structure.complete = true; struct_type->data.structure.fields = allocate(field_count); @@ -1724,22 +1723,26 @@ TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *f ZigLLVMDIType **di_element_types = allocate(field_count); LLVMTypeRef *element_types = allocate(field_count); for (size_t i = 0; i < field_count; i += 1) { - element_types[i] = field_types[i]->type_ref; + element_types[struct_type->data.structure.gen_field_count] = field_types[i]->type_ref; TypeStructField *field = &struct_type->data.structure.fields[i]; field->name = buf_create_from_str(field_names[i]); field->type_entry = field_types[i]; field->src_index = i; - field->gen_index = i; - assert(type_has_bits(field->type_entry)); + if (type_has_bits(field->type_entry)) { + field->gen_index = struct_type->data.structure.gen_field_count; + struct_type->data.structure.gen_field_count += 1; + } else { + field->gen_index = SIZE_MAX; + } auto prev_entry = struct_type->data.structure.fields_by_name.put_unique(field->name, field); assert(prev_entry == nullptr); } struct_type->type_ref = LLVMStructCreateNamed(LLVMGetGlobalContext(), type_name); - LLVMStructSetBody(struct_type->type_ref, element_types, field_count, false); + LLVMStructSetBody(struct_type->type_ref, element_types, struct_type->data.structure.gen_field_count, false); struct_type->di_type = ZigLLVMCreateReplaceableCompositeType(g->dbuilder, ZigLLVMTag_DW_structure_type(), type_name, @@ -1747,11 +1750,14 @@ TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *f for (size_t i = 0; i < field_count; i += 1) { TypeStructField *type_struct_field = &struct_type->data.structure.fields[i]; + if (type_struct_field->gen_index == SIZE_MAX) { + continue; + } TypeTableEntry *field_type = type_struct_field->type_entry; uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, field_type->type_ref); uint64_t debug_align_in_bits = 8*LLVMABIAlignmentOfType(g->target_data_ref, field_type->type_ref); - uint64_t debug_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, struct_type->type_ref, i); - di_element_types[i] = ZigLLVMCreateDebugMemberType(g->dbuilder, + uint64_t debug_offset_in_bits = 8*LLVMOffsetOfElement(g->target_data_ref, struct_type->type_ref, type_struct_field->gen_index); + di_element_types[type_struct_field->gen_index] = ZigLLVMCreateDebugMemberType(g->dbuilder, ZigLLVMTypeToScope(struct_type->di_type), buf_ptr(type_struct_field->name), nullptr, 0, debug_size_in_bits, @@ -1759,7 +1765,7 @@ TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *f debug_offset_in_bits, 0, field_type->di_type); - assert(di_element_types[i]); + assert(di_element_types[type_struct_field->gen_index]); } uint64_t debug_size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, struct_type->type_ref); @@ -1770,7 +1776,7 @@ TypeTableEntry *get_struct_type(CodeGen *g, const char *type_name, const char *f debug_size_in_bits, debug_align_in_bits, 0, - nullptr, di_element_types, field_count, 0, nullptr, ""); + nullptr, di_element_types, struct_type->data.structure.gen_field_count, 0, nullptr, ""); ZigLLVMReplaceTemporary(g->dbuilder, struct_type->di_type, replacement_di_type); struct_type->di_type = replacement_di_type; diff --git a/src/ir.cpp b/src/ir.cpp index c52ab7388f..cdb8201b2b 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -948,12 +948,10 @@ static IrInstruction *ir_build_const_promise_init(IrBuilder *irb, Scope *scope, const_instruction->base.value.data.x_struct.fields[0].type = struct_type->data.structure.fields[0].type_entry; const_instruction->base.value.data.x_struct.fields[0].special = ConstValSpecialStatic; const_instruction->base.value.data.x_struct.fields[0].data.x_maybe = nullptr; - if (struct_type->data.structure.src_field_count > 1) { - const_instruction->base.value.data.x_struct.fields[1].type = return_type; - const_instruction->base.value.data.x_struct.fields[1].special = ConstValSpecialUndef; - const_instruction->base.value.data.x_struct.fields[2].type = struct_type->data.structure.fields[2].type_entry; - const_instruction->base.value.data.x_struct.fields[2].special = ConstValSpecialUndef; - } + const_instruction->base.value.data.x_struct.fields[1].type = return_type; + const_instruction->base.value.data.x_struct.fields[1].special = ConstValSpecialUndef; + const_instruction->base.value.data.x_struct.fields[2].type = struct_type->data.structure.fields[2].type_entry; + const_instruction->base.value.data.x_struct.fields[2].special = ConstValSpecialUndef; return &const_instruction->base; } @@ -2741,10 +2739,8 @@ static IrInstruction *ir_gen_async_return(IrBuilder *irb, Scope *scope, AstNode return return_inst; } - if (irb->exec->coro_result_ptr_field_ptr) { - IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr); - ir_build_store_ptr(irb, scope, node, result_ptr, return_value); - } + IrInstruction *result_ptr = ir_build_load_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr); + ir_build_store_ptr(irb, scope, node, result_ptr, return_value); IrInstruction *promise_type_val = ir_build_const_type(irb, scope, node, get_maybe_type(irb->codegen, irb->codegen->builtin_types.entry_promise)); // TODO replace replacement_value with @intToPtr(?promise, 0x1) when it doesn't crash zig @@ -6328,14 +6324,11 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec Buf *awaiter_handle_field_name = buf_create_from_str(AWAITER_HANDLE_FIELD_NAME); irb->exec->coro_awaiter_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, awaiter_handle_field_name); - if (type_has_bits(return_type)) { - Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); - coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); - result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); - irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, - result_ptr_field_name); - ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, coro_result_field_ptr); - } + Buf *result_field_name = buf_create_from_str(RESULT_FIELD_NAME); + coro_result_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_field_name); + result_ptr_field_name = buf_create_from_str(RESULT_PTR_FIELD_NAME); + irb->exec->coro_result_ptr_field_ptr = ir_build_field_ptr(irb, scope, node, coro_promise_ptr, result_ptr_field_name); + ir_build_store_ptr(irb, scope, node, irb->exec->coro_result_ptr_field_ptr, coro_result_field_ptr); irb->exec->coro_early_final = ir_create_basic_block(irb, scope, "CoroEarlyFinal"); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 0ad6787552..1bca093e79 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,17 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) void { + cases.add("returning error from void async function", + \\const std = @import("std"); + \\export fn entry() void { + \\ const p = async(std.debug.global_allocator) amain() catch unreachable; + \\} + \\async fn amain() void { + \\ return error.ShouldBeCompileError; + \\} + , + ".tmp_source.zig:6:17: error: expected type 'void', found 'error{ShouldBeCompileError}'"); + cases.add("var not allowed in structs", \\export fn entry() void { \\ var s = (struct{v: var}){.v=i32(10)}; From bb80daf509b7afd84fe3076347e69e8f39f586d6 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Wed, 7 Mar 2018 10:39:32 +0100 Subject: [PATCH 17/21] Ast Render no longer outputs erroneous semicolon closes #813 --- src/ast_render.cpp | 5 ++++- src/parser.cpp | 2 +- src/parser.hpp | 2 ++ test/translate_c.zig | 30 +++++++++++++++--------------- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/src/ast_render.cpp b/src/ast_render.cpp index ef1fe6e231..f88feee856 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -490,7 +490,10 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) { AstNode *statement = node->data.block.statements.at(i); print_indent(ar); render_node_grouped(ar, statement); - fprintf(ar->f, ";"); + + if (!statement_terminates_without_semicolon(statement)) + fprintf(ar->f, ";"); + fprintf(ar->f, "\n"); } ar->indent -= ar->indent_size; diff --git a/src/parser.cpp b/src/parser.cpp index 738d19a1c1..c7675ad67d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2315,7 +2315,7 @@ static AstNode *ast_parse_expression(ParseContext *pc, size_t *token_index, bool return nullptr; } -static bool statement_terminates_without_semicolon(AstNode *node) { +bool statement_terminates_without_semicolon(AstNode *node) { switch (node->type) { case NodeTypeIfBoolExpr: if (node->data.if_bool_expr.else_node) diff --git a/src/parser.hpp b/src/parser.hpp index 6e9f002997..21922db4ed 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -23,4 +23,6 @@ void ast_print(AstNode *node, int indent); void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *context), void *context); +bool statement_terminates_without_semicolon(AstNode *node); + #endif diff --git a/test/translate_c.zig b/test/translate_c.zig index e1aa648d73..8073e29151 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -351,7 +351,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ var i: c_int = 0; \\ while (a > c_uint(0)) { \\ a >>= @import("std").math.Log2Int(c_uint)(1); - \\ }; + \\ } \\ return i; \\} ); @@ -498,7 +498,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ var i: c_int = 0; \\ while (a > c_uint(0)) { \\ a >>= u5(1); - \\ }; + \\ } \\ return i; \\} ); @@ -867,12 +867,12 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ while (true) { \\ a -= 1; \\ if (!(a != 0)) break; - \\ }; + \\ } \\ var b: c_int = 2; \\ while (true) { \\ b -= 1; \\ if (!(b != 0)) break; - \\ }; + \\ } \\} ); @@ -962,8 +962,8 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\pub fn foo() void { \\ { \\ var i: c_int = 0; - \\ while (i < 10) : (i += 1) {}; - \\ }; + \\ while (i < 10) : (i += 1) {} + \\ } \\} ); @@ -973,7 +973,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\} , \\pub fn foo() void { - \\ while (true) {}; + \\ while (true) {} \\} ); @@ -987,7 +987,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\pub fn foo() void { \\ while (true) { \\ break; - \\ }; + \\ } \\} ); @@ -1001,7 +1001,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\pub fn foo() void { \\ while (true) { \\ continue; - \\ }; + \\ } \\} ); @@ -1058,7 +1058,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ { \\ var x_0: c_int = 2; \\ x_0 += 1; - \\ }; + \\ } \\ return x; \\} ); @@ -1147,7 +1147,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ return 0; \\ } else { \\ return 1; - \\ }; + \\ } \\} ); @@ -1164,7 +1164,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0; \\ }) { \\ return 0; - \\ }; + \\ } \\} ); @@ -1185,7 +1185,7 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0; \\ }) { \\ return 0; - \\ }; + \\ } \\ { \\ var j: c_int = 4; \\ while (__to_bool_expr: { @@ -1193,8 +1193,8 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0; \\ }) : (j -= 1) { \\ return 0; - \\ }; - \\ }; + \\ } + \\ } \\} ); } From 790aaeacaea88782987c4145bc7ae47a401563f1 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 7 Mar 2018 14:35:48 -0500 Subject: [PATCH 18/21] add compile error for using @tagName on extern union closes #742 --- src/all_types.hpp | 2 ++ src/analyze.cpp | 2 ++ src/ir.cpp | 8 ++++++++ test/compile_errors.zig | 13 +++++++++++++ 4 files changed, 25 insertions(+) diff --git a/src/all_types.hpp b/src/all_types.hpp index 6f36d34bfd..3d732d4ace 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1096,6 +1096,8 @@ struct TypeTableEntryUnion { size_t gen_union_index; size_t gen_tag_index; + bool have_explicit_tag_type; + uint32_t union_size_bytes; TypeTableEntry *most_aligned_union_member; diff --git a/src/analyze.cpp b/src/analyze.cpp index 66d2963425..74dfd003d9 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -2558,6 +2558,8 @@ static void resolve_union_zero_bits(CodeGen *g, TypeTableEntry *union_type) { HashMap occupied_tag_values = {}; AstNode *enum_type_node = decl_node->data.container_decl.init_arg_expr; + union_type->data.unionation.have_explicit_tag_type = decl_node->data.container_decl.auto_enum || + enum_type_node != nullptr; bool auto_layout = (union_type->data.unionation.layout == ContainerLayoutAuto); bool want_safety = (field_count >= 2) && (auto_layout || enum_type_node != nullptr); TypeTableEntry *tag_type; diff --git a/src/ir.cpp b/src/ir.cpp index cdb8201b2b..811744e9d0 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -14137,6 +14137,14 @@ static IrInstruction *ir_analyze_union_tag(IrAnalyze *ira, IrInstruction *source buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value.type->name))); return ira->codegen->invalid_instruction; } + if (!value->value.type->data.unionation.have_explicit_tag_type && !source_instr->is_gen) { + ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("union has no associated enum")); + if (value->value.type->data.unionation.decl_node != nullptr) { + add_error_note(ira->codegen, msg, value->value.type->data.unionation.decl_node, + buf_sprintf("declared here")); + } + return ira->codegen->invalid_instruction; + } TypeTableEntry *tag_type = value->value.type->data.unionation.tag_type; assert(tag_type->id == TypeTableEntryIdEnum); diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 1bca093e79..564ad5d521 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -1,6 +1,19 @@ const tests = @import("tests.zig"); pub fn addCases(cases: &tests.CompileErrorContext) void { + cases.add("@tagName used on union with no associated enum tag", + \\const FloatInt = extern union { + \\ Float: f32, + \\ Int: i32, + \\}; + \\export fn entry() void { + \\ var fi = FloatInt{.Float = 123.45}; + \\ var tagName = @tagName(fi); + \\} + , + ".tmp_source.zig:7:19: error: union has no associated enum", + ".tmp_source.zig:1:18: note: declared here"); + cases.add("returning error from void async function", \\const std = @import("std"); \\export fn entry() void { From 51b2f1b80b9bc43dc389044565cc3a72c174311e Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Thu, 8 Mar 2018 10:29:29 +0100 Subject: [PATCH 19/21] Translate C can now translate switch statements again --- src/translate_c.cpp | 171 +++++++++++++++++++++++++++++++++++++++++-- test/translate_c.zig | 42 +++++++++++ 2 files changed, 206 insertions(+), 7 deletions(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 183f3928e0..2a0a31af16 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -104,6 +104,7 @@ static TransScopeRoot *trans_scope_root_create(Context *c); static TransScopeWhile *trans_scope_while_create(Context *c, TransScope *parent_scope); static TransScopeBlock *trans_scope_block_create(Context *c, TransScope *parent_scope); static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scope, Buf *wanted_name); +static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope); static TransScopeBlock *trans_scope_block_find(TransScope *scope); @@ -2527,6 +2528,155 @@ static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForSt return loop_block_node; } +static AstNode *trans_switch_stmt(Context *c, TransScope *parent_scope, const SwitchStmt *stmt) { + TransScopeBlock *block_scope = trans_scope_block_create(c, parent_scope); + + TransScopeSwitch *switch_scope; + + const DeclStmt *var_decl_stmt = stmt->getConditionVariableDeclStmt(); + if (var_decl_stmt == nullptr) { + switch_scope = trans_scope_switch_create(c, &block_scope->base); + } else { + AstNode *vars_node; + TransScope *var_scope = trans_stmt(c, &block_scope->base, var_decl_stmt, &vars_node); + if (var_scope == nullptr) + return nullptr; + if (vars_node != nullptr) + block_scope->node->data.block.statements.append(vars_node); + switch_scope = trans_scope_switch_create(c, var_scope); + } + block_scope->node->data.block.statements.append(switch_scope->switch_node); + + // TODO avoid name collisions + Buf *end_label_name = buf_create_from_str("__switch"); + switch_scope->end_label_name = end_label_name; + block_scope->node->data.block.name = end_label_name; + + const Expr *cond_expr = stmt->getCond(); + assert(cond_expr != nullptr); + + AstNode *expr_node = trans_expr(c, ResultUsedYes, &block_scope->base, cond_expr, TransRValue); + if (expr_node == nullptr) + return nullptr; + switch_scope->switch_node->data.switch_expr.expr = expr_node; + + AstNode *body_node; + const Stmt *body_stmt = stmt->getBody(); + if (body_stmt->getStmtClass() == Stmt::CompoundStmtClass) { + if (trans_compound_stmt_inline(c, &switch_scope->base, (const CompoundStmt *)body_stmt, + block_scope->node, nullptr)) + { + return nullptr; + } + } else { + TransScope *body_scope = trans_stmt(c, &switch_scope->base, body_stmt, &body_node); + if (body_scope == nullptr) + return nullptr; + if (body_node != nullptr) + block_scope->node->data.block.statements.append(body_node); + } + + if (!switch_scope->found_default && !stmt->isAllEnumCasesCovered()) { + AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng); + prong_node->data.switch_prong.expr = trans_create_node_break(c, end_label_name, nullptr); + switch_scope->switch_node->data.switch_expr.prongs.append(prong_node); + } + + return block_scope->node; +} + +static TransScopeSwitch *trans_scope_switch_find(TransScope *scope) { + while (scope != nullptr) { + if (scope->id == TransScopeIdSwitch) { + return (TransScopeSwitch *)scope; + } + scope = scope->parent; + } + return nullptr; +} + +static int trans_switch_case(Context *c, TransScope *parent_scope, const CaseStmt *stmt, AstNode **out_node, + TransScope **out_scope) { + *out_node = nullptr; + + if (stmt->getRHS() != nullptr) { + emit_warning(c, stmt->getLocStart(), "TODO support GNU switch case a ... b extension"); + return ErrorUnexpected; + } + + TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope); + assert(switch_scope != nullptr); + + Buf *label_name = buf_sprintf("__case_%" PRIu32, switch_scope->case_index); + switch_scope->case_index += 1; + + { + // Add the prong + AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng); + AstNode *item_node = trans_expr(c, ResultUsedYes, &switch_scope->base, stmt->getLHS(), TransRValue); + if (item_node == nullptr) + return ErrorUnexpected; + prong_node->data.switch_prong.items.append(item_node); + prong_node->data.switch_prong.expr = trans_create_node_break(c, label_name, nullptr); + switch_scope->switch_node->data.switch_expr.prongs.append(prong_node); + } + + TransScopeBlock *scope_block = trans_scope_block_find(parent_scope); + + AstNode *case_block = trans_create_node(c, NodeTypeBlock); + case_block->data.block.name = label_name; + case_block->data.block.statements = scope_block->node->data.block.statements; + scope_block->node->data.block.statements = {0}; + scope_block->node->data.block.statements.append(case_block); + + AstNode *sub_stmt_node; + TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node); + if (new_scope == nullptr) + return ErrorUnexpected; + if (sub_stmt_node != nullptr) + scope_block->node->data.block.statements.append(sub_stmt_node); + + *out_scope = new_scope; + return ErrorNone; +} + +static int trans_switch_default(Context *c, TransScope *parent_scope, const DefaultStmt *stmt, AstNode **out_node, + TransScope **out_scope) +{ + *out_node = nullptr; + + TransScopeSwitch *switch_scope = trans_scope_switch_find(parent_scope); + assert(switch_scope != nullptr); + + Buf *label_name = buf_sprintf("__default"); + + { + // Add the prong + AstNode *prong_node = trans_create_node(c, NodeTypeSwitchProng); + prong_node->data.switch_prong.expr = trans_create_node_break(c, label_name, nullptr); + switch_scope->switch_node->data.switch_expr.prongs.append(prong_node); + switch_scope->found_default = true; + } + + TransScopeBlock *scope_block = trans_scope_block_find(parent_scope); + + AstNode *case_block = trans_create_node(c, NodeTypeBlock); + case_block->data.block.name = label_name; + case_block->data.block.statements = scope_block->node->data.block.statements; + scope_block->node->data.block.statements = {0}; + scope_block->node->data.block.statements.append(case_block); + + AstNode *sub_stmt_node; + TransScope *new_scope = trans_stmt(c, parent_scope, stmt->getSubStmt(), &sub_stmt_node); + if (new_scope == nullptr) + return ErrorUnexpected; + if (sub_stmt_node != nullptr) + scope_block->node->data.block.statements.append(sub_stmt_node); + + *out_scope = new_scope; + return ErrorNone; +} + static AstNode *trans_string_literal(Context *c, TransScope *scope, const StringLiteral *stmt) { switch (stmt->getKind()) { case StringLiteral::Ascii: @@ -2551,7 +2701,8 @@ static AstNode *trans_break_stmt(Context *c, TransScope *scope, const BreakStmt if (cur_scope->id == TransScopeIdWhile) { return trans_create_node(c, NodeTypeBreak); } else if (cur_scope->id == TransScopeIdSwitch) { - zig_panic("TODO"); + TransScopeSwitch *switch_scope = (TransScopeSwitch *)cur_scope; + return trans_create_node_break(c, switch_scope->end_label_name, nullptr); } cur_scope = cur_scope->parent; } @@ -2651,14 +2802,12 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, return wrap_stmt(out_node, out_child_scope, scope, trans_expr(c, result_used, scope, ((const ParenExpr*)stmt)->getSubExpr(), lrvalue)); case Stmt::SwitchStmtClass: - emit_warning(c, stmt->getLocStart(), "TODO handle C SwitchStmtClass"); - return ErrorUnexpected; + return wrap_stmt(out_node, out_child_scope, scope, + trans_switch_stmt(c, scope, (const SwitchStmt *)stmt)); case Stmt::CaseStmtClass: - emit_warning(c, stmt->getLocStart(), "TODO handle C CaseStmtClass"); - return ErrorUnexpected; + return trans_switch_case(c, scope, (const CaseStmt *)stmt, out_node, out_child_scope); case Stmt::DefaultStmtClass: - emit_warning(c, stmt->getLocStart(), "TODO handle C DefaultStmtClass"); - return ErrorUnexpected; + return trans_switch_default(c, scope, (const DefaultStmt *)stmt, out_node, out_child_scope); case Stmt::NoStmtClass: emit_warning(c, stmt->getLocStart(), "TODO handle C NoStmtClass"); return ErrorUnexpected; @@ -3828,6 +3977,14 @@ static TransScopeVar *trans_scope_var_create(Context *c, TransScope *parent_scop return result; } +static TransScopeSwitch *trans_scope_switch_create(Context *c, TransScope *parent_scope) { + TransScopeSwitch *result = allocate(1); + result->base.id = TransScopeIdSwitch; + result->base.parent = parent_scope; + result->switch_node = trans_create_node(c, NodeTypeSwitchExpr); + return result; +} + static TransScopeBlock *trans_scope_block_find(TransScope *scope) { while (scope != nullptr) { if (scope->id == TransScopeIdBlock) { diff --git a/test/translate_c.zig b/test/translate_c.zig index 8073e29151..74bf5a1cb5 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -1197,4 +1197,46 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\ } \\} ); + + cases.add("for on int", + \\int switch_fn(int i) { + \\ int res = 0; + \\ switch (i) { + \\ case 0: + \\ res = 1; + \\ case 1: + \\ res = 2; + \\ default: + \\ res = 3 * i; + \\ break; + \\ case 2: + \\ res = 5; + \\ } + \\} + , + \\pub fn switch_fn(i: c_int) c_int { + \\ var res: c_int = 0; + \\ __switch: { + \\ __case_2: { + \\ __default: { + \\ __case_1: { + \\ __case_0: { + \\ switch (i) { + \\ 0 => break :__case_0, + \\ 1 => break :__case_1, + \\ else => break :__default, + \\ 2 => break :__case_2, + \\ } + \\ } + \\ res = 1; + \\ } + \\ res = 2; + \\ } + \\ res = (3 * i); + \\ break :__switch; + \\ } + \\ res = 5; + \\ } + \\} + ); } From b2887620f39d756b9f73a93b8b0932b3e471d996 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Thu, 8 Mar 2018 13:15:30 +0100 Subject: [PATCH 20/21] Translate C will now handle ignored return values --- src/translate_c.cpp | 40 +++++++++++++++++++++++++++++++--------- test/translate_c.zig | 26 ++++++++++++++++++++++---- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index 2a0a31af16..c6a25271b8 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -500,15 +500,31 @@ static bool qual_type_is_ptr(QualType qt) { return ty->getTypeClass() == Type::Pointer; } -static bool qual_type_is_fn_ptr(Context *c, QualType qt) { +static const FunctionProtoType *qual_type_get_fn_proto(QualType qt, bool *is_ptr) { const Type *ty = qual_type_canon(qt); - if (ty->getTypeClass() != Type::Pointer) { - return false; + *is_ptr = false; + + if (ty->getTypeClass() == Type::Pointer) { + *is_ptr = true; + const PointerType *pointer_ty = static_cast(ty); + QualType child_qt = pointer_ty->getPointeeType(); + ty = child_qt.getTypePtr(); } - const PointerType *pointer_ty = static_cast(ty); - QualType child_qt = pointer_ty->getPointeeType(); - const Type *child_ty = child_qt.getTypePtr(); - return child_ty->getTypeClass() == Type::FunctionProto; + + if (ty->getTypeClass() == Type::FunctionProto) { + return static_cast(ty); + } + + return nullptr; +} + +static bool qual_type_is_fn_ptr(QualType qt) { + bool is_ptr; + if (qual_type_get_fn_proto(qt, &is_ptr)) { + return is_ptr; + } + + return false; } static uint32_t qual_type_int_bit_width(Context *c, const QualType &qt, const SourceLocation &source_loc) { @@ -1871,7 +1887,7 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc AstNode *value_node = trans_expr(c, result_used, scope, stmt->getSubExpr(), TransRValue); if (value_node == nullptr) return nullptr; - bool is_fn_ptr = qual_type_is_fn_ptr(c, stmt->getSubExpr()->getType()); + bool is_fn_ptr = qual_type_is_fn_ptr(stmt->getSubExpr()->getType()); if (is_fn_ptr) return value_node; AstNode *unwrapped = trans_create_node_prefix_op(c, PrefixOpUnwrapMaybe, value_node); @@ -2327,8 +2343,10 @@ static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope * if (callee_raw_node == nullptr) return nullptr; + bool is_ptr = false; + const FunctionProtoType *fn_ty = qual_type_get_fn_proto(stmt->getCallee()->getType(), &is_ptr); AstNode *callee_node = nullptr; - if (qual_type_is_fn_ptr(c, stmt->getCallee()->getType())) { + if (is_ptr && fn_ty) { if (stmt->getCallee()->getStmtClass() == Stmt::ImplicitCastExprClass) { const ImplicitCastExpr *implicit_cast = static_cast(stmt->getCallee()); if (implicit_cast->getCastKind() == CK_FunctionToPointerDecay) { @@ -2360,6 +2378,10 @@ static AstNode *trans_call_expr(Context *c, ResultUsed result_used, TransScope * node->data.fn_call_expr.params.append(arg_node); } + if (result_used == ResultUsedNo && fn_ty && !qual_type_canon(fn_ty->getReturnType())->isVoidType()) { + node = trans_create_node_bin_op(c, trans_create_node_symbol_str(c, "_"), BinOpTypeAssign, node); + } + return node; } diff --git a/test/translate_c.zig b/test/translate_c.zig index 74bf5a1cb5..0cfefe79ab 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -515,11 +515,19 @@ pub fn addCases(cases: &tests.TranslateCContext) void { cases.addC("function call", \\static void bar(void) { } - \\void foo(void) { bar(); } + \\static int baz(void) { return 0; } + \\void foo(void) { + \\ bar(); + \\ baz(); + \\} , \\pub fn bar() void {} + \\pub fn baz() c_int { + \\ return 0; + \\} \\pub export fn foo() void { \\ bar(); + \\ _ = baz(); \\} ); @@ -878,21 +886,31 @@ pub fn addCases(cases: &tests.TranslateCContext) void { cases.addC("deref function pointer", \\void foo(void) {} - \\void baz(void) {} + \\int baz(void) { return 0; } \\void bar(void) { \\ void(*f)(void) = foo; + \\ int(*b)(void) = baz; \\ f(); \\ (*(f))(); + \\ foo(); + \\ b(); + \\ (*(b))(); \\ baz(); \\} , \\pub export fn foo() void {} - \\pub export fn baz() void {} + \\pub export fn baz() c_int { + \\ return 0; + \\} \\pub export fn bar() void { \\ var f: ?extern fn() void = foo; + \\ var b: ?extern fn() c_int = baz; \\ (??f)(); \\ (??f)(); - \\ baz(); + \\ foo(); + \\ _ = (??b)(); + \\ _ = (??b)(); + \\ _ = baz(); \\} ); From 2e010c60ae006944ae20ab8b3445598471c9f1e8 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Thu, 8 Mar 2018 15:34:00 +0100 Subject: [PATCH 21/21] Translate C now correctly converts ints, floats, ptrs and enums to bools * Boolean "and" and "or" should also work with these types. * This new method also simplifies to output code. --- src/translate_c.cpp | 337 +++++++++++++++++++++++++++++++++---------- test/translate_c.zig | 146 ++++++++++--------- 2 files changed, 339 insertions(+), 144 deletions(-) diff --git a/src/translate_c.cpp b/src/translate_c.cpp index c6a25271b8..21a085ebba 100644 --- a/src/translate_c.cpp +++ b/src/translate_c.cpp @@ -119,7 +119,7 @@ static int trans_stmt_extra(Context *c, TransScope *scope, const Stmt *stmt, static TransScope *trans_stmt(Context *c, TransScope *scope, const Stmt *stmt, AstNode **out_node); static AstNode *trans_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr, TransLRValue lrval); static AstNode *trans_qual_type(Context *c, QualType qt, const SourceLocation &source_loc); -static AstNode *trans_to_bool_expr(Context *c, TransScope *scope, AstNode *expr); +static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr, TransLRValue lrval); ATTRIBUTE_PRINTF(3, 4) static void emit_warning(Context *c, const SourceLocation &sl, const char *format, ...) { @@ -467,6 +467,14 @@ static QualType get_expr_qual_type(Context *c, const Expr *expr) { return expr->getType(); } +static QualType get_expr_qual_type_before_implicit_cast(Context *c, const Expr *expr) { + if (expr->getStmtClass() == Stmt::ImplicitCastExprClass) { + const ImplicitCastExpr *cast_expr = static_cast(expr); + return get_expr_qual_type(c, cast_expr->getSubExpr()); + } + return expr->getType(); +} + static AstNode *get_expr_type(Context *c, const Expr *expr) { return trans_qual_type(c, get_expr_qual_type(c, expr), expr->getLocStart()); } @@ -1152,6 +1160,22 @@ static AstNode *trans_create_bin_op(Context *c, TransScope *scope, Expr *lhs, Bi return node; } +static AstNode *trans_create_bool_bin_op(Context *c, TransScope *scope, Expr *lhs, BinOpType bin_op, Expr *rhs) { + assert(bin_op == BinOpTypeBoolAnd || bin_op == BinOpTypeBoolOr); + AstNode *node = trans_create_node(c, NodeTypeBinOpExpr); + node->data.bin_op_expr.bin_op = bin_op; + + node->data.bin_op_expr.op1 = trans_bool_expr(c, ResultUsedYes, scope, lhs, TransRValue); + if (node->data.bin_op_expr.op1 == nullptr) + return nullptr; + + node->data.bin_op_expr.op2 = trans_bool_expr(c, ResultUsedYes, scope, rhs, TransRValue); + if (node->data.bin_op_expr.op2 == nullptr) + return nullptr; + + return node; +} + static AstNode *trans_create_assign(Context *c, ResultUsed result_used, TransScope *scope, Expr *lhs, Expr *rhs) { if (result_used == ResultUsedNo) { // common case @@ -1293,10 +1317,9 @@ static AstNode *trans_binary_operator(Context *c, ResultUsed result_used, TransS case BO_Or: return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBinOr, stmt->getRHS()); case BO_LAnd: - return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBoolAnd, stmt->getRHS()); + return trans_create_bool_bin_op(c, scope, stmt->getLHS(), BinOpTypeBoolAnd, stmt->getRHS()); case BO_LOr: - // TODO: int vs bool - return trans_create_bin_op(c, scope, stmt->getLHS(), BinOpTypeBoolOr, stmt->getRHS()); + return trans_create_bool_bin_op(c, scope, stmt->getLHS(), BinOpTypeBoolOr, stmt->getRHS()); case BO_Assign: return trans_create_assign(c, result_used, scope, stmt->getLHS(), stmt->getRHS()); case BO_Comma: @@ -1924,7 +1947,6 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc return nullptr; } } - case UO_LNot: case UO_Not: { Expr *op_expr = stmt->getSubExpr(); @@ -1932,14 +1954,16 @@ static AstNode *trans_unary_operator(Context *c, ResultUsed result_used, TransSc if (sub_node == nullptr) return nullptr; - switch (stmt->getOpcode()) { - case UO_LNot: - return trans_create_node_prefix_op(c, PrefixOpBoolNot, trans_to_bool_expr(c, scope, sub_node)); - case UO_Not: - return trans_create_node_prefix_op(c, PrefixOpBinNot, sub_node); - default: - zig_unreachable(); - } + return trans_create_node_prefix_op(c, PrefixOpBinNot, sub_node); + } + case UO_LNot: + { + Expr *op_expr = stmt->getSubExpr(); + AstNode *sub_node = trans_bool_expr(c, ResultUsedYes, scope, op_expr, TransRValue); + if (sub_node == nullptr) + return nullptr; + + return trans_create_node_prefix_op(c, PrefixOpBoolNot, sub_node); } case UO_Real: emit_warning(c, stmt->getLocStart(), "TODO handle C translation UO_Real"); @@ -2220,10 +2244,30 @@ static int trans_local_declaration(Context *c, TransScope *scope, const DeclStmt return ErrorNone; } -static AstNode *trans_to_bool_expr(Context *c, TransScope *scope, AstNode *expr) { - switch (expr->type) { +static AstNode *to_enum_zero_cmp(Context *c, AstNode *expr, AstNode *enum_type) { + AstNode *tag_type = trans_create_node_builtin_fn_call_str(c, "TagType"); + tag_type->data.fn_call_expr.params.append(enum_type); + + // @TagType(Enum)(0) + AstNode *zero = trans_create_node_unsigned_negative(c, 0, false); + AstNode *casted_zero = trans_create_node_fn_call_1(c, tag_type, zero); + + // @bitCast(Enum, @TagType(Enum)(0)) + AstNode *bitcast = trans_create_node_builtin_fn_call_str(c, "bitCast"); + bitcast->data.fn_call_expr.params.append(enum_type); + bitcast->data.fn_call_expr.params.append(casted_zero); + + return trans_create_node_bin_op(c, expr, BinOpTypeCmpNotEq, bitcast); +} + +static AstNode *trans_bool_expr(Context *c, ResultUsed result_used, TransScope *scope, const Expr *expr, TransLRValue lrval) { + AstNode *res = trans_expr(c, result_used, scope, expr, lrval); + if (res == nullptr) + return nullptr; + + switch (res->type) { case NodeTypeBinOpExpr: - switch (expr->data.bin_op_expr.bin_op) { + switch (res->data.bin_op_expr.bin_op) { case BinOpTypeBoolOr: case BinOpTypeBoolAnd: case BinOpTypeCmpEq: @@ -2232,76 +2276,208 @@ static AstNode *trans_to_bool_expr(Context *c, TransScope *scope, AstNode *expr) case BinOpTypeCmpGreaterThan: case BinOpTypeCmpLessOrEq: case BinOpTypeCmpGreaterOrEq: - return expr; + return res; default: - goto convert_to_bitcast; + break; } case NodeTypePrefixOpExpr: - switch (expr->data.prefix_op_expr.prefix_op) { + switch (res->data.prefix_op_expr.prefix_op) { case PrefixOpBoolNot: - return expr; + return res; default: - goto convert_to_bitcast; + break; } case NodeTypeBoolLiteral: - return expr; + return res; - default: { - // In Zig, float, int and pointer does not implicitly cast to bool. - // To make it work, we bitcast any value we get to an int of the right size - // and comp it to 0 - // TODO: This doesn't work for pointers, as they become nullable on - // translate - // c: expr - // zig: __to_bool_expr: { - // zig: const _tmp = cond; - // zig: break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0; - // zig: } - convert_to_bitcast: - TransScopeBlock *child_scope = trans_scope_block_create(c, scope); - Buf *label_name = buf_create_from_str("__to_bool_expr"); - child_scope->node->data.block.name = label_name; - - // const _tmp = cond; - // TODO: avoid name collisions with generated variable names - Buf *tmp_var_name = buf_create_from_str("_tmp"); - AstNode *tmp_var_decl = trans_create_node_var_decl_local(c, true, tmp_var_name, nullptr, expr); - child_scope->node->data.block.statements.append(tmp_var_decl); - - // @sizeOf(@typeOf(_tmp)) * 8 - AstNode *typeof_tmp = trans_create_node_builtin_fn_call_str(c, "typeOf"); - typeof_tmp->data.fn_call_expr.params.append(trans_create_node_symbol(c, tmp_var_name)); - AstNode *sizeof_tmp = trans_create_node_builtin_fn_call_str(c, "sizeOf"); - sizeof_tmp->data.fn_call_expr.params.append(typeof_tmp); - AstNode *sizeof_tmp_in_bits = trans_create_node_bin_op( - c, sizeof_tmp, BinOpTypeMult, - trans_create_node_unsigned_negative(c, 8, false)); - - // @IntType(false, @sizeOf(@typeOf(_tmp)) * 8) - AstNode *int_type = trans_create_node_builtin_fn_call_str(c, "IntType"); - int_type->data.fn_call_expr.params.append(trans_create_node_bool(c, false)); - int_type->data.fn_call_expr.params.append(sizeof_tmp_in_bits); - - // @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) - AstNode *bit_cast = trans_create_node_builtin_fn_call_str(c, "bitCast"); - bit_cast->data.fn_call_expr.params.append(int_type); - bit_cast->data.fn_call_expr.params.append(trans_create_node_symbol(c, tmp_var_name)); - - // break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0 - AstNode *not_eql_zero = trans_create_node_bin_op(c, bit_cast, BinOpTypeCmpNotEq, trans_create_node_unsigned_negative(c, 0, false)); - child_scope->node->data.block.statements.append(trans_create_node_break(c, label_name, not_eql_zero)); - - return child_scope->node; - } + default: + break; } + + + const Type *ty = get_expr_qual_type_before_implicit_cast(c, expr).getTypePtr(); + auto classs = ty->getTypeClass(); + switch (classs) { + case Type::Builtin: + { + const BuiltinType *builtin_ty = static_cast(ty); + switch (builtin_ty->getKind()) { + case BuiltinType::Bool: + case BuiltinType::Char_U: + case BuiltinType::UChar: + case BuiltinType::Char_S: + case BuiltinType::SChar: + case BuiltinType::UShort: + case BuiltinType::UInt: + case BuiltinType::ULong: + case BuiltinType::ULongLong: + case BuiltinType::Short: + case BuiltinType::Int: + case BuiltinType::Long: + case BuiltinType::LongLong: + case BuiltinType::UInt128: + case BuiltinType::Int128: + case BuiltinType::Float: + case BuiltinType::Double: + case BuiltinType::Float128: + case BuiltinType::LongDouble: + case BuiltinType::WChar_U: + case BuiltinType::Char16: + case BuiltinType::Char32: + case BuiltinType::WChar_S: + return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node_unsigned_negative(c, 0, false)); + case BuiltinType::NullPtr: + return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node(c, NodeTypeNullLiteral)); + + case BuiltinType::Void: + case BuiltinType::Half: + case BuiltinType::ObjCId: + case BuiltinType::ObjCClass: + case BuiltinType::ObjCSel: + case BuiltinType::OMPArraySection: + case BuiltinType::Dependent: + case BuiltinType::Overload: + case BuiltinType::BoundMember: + case BuiltinType::PseudoObject: + case BuiltinType::UnknownAny: + case BuiltinType::BuiltinFn: + case BuiltinType::ARCUnbridgedCast: + case BuiltinType::OCLImage1dRO: + case BuiltinType::OCLImage1dArrayRO: + case BuiltinType::OCLImage1dBufferRO: + case BuiltinType::OCLImage2dRO: + case BuiltinType::OCLImage2dArrayRO: + case BuiltinType::OCLImage2dDepthRO: + case BuiltinType::OCLImage2dArrayDepthRO: + case BuiltinType::OCLImage2dMSAARO: + case BuiltinType::OCLImage2dArrayMSAARO: + case BuiltinType::OCLImage2dMSAADepthRO: + case BuiltinType::OCLImage2dArrayMSAADepthRO: + case BuiltinType::OCLImage3dRO: + case BuiltinType::OCLImage1dWO: + case BuiltinType::OCLImage1dArrayWO: + case BuiltinType::OCLImage1dBufferWO: + case BuiltinType::OCLImage2dWO: + case BuiltinType::OCLImage2dArrayWO: + case BuiltinType::OCLImage2dDepthWO: + case BuiltinType::OCLImage2dArrayDepthWO: + case BuiltinType::OCLImage2dMSAAWO: + case BuiltinType::OCLImage2dArrayMSAAWO: + case BuiltinType::OCLImage2dMSAADepthWO: + case BuiltinType::OCLImage2dArrayMSAADepthWO: + case BuiltinType::OCLImage3dWO: + case BuiltinType::OCLImage1dRW: + case BuiltinType::OCLImage1dArrayRW: + case BuiltinType::OCLImage1dBufferRW: + case BuiltinType::OCLImage2dRW: + case BuiltinType::OCLImage2dArrayRW: + case BuiltinType::OCLImage2dDepthRW: + case BuiltinType::OCLImage2dArrayDepthRW: + case BuiltinType::OCLImage2dMSAARW: + case BuiltinType::OCLImage2dArrayMSAARW: + case BuiltinType::OCLImage2dMSAADepthRW: + case BuiltinType::OCLImage2dArrayMSAADepthRW: + case BuiltinType::OCLImage3dRW: + case BuiltinType::OCLSampler: + case BuiltinType::OCLEvent: + case BuiltinType::OCLClkEvent: + case BuiltinType::OCLQueue: + case BuiltinType::OCLReserveID: + return res; + } + break; + } + case Type::Pointer: + return trans_create_node_bin_op(c, res, BinOpTypeCmpNotEq, trans_create_node(c, NodeTypeNullLiteral)); + + case Type::Typedef: + { + const TypedefType *typedef_ty = static_cast(ty); + const TypedefNameDecl *typedef_decl = typedef_ty->getDecl(); + auto existing_entry = c->decl_table.maybe_get((void*)typedef_decl->getCanonicalDecl()); + if (existing_entry) { + return existing_entry->value; + } + + return res; + } + + case Type::Enum: + { + const EnumType *enum_ty = static_cast(ty); + AstNode *enum_type = resolve_enum_decl(c, enum_ty->getDecl()); + return to_enum_zero_cmp(c, res, enum_type); + } + + case Type::Elaborated: + { + const ElaboratedType *elaborated_ty = static_cast(ty); + switch (elaborated_ty->getKeyword()) { + case ETK_Enum: { + AstNode *enum_type = trans_qual_type(c, elaborated_ty->getNamedType(), expr->getLocStart()); + return to_enum_zero_cmp(c, res, enum_type); + } + case ETK_Struct: + case ETK_Union: + case ETK_Interface: + case ETK_Class: + case ETK_Typename: + case ETK_None: + return res; + } + } + + case Type::FunctionProto: + case Type::Record: + case Type::ConstantArray: + case Type::Paren: + case Type::Decayed: + case Type::Attributed: + case Type::IncompleteArray: + case Type::BlockPointer: + case Type::LValueReference: + case Type::RValueReference: + case Type::MemberPointer: + case Type::VariableArray: + case Type::DependentSizedArray: + case Type::DependentSizedExtVector: + case Type::Vector: + case Type::ExtVector: + case Type::FunctionNoProto: + case Type::UnresolvedUsing: + case Type::Adjusted: + case Type::TypeOfExpr: + case Type::TypeOf: + case Type::Decltype: + case Type::UnaryTransform: + case Type::TemplateTypeParm: + case Type::SubstTemplateTypeParm: + case Type::SubstTemplateTypeParmPack: + case Type::TemplateSpecialization: + case Type::Auto: + case Type::InjectedClassName: + case Type::DependentName: + case Type::DependentTemplateSpecialization: + case Type::PackExpansion: + case Type::ObjCObject: + case Type::ObjCInterface: + case Type::Complex: + case Type::ObjCObjectPointer: + case Type::Atomic: + case Type::Pipe: + case Type::ObjCTypeParam: + case Type::DeducedTemplateSpecialization: + return res; + } + zig_unreachable(); } static AstNode *trans_while_loop(Context *c, TransScope *scope, const WhileStmt *stmt) { TransScopeWhile *while_scope = trans_scope_while_create(c, scope); - while_scope->node->data.while_expr.condition = trans_to_bool_expr(c, scope, trans_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue)); + while_scope->node->data.while_expr.condition = trans_bool_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue); if (while_scope->node->data.while_expr.condition == nullptr) return nullptr; @@ -2328,11 +2504,10 @@ static AstNode *trans_if_statement(Context *c, TransScope *scope, const IfStmt * return nullptr; } - AstNode *condition_node = trans_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue); - if (condition_node == nullptr) + if_node->data.if_bool_expr.condition = trans_bool_expr(c, ResultUsedYes, scope, stmt->getCond(), TransRValue); + if (if_node->data.if_bool_expr.condition == nullptr) return nullptr; - if_node->data.if_bool_expr.condition = trans_to_bool_expr(c, scope, condition_node); return if_node; } @@ -2524,12 +2699,18 @@ static AstNode *trans_for_loop(Context *c, TransScope *parent_scope, const ForSt if (cond_stmt == nullptr) { while_scope->node->data.while_expr.condition = trans_create_node_bool(c, true); } else { - TransScope *end_cond_scope = trans_stmt(c, cond_scope, cond_stmt, - &while_scope->node->data.while_expr.condition); - if (end_cond_scope == nullptr) - return nullptr; + if (Expr::classof(cond_stmt)) { + const Expr *cond_expr = static_cast(cond_stmt); + while_scope->node->data.while_expr.condition = trans_bool_expr(c, ResultUsedYes, cond_scope, cond_expr, TransRValue); - while_scope->node->data.while_expr.condition = trans_to_bool_expr(c, cond_scope, while_scope->node->data.while_expr.condition); + if (while_scope->node->data.while_expr.condition == nullptr) + return nullptr; + } else { + TransScope *end_cond_scope = trans_stmt(c, cond_scope, cond_stmt, + &while_scope->node->data.while_expr.condition); + if (end_cond_scope == nullptr) + return nullptr; + } } const Stmt *inc_stmt = stmt->getInc(); diff --git a/test/translate_c.zig b/test/translate_c.zig index 0cfefe79ab..9a69c2b03e 100644 --- a/test/translate_c.zig +++ b/test/translate_c.zig @@ -451,6 +451,28 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\} ); + cases.addC("logical and, logical or on none bool values", + \\int and_or_none_bool(int a, float b, void *c) { + \\ if (a && b) return 0; + \\ if (b && c) return 1; + \\ if (a && c) return 2; + \\ if (a || b) return 3; + \\ if (b || c) return 4; + \\ if (a || c) return 5; + \\ return 6; + \\} + , + \\pub export fn and_or_none_bool(a: c_int, b: f32, c: ?&c_void) c_int { + \\ if ((a != 0) and (b != 0)) return 0; + \\ if ((b != 0) and (c != null)) return 1; + \\ if ((a != 0) and (c != null)) return 2; + \\ if ((a != 0) or (b != 0)) return 3; + \\ if ((b != 0) or (c != null)) return 4; + \\ if ((a != 0) or (c != null)) return 5; + \\ return 6; + \\} + ); + cases.addC("assign", \\int max(int a) { \\ int tmp; @@ -1102,17 +1124,18 @@ pub fn addCases(cases: &tests.TranslateCContext) void { ); cases.add("bool not", - \\int foo(int x) { - \\ return !(x == 0); - \\ return !x; + \\int foo(int a, float b, void *c) { + \\ return !(a == 0); + \\ return !a; + \\ return !b; + \\ return !c; \\} , - \\pub fn foo(x: c_int) c_int { - \\ return !(x == 0); - \\ return !__to_bool_expr: { - \\ const _tmp = x; - \\ break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0; - \\ }; + \\pub fn foo(a: c_int, b: f32, c: ?&c_void) c_int { + \\ return !(a == 0); + \\ return !(a != 0); + \\ return !(b != 0); + \\ return !(c != null); \\} ); @@ -1148,75 +1171,66 @@ pub fn addCases(cases: &tests.TranslateCContext) void { \\pub const NRF_GPIO = if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Pointer) @ptrCast(&NRF_GPIO_Type, NRF_GPIO_BASE) else if (@typeId(@typeOf(NRF_GPIO_BASE)) == @import("builtin").TypeId.Int) @intToPtr(&NRF_GPIO_Type, NRF_GPIO_BASE) else (&NRF_GPIO_Type)(NRF_GPIO_BASE); ); - cases.add("if on int", - \\int if_int(int i) { - \\ if (i) { - \\ return 0; - \\ } else { - \\ return 1; - \\ } + cases.add("if on none bool", + \\enum SomeEnum { A, B, C }; + \\int if_none_bool(int a, float b, void *c, enum SomeEnum d) { + \\ if (a) return 0; + \\ if (b) return 1; + \\ if (c) return 2; + \\ if (d) return 3; + \\ return 4; \\} , - \\pub fn if_int(i: c_int) c_int { - \\ if (__to_bool_expr: { - \\ const _tmp = i; - \\ break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0; - \\ }) { - \\ return 0; - \\ } else { - \\ return 1; - \\ } - \\} + \\pub const A = enum_SomeEnum.A; + \\pub const B = enum_SomeEnum.B; + \\pub const C = enum_SomeEnum.C; + \\pub const enum_SomeEnum = extern enum { + \\ A, + \\ B, + \\ C, + \\}; + \\pub fn if_none_bool(a: c_int, b: f32, c: ?&c_void, d: enum_SomeEnum) c_int { + \\ if (a != 0) return 0; + \\ if (b != 0) return 1; + \\ if (c != null) return 2; + \\ if (d != @bitCast(enum_SomeEnum, @TagType(enum_SomeEnum)(0))) return 3; + \\ return 4; + \\} ); - cases.add("while on int", - \\int while_int(int i) { - \\ while (i) { - \\ return 0; - \\ } + cases.add("while on none bool", + \\int while_none_bool(int a, float b, void *c) { + \\ while (a) return 0; + \\ while (b) return 1; + \\ while (c) return 2; + \\ return 3; \\} , - \\pub fn while_int(i: c_int) c_int { - \\ while (__to_bool_expr: { - \\ const _tmp = i; - \\ break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0; - \\ }) { - \\ return 0; - \\ } - \\} + \\pub fn while_none_bool(a: c_int, b: f32, c: ?&c_void) c_int { + \\ while (a != 0) return 0; + \\ while (b != 0) return 1; + \\ while (c != null) return 2; + \\ return 3; + \\} ); - cases.add("for on int", - \\int for_int(int i) { - \\ for (;i;) { - \\ return 0; - \\ } - \\ - \\ for (int j = 4;j;j--) { - \\ return 0; - \\ } + cases.add("for on none bool", + \\int for_none_bool(int a, float b, void *c) { + \\ for (;a;) return 0; + \\ for (;b;) return 1; + \\ for (;c;) return 2; + \\ return 3; \\} , - \\pub fn for_int(i: c_int) c_int { - \\ while (__to_bool_expr: { - \\ const _tmp = i; - \\ break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0; - \\ }) { - \\ return 0; - \\ } - \\ { - \\ var j: c_int = 4; - \\ while (__to_bool_expr: { - \\ const _tmp = j; - \\ break :__to_bool_expr @bitCast(@IntType(false, @sizeOf(@typeOf(_tmp)) * 8), _tmp) != 0; - \\ }) : (j -= 1) { - \\ return 0; - \\ } - \\ } - \\} + \\pub fn for_none_bool(a: c_int, b: f32, c: ?&c_void) c_int { + \\ while (a != 0) return 0; + \\ while (b != 0) return 1; + \\ while (c != null) return 2; + \\ return 3; + \\} ); - cases.add("for on int", + cases.add("switch on int", \\int switch_fn(int i) { \\ int res = 0; \\ switch (i) {