| author | |
| committer | |
| log | b7dd88ad68aab5b6bc8321431d1a53b343b2dd37 |
| tree | 78a9cd198eb94529a5841eb68c6bf57f9be21607 |
| parent | 14b9cbd43c21ad2ba75b38ef5fc681c044e7662e |
closes #3210 files changed, 205 insertions(+), 19 deletions(-)
doc/langref.md+1-1| ... | ... | @@ -160,7 +160,7 @@ SliceExpression : token(LBracket) Expression token(Ellipsis) option(Expression) |
| 160 | 160 | |
| 161 | 161 | PrefixOp : token(Not) | token(Dash) | token(Tilde) | token(Star) | (token(Ampersand) option(token(Const))) |
| 162 | 162 | |
| 163 | PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType | |
| 163 | PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType | (token(AtSign) token(Symbol) FnCallExpression) | |
| 164 | 164 | |
| 165 | 165 | StructValueExpression : token(Type) token(LBrace) list(StructValueExpressionField, token(Comma)) token(RBrace) |
| 166 | 166 |
src/analyze.cpp+40-2| ... | ... | @@ -2064,6 +2064,41 @@ static TypeTableEntry *analyze_compiler_fn_type(CodeGen *g, ImportTableEntry *im |
| 2064 | 2064 | } |
| 2065 | 2065 | } |
| 2066 | 2066 | |
| 2067 | static TypeTableEntry *analyze_builtin_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, | |
| 2068 | TypeTableEntry *expected_type, AstNode *node) | |
| 2069 | { | |
| 2070 | AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; | |
| 2071 | Buf *name = &fn_ref_expr->data.symbol; | |
| 2072 | ||
| 2073 | auto entry = g->builtin_fn_table.maybe_get(name); | |
| 2074 | ||
| 2075 | if (entry) { | |
| 2076 | BuiltinFnEntry *builtin_fn = entry->value; | |
| 2077 | int actual_param_count = node->data.fn_call_expr.params.length; | |
| 2078 | ||
| 2079 | assert(node->codegen_node); | |
| 2080 | node->codegen_node->data.fn_call_node.builtin_fn = builtin_fn; | |
| 2081 | ||
| 2082 | if (builtin_fn->param_count != actual_param_count) { | |
| 2083 | add_node_error(g, node, | |
| 2084 | buf_sprintf("expected %d arguments, got %d", | |
| 2085 | builtin_fn->param_count, actual_param_count)); | |
| 2086 | } | |
| 2087 | ||
| 2088 | for (int i = 0; i < actual_param_count; i += 1) { | |
| 2089 | AstNode *child = node->data.fn_call_expr.params.at(i); | |
| 2090 | TypeTableEntry *expected_param_type = builtin_fn->param_types[i]; | |
| 2091 | analyze_expression(g, import, context, expected_param_type, child); | |
| 2092 | } | |
| 2093 | ||
| 2094 | return builtin_fn->return_type; | |
| 2095 | } else { | |
| 2096 | add_node_error(g, node, | |
| 2097 | buf_sprintf("invalid builtin function: '%s'", buf_ptr(name))); | |
| 2098 | return g->builtin_types.entry_invalid; | |
| 2099 | } | |
| 2100 | } | |
| 2101 | ||
| 2067 | 2102 | static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import, BlockContext *context, |
| 2068 | 2103 | TypeTableEntry *expected_type, AstNode *node) |
| 2069 | 2104 | { |
| ... | ... | @@ -2091,6 +2126,9 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import |
| 2091 | 2126 | return g->builtin_types.entry_invalid; |
| 2092 | 2127 | } |
| 2093 | 2128 | } else if (fn_ref_expr->type == NodeTypeSymbol) { |
| 2129 | if (node->data.fn_call_expr.is_builtin) { | |
| 2130 | return analyze_builtin_fn_call_expr(g, import, context, expected_type, node); | |
| 2131 | } | |
| 2094 | 2132 | name = &fn_ref_expr->data.symbol; |
| 2095 | 2133 | } else { |
| 2096 | 2134 | add_node_error(g, node, |
| ... | ... | @@ -2126,12 +2164,12 @@ static TypeTableEntry *analyze_fn_call_expr(CodeGen *g, ImportTableEntry *import |
| 2126 | 2164 | if (fn_proto->is_var_args) { |
| 2127 | 2165 | if (actual_param_count < expected_param_count) { |
| 2128 | 2166 | add_node_error(g, node, |
| 2129 | buf_sprintf("wrong number of arguments. Expected at least %d, got %d.", | |
| 2167 | buf_sprintf("expected at least %d arguments, got %d", | |
| 2130 | 2168 | expected_param_count, actual_param_count)); |
| 2131 | 2169 | } |
| 2132 | 2170 | } else if (expected_param_count != actual_param_count) { |
| 2133 | 2171 | add_node_error(g, node, |
| 2134 | buf_sprintf("wrong number of arguments. Expected %d, got %d.", | |
| 2172 | buf_sprintf("expected %d arguments, got %d", | |
| 2135 | 2173 | expected_param_count, actual_param_count)); |
| 2136 | 2174 | } |
| 2137 | 2175 |
src/analyze.hpp+20-7| ... | ... | @@ -148,6 +148,20 @@ struct FnTableEntry { |
| 148 | 148 | HashMap<Buf *, LabelTableEntry *, buf_hash, buf_eql_buf> label_table; |
| 149 | 149 | }; |
| 150 | 150 | |
| 151 | enum BuiltinFnId { | |
| 152 | BuiltinFnIdInvalid, | |
| 153 | BuiltinFnIdArithmeticWithOverflow, | |
| 154 | }; | |
| 155 | ||
| 156 | struct BuiltinFnEntry { | |
| 157 | BuiltinFnId id; | |
| 158 | Buf name; | |
| 159 | int param_count; | |
| 160 | TypeTableEntry *return_type; | |
| 161 | TypeTableEntry **param_types; | |
| 162 | LLVMValueRef fn_val; | |
| 163 | }; | |
| 164 | ||
| 151 | 165 | struct CodeGen { |
| 152 | 166 | LLVMModuleRef module; |
| 153 | 167 | ZigList<ErrorMsg*> errors; |
| ... | ... | @@ -161,6 +175,7 @@ struct CodeGen { |
| 161 | 175 | HashMap<Buf *, LLVMValueRef, buf_hash, buf_eql_buf> str_table; |
| 162 | 176 | HashMap<Buf *, bool, buf_hash, buf_eql_buf> link_table; |
| 163 | 177 | HashMap<Buf *, ImportTableEntry *, buf_hash, buf_eql_buf> import_table; |
| 178 | HashMap<Buf *, BuiltinFnEntry *, buf_hash, buf_eql_buf> builtin_fn_table; | |
| 164 | 179 | |
| 165 | 180 | struct { |
| 166 | 181 | TypeTableEntry *entry_bool; |
| ... | ... | @@ -342,6 +357,10 @@ struct WhileNode { |
| 342 | 357 | bool contains_break; |
| 343 | 358 | }; |
| 344 | 359 | |
| 360 | struct FnCallNode { | |
| 361 | BuiltinFnEntry *builtin_fn; | |
| 362 | }; | |
| 363 | ||
| 345 | 364 | struct CodeGenNode { |
| 346 | 365 | union { |
| 347 | 366 | TypeNode type_node; // for NodeTypeType |
| ... | ... | @@ -363,17 +382,11 @@ struct CodeGenNode { |
| 363 | 382 | ParamDeclNode param_decl_node; // for NodeTypeParamDecl |
| 364 | 383 | ImportNode import_node; // for NodeTypeUse |
| 365 | 384 | WhileNode while_node; // for NodeTypeWhileExpr |
| 385 | FnCallNode fn_call_node; // for NodeTypeFnCallExpr | |
| 366 | 386 | } data; |
| 367 | 387 | ExprNode expr_node; // for all the expression nodes |
| 368 | 388 | }; |
| 369 | 389 | |
| 370 | static inline Buf *hack_get_fn_call_name(CodeGen *g, AstNode *node) { | |
| 371 | // Assume that the expression evaluates to a simple name and return the buf | |
| 372 | // TODO after type checking works we should be able to remove this hack | |
| 373 | assert(node->type == NodeTypeSymbol); | |
| 374 | return &node->data.symbol; | |
| 375 | } | |
| 376 | ||
| 377 | 390 | void semantic_analyze(CodeGen *g); |
| 378 | 391 | void add_node_error(CodeGen *g, AstNode *node, Buf *msg); |
| 379 | 392 | void alloc_codegen_node(AstNode *node); |
src/codegen.cpp+105-2| ... | ... | @@ -22,6 +22,7 @@ CodeGen *codegen_create(Buf *root_source_dir) { |
| 22 | 22 | g->str_table.init(32); |
| 23 | 23 | g->link_table.init(32); |
| 24 | 24 | g->import_table.init(32); |
| 25 | g->builtin_fn_table.init(32); | |
| 25 | 26 | g->build_type = CodeGenBuildTypeDebug; |
| 26 | 27 | g->root_source_dir = root_source_dir; |
| 27 | 28 | |
| ... | ... | @@ -139,6 +140,41 @@ static TypeTableEntry *get_expr_type(AstNode *node) { |
| 139 | 140 | return cast_type ? cast_type : node->codegen_node->expr_node.type_entry; |
| 140 | 141 | } |
| 141 | 142 | |
| 143 | static LLVMValueRef gen_builtin_fn_call_expr(CodeGen *g, AstNode *node) { | |
| 144 | assert(node->type == NodeTypeFnCallExpr); | |
| 145 | AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; | |
| 146 | assert(fn_ref_expr->type == NodeTypeSymbol); | |
| 147 | BuiltinFnEntry *builtin_fn = node->codegen_node->data.fn_call_node.builtin_fn; | |
| 148 | ||
| 149 | switch (builtin_fn->id) { | |
| 150 | case BuiltinFnIdInvalid: | |
| 151 | zig_unreachable(); | |
| 152 | case BuiltinFnIdArithmeticWithOverflow: | |
| 153 | { | |
| 154 | int fn_call_param_count = node->data.fn_call_expr.params.length; | |
| 155 | assert(fn_call_param_count == 3); | |
| 156 | ||
| 157 | LLVMValueRef op1 = gen_expr(g, node->data.fn_call_expr.params.at(0)); | |
| 158 | LLVMValueRef op2 = gen_expr(g, node->data.fn_call_expr.params.at(1)); | |
| 159 | LLVMValueRef ptr_result = gen_expr(g, node->data.fn_call_expr.params.at(2)); | |
| 160 | ||
| 161 | LLVMValueRef params[] = { | |
| 162 | op1, | |
| 163 | op2, | |
| 164 | }; | |
| 165 | ||
| 166 | add_debug_source_node(g, node); | |
| 167 | LLVMValueRef result_struct = LLVMBuildCall(g->builder, builtin_fn->fn_val, params, 2, ""); | |
| 168 | LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, ""); | |
| 169 | LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, ""); | |
| 170 | LLVMBuildStore(g->builder, result, ptr_result); | |
| 171 | ||
| 172 | return overflow_bit; | |
| 173 | } | |
| 174 | } | |
| 175 | zig_unreachable(); | |
| 176 | } | |
| 177 | ||
| 142 | 178 | static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { |
| 143 | 179 | assert(node->type == NodeTypeFnCallExpr); |
| 144 | 180 | |
| ... | ... | @@ -159,7 +195,15 @@ static LLVMValueRef gen_fn_call_expr(CodeGen *g, AstNode *node) { |
| 159 | 195 | zig_unreachable(); |
| 160 | 196 | } |
| 161 | 197 | } else if (fn_ref_expr->type == NodeTypeSymbol) { |
| 162 | Buf *name = hack_get_fn_call_name(g, fn_ref_expr); | |
| 198 | if (node->data.fn_call_expr.is_builtin) { | |
| 199 | return gen_builtin_fn_call_expr(g, node); | |
| 200 | } | |
| 201 | ||
| 202 | // Assume that the expression evaluates to a simple name and return the buf | |
| 203 | // TODO after we support function pointers we can make this generic | |
| 204 | assert(fn_ref_expr->type == NodeTypeSymbol); | |
| 205 | Buf *name = &fn_ref_expr->data.symbol; | |
| 206 | ||
| 163 | 207 | struct_type = nullptr; |
| 164 | 208 | first_param_expr = nullptr; |
| 165 | 209 | fn_table_entry = g->cur_fn->import_entry->fn_table.get(name); |
| ... | ... | @@ -2167,6 +2211,64 @@ static void define_builtin_types(CodeGen *g) { |
| 2167 | 2211 | } |
| 2168 | 2212 | } |
| 2169 | 2213 | |
| 2214 | static void define_builtin_fns_int(CodeGen *g, TypeTableEntry *type_entry) { | |
| 2215 | assert(type_entry->id == TypeTableEntryIdInt); | |
| 2216 | struct OverflowFn { | |
| 2217 | const char *bare_name; | |
| 2218 | const char *signed_name; | |
| 2219 | const char *unsigned_name; | |
| 2220 | }; | |
| 2221 | OverflowFn overflow_fns[] = { | |
| 2222 | {"add", "sadd", "uadd"}, | |
| 2223 | {"sub", "ssub", "usub"}, | |
| 2224 | {"mul", "smul", "umul"}, | |
| 2225 | }; | |
| 2226 | for (int i = 0; i < sizeof(overflow_fns)/sizeof(overflow_fns[0]); i += 1) { | |
| 2227 | OverflowFn *overflow_fn = &overflow_fns[i]; | |
| 2228 | BuiltinFnEntry *builtin_fn = allocate<BuiltinFnEntry>(1); | |
| 2229 | buf_resize(&builtin_fn->name, 0); | |
| 2230 | buf_appendf(&builtin_fn->name, "%s_with_overflow_%s", overflow_fn->bare_name, buf_ptr(&type_entry->name)); | |
| 2231 | builtin_fn->id = BuiltinFnIdArithmeticWithOverflow; | |
| 2232 | builtin_fn->return_type = g->builtin_types.entry_bool; | |
| 2233 | builtin_fn->param_count = 3; | |
| 2234 | builtin_fn->param_types = allocate<TypeTableEntry *>(builtin_fn->param_count); | |
| 2235 | builtin_fn->param_types[0] = type_entry; | |
| 2236 | builtin_fn->param_types[1] = type_entry; | |
| 2237 | builtin_fn->param_types[2] = get_pointer_to_type(g, type_entry, false, false); | |
| 2238 | ||
| 2239 | ||
| 2240 | const char *signed_str = type_entry->data.integral.is_signed ? | |
| 2241 | overflow_fn->signed_name : overflow_fn->unsigned_name; | |
| 2242 | Buf *llvm_name = buf_sprintf("llvm.%s.with.overflow.i%" PRIu64, signed_str, type_entry->size_in_bits); | |
| 2243 | ||
| 2244 | LLVMTypeRef return_elem_types[] = { | |
| 2245 | type_entry->type_ref, | |
| 2246 | LLVMInt1Type(), | |
| 2247 | }; | |
| 2248 | LLVMTypeRef param_types[] = { | |
| 2249 | type_entry->type_ref, | |
| 2250 | type_entry->type_ref, | |
| 2251 | }; | |
| 2252 | LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false); | |
| 2253 | LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false); | |
| 2254 | builtin_fn->fn_val = LLVMAddFunction(g->module, buf_ptr(llvm_name), fn_type); | |
| 2255 | assert(LLVMGetIntrinsicID(builtin_fn->fn_val)); | |
| 2256 | ||
| 2257 | g->builtin_fn_table.put(&builtin_fn->name, builtin_fn); | |
| 2258 | } | |
| 2259 | } | |
| 2260 | ||
| 2261 | static void define_builtin_fns(CodeGen *g) { | |
| 2262 | define_builtin_fns_int(g, g->builtin_types.entry_u8); | |
| 2263 | define_builtin_fns_int(g, g->builtin_types.entry_u16); | |
| 2264 | define_builtin_fns_int(g, g->builtin_types.entry_u32); | |
| 2265 | define_builtin_fns_int(g, g->builtin_types.entry_u64); | |
| 2266 | define_builtin_fns_int(g, g->builtin_types.entry_i8); | |
| 2267 | define_builtin_fns_int(g, g->builtin_types.entry_i16); | |
| 2268 | define_builtin_fns_int(g, g->builtin_types.entry_i32); | |
| 2269 | define_builtin_fns_int(g, g->builtin_types.entry_i64); | |
| 2270 | } | |
| 2271 | ||
| 2170 | 2272 | |
| 2171 | 2273 | |
| 2172 | 2274 | static void init(CodeGen *g, Buf *source_path) { |
| ... | ... | @@ -2228,9 +2330,10 @@ static void init(CodeGen *g, Buf *source_path) { |
| 2228 | 2330 | "", 0, !g->strip_debug_symbols); |
| 2229 | 2331 | |
| 2230 | 2332 | // This is for debug stuff that doesn't have a real file. |
| 2231 | g->dummy_di_file = nullptr; //LLVMZigCreateFile(g->dbuilder, "", ""); | |
| 2333 | g->dummy_di_file = nullptr; | |
| 2232 | 2334 | |
| 2233 | 2335 | define_builtin_types(g); |
| 2336 | define_builtin_fns(g); | |
| 2234 | 2337 | |
| 2235 | 2338 | } |
| 2236 | 2339 |
src/parser.cpp+13-1| ... | ... | @@ -1313,7 +1313,7 @@ static AstNode *ast_parse_struct_val_expr(ParseContext *pc, int *token_index) { |
| 1313 | 1313 | } |
| 1314 | 1314 | |
| 1315 | 1315 | /* |
| 1316 | PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType | |
| 1316 | PrimaryExpression : token(Number) | token(String) | token(CharLiteral) | KeywordLiteral | GroupedExpression | Goto | token(Break) | token(Continue) | BlockExpression | token(Symbol) | StructValueExpression | CompilerFnType | (token(AtSign) token(Symbol) FnCallExpression) | |
| 1317 | 1317 | KeywordLiteral : token(Unreachable) | token(Void) | token(True) | token(False) | token(Null) |
| 1318 | 1318 | */ |
| 1319 | 1319 | static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool mandatory) { |
| ... | ... | @@ -1356,6 +1356,18 @@ static AstNode *ast_parse_primary_expr(ParseContext *pc, int *token_index, bool |
| 1356 | 1356 | AstNode *node = ast_create_node(pc, NodeTypeNullLiteral, token); |
| 1357 | 1357 | *token_index += 1; |
| 1358 | 1358 | return node; |
| 1359 | } else if (token->id == TokenIdAtSign) { | |
| 1360 | *token_index += 1; | |
| 1361 | Token *name_tok = ast_eat_token(pc, token_index, TokenIdSymbol); | |
| 1362 | AstNode *name_node = ast_create_node(pc, NodeTypeSymbol, name_tok); | |
| 1363 | ast_buf_from_token(pc, name_tok, &name_node->data.symbol); | |
| 1364 | ||
| 1365 | AstNode *node = ast_create_node(pc, NodeTypeFnCallExpr, token); | |
| 1366 | node->data.fn_call_expr.fn_ref_expr = name_node; | |
| 1367 | ast_eat_token(pc, token_index, TokenIdLParen); | |
| 1368 | ast_parse_fn_call_param_list(pc, token_index, &node->data.fn_call_expr.params); | |
| 1369 | node->data.fn_call_expr.is_builtin = true; | |
| 1370 | return node; | |
| 1359 | 1371 | } else if (token->id == TokenIdSymbol) { |
| 1360 | 1372 | Token *next_token = &pc->tokens->at(*token_index + 1); |
| 1361 | 1373 |
src/parser.hpp+1| ... | ... | @@ -176,6 +176,7 @@ struct AstNodeBinOpExpr { |
| 176 | 176 | struct AstNodeFnCallExpr { |
| 177 | 177 | AstNode *fn_ref_expr; |
| 178 | 178 | ZigList<AstNode *> params; |
| 179 | bool is_builtin; | |
| 179 | 180 | }; |
| 180 | 181 | |
| 181 | 182 | struct AstNodeArrayAccessExpr { |
src/tokenizer.cpp+5| ... | ... | @@ -376,6 +376,10 @@ void tokenize(Buf *buf, Tokenization *out) { |
| 376 | 376 | begin_token(&t, TokenIdTilde); |
| 377 | 377 | end_token(&t); |
| 378 | 378 | break; |
| 379 | case '@': | |
| 380 | begin_token(&t, TokenIdAtSign); | |
| 381 | end_token(&t); | |
| 382 | break; | |
| 379 | 383 | case '-': |
| 380 | 384 | begin_token(&t, TokenIdDash); |
| 381 | 385 | t.state = TokenizeStateSawDash; |
| ... | ... | @@ -1074,6 +1078,7 @@ static const char * token_name(Token *token) { |
| 1074 | 1078 | case TokenIdMaybe: return "Maybe"; |
| 1075 | 1079 | case TokenIdDoubleQuestion: return "DoubleQuestion"; |
| 1076 | 1080 | case TokenIdMaybeAssign: return "MaybeAssign"; |
| 1081 | case TokenIdAtSign: return "AtSign"; | |
| 1077 | 1082 | } |
| 1078 | 1083 | return "(invalid token)"; |
| 1079 | 1084 | } |
src/tokenizer.hpp+1| ... | ... | @@ -89,6 +89,7 @@ enum TokenId { |
| 89 | 89 | TokenIdMaybe, |
| 90 | 90 | TokenIdDoubleQuestion, |
| 91 | 91 | TokenIdMaybeAssign, |
| 92 | TokenIdAtSign, | |
| 92 | 93 | }; |
| 93 | 94 | |
| 94 | 95 | struct Token { |
std/std.zig-5| ... | ... | @@ -63,10 +63,6 @@ pub fn parse_u64(buf: []u8, radix: u8, result: &u64) -> bool { |
| 63 | 63 | return true; |
| 64 | 64 | } |
| 65 | 65 | |
| 66 | x *= radix; | |
| 67 | x += digit; | |
| 68 | ||
| 69 | /* TODO intrinsics mul and add with overflow | |
| 70 | 66 | // x *= radix |
| 71 | 67 | if (@mul_with_overflow_u64(x, radix, &x)) { |
| 72 | 68 | return true; |
| ... | ... | @@ -76,7 +72,6 @@ pub fn parse_u64(buf: []u8, radix: u8, result: &u64) -> bool { |
| 76 | 72 | if (@add_with_overflow_u64(x, digit, &x)) { |
| 77 | 73 | return true; |
| 78 | 74 | } |
| 79 | */ | |
| 80 | 75 | |
| 81 | 76 | i += 1; |
| 82 | 77 | } |
test/run_tests.cpp+19-1| ... | ... | @@ -953,6 +953,24 @@ fn f(c: u8) -> u8 { |
| 953 | 953 | } else { |
| 954 | 954 | 2 |
| 955 | 955 | } |
| 956 | } | |
| 957 | )SOURCE", "OK\n"); | |
| 958 | ||
| 959 | add_simple_case("overflow intrinsics", R"SOURCE( | |
| 960 | use "std.zig"; | |
| 961 | pub fn main(argc: isize, argv: &&u8, env: &&u8) -> i32 { | |
| 962 | var result: u8; | |
| 963 | if (!@add_with_overflow_u8(250, 100, &result)) { | |
| 964 | print_str("BAD\n"); | |
| 965 | } | |
| 966 | if (@add_with_overflow_u8(100, 150, &result)) { | |
| 967 | print_str("BAD\n"); | |
| 968 | } | |
| 969 | if (result != 250) { | |
| 970 | print_str("BAD\n"); | |
| 971 | } | |
| 972 | print_str("OK\n"); | |
| 973 | return 0; | |
| 956 | 974 | } |
| 957 | 975 | )SOURCE", "OK\n"); |
| 958 | 976 | } |
| ... | ... | @@ -995,7 +1013,7 @@ fn a() { |
| 995 | 1013 | b(1); |
| 996 | 1014 | } |
| 997 | 1015 | fn b(a: i32, b: i32, c: i32) { } |
| 998 | )SOURCE", 1, ".tmp_source.zig:3:6: error: wrong number of arguments. Expected 3, got 1."); | |
| 1016 | )SOURCE", 1, ".tmp_source.zig:3:6: error: expected 3 arguments, got 1"); | |
| 999 | 1017 | |
| 1000 | 1018 | add_compile_fail_case("invalid type", R"SOURCE( |
| 1001 | 1019 | fn a() -> bogus {} |