| author | |
| committer | |
| log | 7d0de54ad44832589379a4bcbba493db2087bebf |
| tree | 5efb0a82c4ad399e25f603ba25cc6e36566ca845 |
| parent | e974d4c4295c4fbdbc239caa2cf2d653f65662f1 |
* Introduce `ret_load` ZIR instruction which does return semantics
based on a corresponding `ret_ptr` instruction. If the return type of
the function has storage for the return type, it simply returns.
However if the return type of the function is by-value, it loads the
return value from the `ret_ptr` allocation and returns that.
* AstGen: improve `finishThenElseBlock` to not emit break instructions
after a return instruction in the same block.
* Sema: `ret_ptr` instruction works correctly in comptime contexts.
Same with `alloc_mut`.
The test case with a recursive inline function having an implicitly
comptime return value now has a runtime return value because of the fact
that it calls a function in a non-comptime context.5 files changed, 92 insertions(+), 56 deletions(-)
src/AstGen.zig+25-9| ... | @@ -2131,6 +2131,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner | ... | @@ -2131,6 +2131,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner |
| 2131 | .condbr_inline, | 2131 | .condbr_inline, |
| 2132 | .compile_error, | 2132 | .compile_error, |
| 2133 | .ret_node, | 2133 | .ret_node, |
| 2134 | .ret_load, | ||
| 2134 | .ret_coerce, | 2135 | .ret_coerce, |
| 2135 | .ret_err_value, | 2136 | .ret_err_value, |
| 2136 | .@"unreachable", | 2137 | .@"unreachable", |
| ... | @@ -4791,11 +4792,10 @@ fn finishThenElseBlock( | ... | @@ -4791,11 +4792,10 @@ fn finishThenElseBlock( |
| 4791 | const strat = rl.strategy(block_scope); | 4792 | const strat = rl.strategy(block_scope); |
| 4792 | switch (strat.tag) { | 4793 | switch (strat.tag) { |
| 4793 | .break_void => { | 4794 | .break_void => { |
| 4794 | if (!parent_gz.refIsNoReturn(then_result)) { | 4795 | if (!then_scope.endsWithNoReturn()) { |
| 4795 | _ = try then_scope.addBreak(break_tag, then_break_block, .void_value); | 4796 | _ = try then_scope.addBreak(break_tag, then_break_block, .void_value); |
| 4796 | } | 4797 | } |
| 4797 | const elide_else = if (else_result != .none) parent_gz.refIsNoReturn(else_result) else false; | 4798 | if (!else_scope.endsWithNoReturn()) { |
| 4798 | if (!elide_else) { | ||
| 4799 | _ = try else_scope.addBreak(break_tag, main_block, .void_value); | 4799 | _ = try else_scope.addBreak(break_tag, main_block, .void_value); |
| 4800 | } | 4800 | } |
| 4801 | assert(!strat.elide_store_to_block_ptr_instructions); | 4801 | assert(!strat.elide_store_to_block_ptr_instructions); |
| ... | @@ -4803,11 +4803,11 @@ fn finishThenElseBlock( | ... | @@ -4803,11 +4803,11 @@ fn finishThenElseBlock( |
| 4803 | return indexToRef(main_block); | 4803 | return indexToRef(main_block); |
| 4804 | }, | 4804 | }, |
| 4805 | .break_operand => { | 4805 | .break_operand => { |
| 4806 | if (!parent_gz.refIsNoReturn(then_result)) { | 4806 | if (!then_scope.endsWithNoReturn()) { |
| 4807 | _ = try then_scope.addBreak(break_tag, then_break_block, then_result); | 4807 | _ = try then_scope.addBreak(break_tag, then_break_block, then_result); |
| 4808 | } | 4808 | } |
| 4809 | if (else_result != .none) { | 4809 | if (else_result != .none) { |
| 4810 | if (!parent_gz.refIsNoReturn(else_result)) { | 4810 | if (!else_scope.endsWithNoReturn()) { |
| 4811 | _ = try else_scope.addBreak(break_tag, main_block, else_result); | 4811 | _ = try else_scope.addBreak(break_tag, main_block, else_result); |
| 4812 | } | 4812 | } |
| 4813 | } else { | 4813 | } else { |
| ... | @@ -6236,7 +6236,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref | ... | @@ -6236,7 +6236,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref |
| 6236 | // Value is always an error. Emit both error defers and regular defers. | 6236 | // Value is always an error. Emit both error defers and regular defers. |
| 6237 | const err_code = try gz.addUnNode(.err_union_code, operand, node); | 6237 | const err_code = try gz.addUnNode(.err_union_code, operand, node); |
| 6238 | try genDefers(gz, defer_outer, scope, .{ .both = err_code }); | 6238 | try genDefers(gz, defer_outer, scope, .{ .both = err_code }); |
| 6239 | _ = try gz.addUnNode(.ret_node, operand, node); | 6239 | try gz.addRet(rl, operand, node); |
| 6240 | return Zir.Inst.Ref.unreachable_value; | 6240 | return Zir.Inst.Ref.unreachable_value; |
| 6241 | }, | 6241 | }, |
| 6242 | .maybe => { | 6242 | .maybe => { |
| ... | @@ -6244,7 +6244,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref | ... | @@ -6244,7 +6244,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref |
| 6244 | if (!defer_counts.have_err) { | 6244 | if (!defer_counts.have_err) { |
| 6245 | // Only regular defers; no branch needed. | 6245 | // Only regular defers; no branch needed. |
| 6246 | try genDefers(gz, defer_outer, scope, .normal_only); | 6246 | try genDefers(gz, defer_outer, scope, .normal_only); |
| 6247 | _ = try gz.addUnNode(.ret_node, operand, node); | 6247 | try gz.addRet(rl, operand, node); |
| 6248 | return Zir.Inst.Ref.unreachable_value; | 6248 | return Zir.Inst.Ref.unreachable_value; |
| 6249 | } | 6249 | } |
| 6250 | 6250 | ||
| ... | @@ -6256,7 +6256,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref | ... | @@ -6256,7 +6256,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref |
| 6256 | defer then_scope.instructions.deinit(astgen.gpa); | 6256 | defer then_scope.instructions.deinit(astgen.gpa); |
| 6257 | 6257 | ||
| 6258 | try genDefers(&then_scope, defer_outer, scope, .normal_only); | 6258 | try genDefers(&then_scope, defer_outer, scope, .normal_only); |
| 6259 | _ = try then_scope.addUnNode(.ret_node, operand, node); | 6259 | try then_scope.addRet(rl, operand, node); |
| 6260 | 6260 | ||
| 6261 | var else_scope = gz.makeSubBlock(scope); | 6261 | var else_scope = gz.makeSubBlock(scope); |
| 6262 | defer else_scope.instructions.deinit(astgen.gpa); | 6262 | defer else_scope.instructions.deinit(astgen.gpa); |
| ... | @@ -6265,7 +6265,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref | ... | @@ -6265,7 +6265,7 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref |
| 6265 | .both = try else_scope.addUnNode(.err_union_code, operand, node), | 6265 | .both = try else_scope.addUnNode(.err_union_code, operand, node), |
| 6266 | }; | 6266 | }; |
| 6267 | try genDefers(&else_scope, defer_outer, scope, which_ones); | 6267 | try genDefers(&else_scope, defer_outer, scope, which_ones); |
| 6268 | _ = try else_scope.addUnNode(.ret_node, operand, node); | 6268 | try else_scope.addRet(rl, operand, node); |
| 6269 | 6269 | ||
| 6270 | try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope); | 6270 | try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope); |
| 6271 | 6271 | ||
| ... | @@ -9003,6 +9003,14 @@ const GenZir = struct { | ... | @@ -9003,6 +9003,14 @@ const GenZir = struct { |
| 9003 | used: bool = false, | 9003 | used: bool = false, |
| 9004 | }; | 9004 | }; |
| 9005 | 9005 | ||
| 9006 | fn endsWithNoReturn(gz: GenZir) bool { | ||
| 9007 | const tags = gz.astgen.instructions.items(.tag); | ||
| 9008 | if (gz.instructions.items.len == 0) return false; | ||
| 9009 | const last_inst = gz.instructions.items[gz.instructions.items.len - 1]; | ||
| 9010 | return tags[last_inst].isNoReturn(); | ||
| 9011 | } | ||
| 9012 | |||
| 9013 | /// TODO all uses of this should be replaced with uses of `endsWithNoReturn`. | ||
| 9006 | fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool { | 9014 | fn refIsNoReturn(gz: GenZir, inst_ref: Zir.Inst.Ref) bool { |
| 9007 | if (inst_ref == .unreachable_value) return true; | 9015 | if (inst_ref == .unreachable_value) return true; |
| 9008 | if (refToIndex(inst_ref)) |inst_index| { | 9016 | if (refToIndex(inst_ref)) |inst_index| { |
| ... | @@ -9977,6 +9985,14 @@ const GenZir = struct { | ... | @@ -9977,6 +9985,14 @@ const GenZir = struct { |
| 9977 | gz.instructions.appendAssumeCapacity(new_index); | 9985 | gz.instructions.appendAssumeCapacity(new_index); |
| 9978 | return new_index; | 9986 | return new_index; |
| 9979 | } | 9987 | } |
| 9988 | |||
| 9989 | fn addRet(gz: *GenZir, rl: ResultLoc, operand: Zir.Inst.Ref, node: ast.Node.Index) !void { | ||
| 9990 | switch (rl) { | ||
| 9991 | .ptr => |ret_ptr| _ = try gz.addUnNode(.ret_load, ret_ptr, node), | ||
| 9992 | .ty => _ = try gz.addUnNode(.ret_node, operand, node), | ||
| 9993 | else => unreachable, | ||
| 9994 | } | ||
| 9995 | } | ||
| 9980 | }; | 9996 | }; |
| 9981 | 9997 | ||
| 9982 | /// This can only be for short-lived references; the memory becomes invalidated | 9998 | /// This can only be for short-lived references; the memory becomes invalidated |
src/Sema.zig+49-17| ... | @@ -366,6 +366,7 @@ pub fn analyzeBody( | ... | @@ -366,6 +366,7 @@ pub fn analyzeBody( |
| 366 | .compile_error => return sema.zirCompileError(block, inst), | 366 | .compile_error => return sema.zirCompileError(block, inst), |
| 367 | .ret_coerce => return sema.zirRetCoerce(block, inst), | 367 | .ret_coerce => return sema.zirRetCoerce(block, inst), |
| 368 | .ret_node => return sema.zirRetNode(block, inst), | 368 | .ret_node => return sema.zirRetNode(block, inst), |
| 369 | .ret_load => return sema.zirRetLoad(block, inst), | ||
| 369 | .ret_err_value => return sema.zirRetErrValue(block, inst), | 370 | .ret_err_value => return sema.zirRetErrValue(block, inst), |
| 370 | .@"unreachable" => return sema.zirUnreachable(block, inst), | 371 | .@"unreachable" => return sema.zirUnreachable(block, inst), |
| 371 | .repeat => return sema.zirRepeat(block, inst), | 372 | .repeat => return sema.zirRepeat(block, inst), |
| ... | @@ -718,8 +719,8 @@ fn resolveMaybeUndefValAllowVariables( | ... | @@ -718,8 +719,8 @@ fn resolveMaybeUndefValAllowVariables( |
| 718 | if (try sema.typeHasOnePossibleValue(block, src, sema.typeOf(inst))) |opv| { | 719 | if (try sema.typeHasOnePossibleValue(block, src, sema.typeOf(inst))) |opv| { |
| 719 | return opv; | 720 | return opv; |
| 720 | } | 721 | } |
| 721 | 722 | const air_tags = sema.air_instructions.items(.tag); | |
| 722 | switch (sema.air_instructions.items(.tag)[i]) { | 723 | switch (air_tags[i]) { |
| 723 | .constant => { | 724 | .constant => { |
| 724 | const ty_pl = sema.air_instructions.items(.data)[i].ty_pl; | 725 | const ty_pl = sema.air_instructions.items(.data)[i].ty_pl; |
| 725 | return sema.air_values.items[ty_pl.payload]; | 726 | return sema.air_values.items[ty_pl.payload]; |
| ... | @@ -1248,6 +1249,11 @@ fn zirRetPtr( | ... | @@ -1248,6 +1249,11 @@ fn zirRetPtr( |
| 1248 | 1249 | ||
| 1249 | const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; | 1250 | const src: LazySrcLoc = .{ .node_offset = @bitCast(i32, extended.operand) }; |
| 1250 | try sema.requireFunctionBlock(block, src); | 1251 | try sema.requireFunctionBlock(block, src); |
| 1252 | |||
| 1253 | if (block.is_comptime) { | ||
| 1254 | return sema.analyzeComptimeAlloc(block, sema.fn_ret_ty); | ||
| 1255 | } | ||
| 1256 | |||
| 1251 | const ptr_type = try Module.simplePtrType(sema.arena, sema.fn_ret_ty, true, .One); | 1257 | const ptr_type = try Module.simplePtrType(sema.arena, sema.fn_ret_ty, true, .One); |
| 1252 | return block.addTy(.alloc, ptr_type); | 1258 | return block.addTy(.alloc, ptr_type); |
| 1253 | } | 1259 | } |
| ... | @@ -1375,21 +1381,7 @@ fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp | ... | @@ -1375,21 +1381,7 @@ fn zirAllocComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Comp |
| 1375 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 1381 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 1376 | const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; | 1382 | const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; |
| 1377 | const var_type = try sema.resolveType(block, ty_src, inst_data.operand); | 1383 | const var_type = try sema.resolveType(block, ty_src, inst_data.operand); |
| 1378 | const ptr_type = try Module.simplePtrType(sema.arena, var_type, true, .One); | 1384 | return sema.analyzeComptimeAlloc(block, var_type); |
| 1379 | |||
| 1380 | var anon_decl = try block.startAnonDecl(); | ||
| 1381 | defer anon_decl.deinit(); | ||
| 1382 | const decl = try anon_decl.finish( | ||
| 1383 | try var_type.copy(anon_decl.arena()), | ||
| 1384 | // AstGen guarantees there will be a store before the first load, so we put a value | ||
| 1385 | // here indicating there is no valid value. | ||
| 1386 | Value.initTag(.unreachable_value), | ||
| 1387 | ); | ||
| 1388 | try sema.mod.declareDeclDependency(sema.owner_decl, decl); | ||
| 1389 | return sema.addConstant(ptr_type, try Value.Tag.decl_ref_mut.create(sema.arena, .{ | ||
| 1390 | .runtime_index = block.runtime_index, | ||
| 1391 | .decl = decl, | ||
| 1392 | })); | ||
| 1393 | } | 1385 | } |
| 1394 | 1386 | ||
| 1395 | fn zirAllocInferredComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 1387 | fn zirAllocInferredComptime(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | @@ -1419,6 +1411,9 @@ fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr | ... | @@ -1419,6 +1411,9 @@ fn zirAllocMut(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileEr |
| 1419 | const var_decl_src = inst_data.src(); | 1411 | const var_decl_src = inst_data.src(); |
| 1420 | const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; | 1412 | const ty_src: LazySrcLoc = .{ .node_offset_var_decl_ty = inst_data.src_node }; |
| 1421 | const var_type = try sema.resolveType(block, ty_src, inst_data.operand); | 1413 | const var_type = try sema.resolveType(block, ty_src, inst_data.operand); |
| 1414 | if (block.is_comptime) { | ||
| 1415 | return sema.analyzeComptimeAlloc(block, var_type); | ||
| 1416 | } | ||
| 1422 | try sema.validateVarType(block, ty_src, var_type); | 1417 | try sema.validateVarType(block, ty_src, var_type); |
| 1423 | const ptr_type = try Module.simplePtrType(sema.arena, var_type, true, .One); | 1418 | const ptr_type = try Module.simplePtrType(sema.arena, var_type, true, .One); |
| 1424 | try sema.requireRuntimeBlock(block, var_decl_src); | 1419 | try sema.requireRuntimeBlock(block, var_decl_src); |
| ... | @@ -6280,6 +6275,21 @@ fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr | ... | @@ -6280,6 +6275,21 @@ fn zirRetNode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileErr |
| 6280 | return sema.analyzeRet(block, operand, src, false); | 6275 | return sema.analyzeRet(block, operand, src, false); |
| 6281 | } | 6276 | } |
| 6282 | 6277 | ||
| 6278 | fn zirRetLoad(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) CompileError!Zir.Inst.Index { | ||
| 6279 | const tracy = trace(@src()); | ||
| 6280 | defer tracy.end(); | ||
| 6281 | |||
| 6282 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | ||
| 6283 | const src = inst_data.src(); | ||
| 6284 | // TODO: when implementing functions that accept a result location pointer, | ||
| 6285 | // this logic will be updated to only do a load in case that the function's return | ||
| 6286 | // type in fact does not need a result location pointer. Until then we assume | ||
| 6287 | // the `ret_ptr` is the same as an `alloc` and do a load here. | ||
| 6288 | const ret_ptr = sema.resolveInst(inst_data.operand); | ||
| 6289 | const operand = try sema.analyzeLoad(block, src, ret_ptr, src); | ||
| 6290 | return sema.analyzeRet(block, operand, src, false); | ||
| 6291 | } | ||
| 6292 | |||
| 6283 | fn analyzeRet( | 6293 | fn analyzeRet( |
| 6284 | sema: *Sema, | 6294 | sema: *Sema, |
| 6285 | block: *Scope.Block, | 6295 | block: *Scope.Block, |
| ... | @@ -9416,3 +9426,25 @@ fn isComptimeKnown( | ... | @@ -9416,3 +9426,25 @@ fn isComptimeKnown( |
| 9416 | ) !bool { | 9426 | ) !bool { |
| 9417 | return (try sema.resolveMaybeUndefVal(block, src, inst)) != null; | 9427 | return (try sema.resolveMaybeUndefVal(block, src, inst)) != null; |
| 9418 | } | 9428 | } |
| 9429 | |||
| 9430 | fn analyzeComptimeAlloc( | ||
| 9431 | sema: *Sema, | ||
| 9432 | block: *Scope.Block, | ||
| 9433 | var_type: Type, | ||
| 9434 | ) CompileError!Air.Inst.Ref { | ||
| 9435 | const ptr_type = try Module.simplePtrType(sema.arena, var_type, true, .One); | ||
| 9436 | |||
| 9437 | var anon_decl = try block.startAnonDecl(); | ||
| 9438 | defer anon_decl.deinit(); | ||
| 9439 | const decl = try anon_decl.finish( | ||
| 9440 | try var_type.copy(anon_decl.arena()), | ||
| 9441 | // AstGen guarantees there will be a store before the first load, so we put a value | ||
| 9442 | // here indicating there is no valid value. | ||
| 9443 | Value.initTag(.unreachable_value), | ||
| 9444 | ); | ||
| 9445 | try sema.mod.declareDeclDependency(sema.owner_decl, decl); | ||
| 9446 | return sema.addConstant(ptr_type, try Value.Tag.decl_ref_mut.create(sema.arena, .{ | ||
| 9447 | .runtime_index = block.runtime_index, | ||
| 9448 | .decl = decl, | ||
| 9449 | })); | ||
| 9450 | } |
src/Zir.zig+8| ... | @@ -465,6 +465,11 @@ pub const Inst = struct { | ... | @@ -465,6 +465,11 @@ pub const Inst = struct { |
| 465 | /// Uses the `un_node` union field. | 465 | /// Uses the `un_node` union field. |
| 466 | ret_node, | 466 | ret_node, |
| 467 | /// Sends control flow back to the function's callee. | 467 | /// Sends control flow back to the function's callee. |
| 468 | /// The operand is a `ret_ptr` instruction, where the return value can be found. | ||
| 469 | /// Includes an AST node source location. | ||
| 470 | /// Uses the `un_node` union field. | ||
| 471 | ret_load, | ||
| 472 | /// Sends control flow back to the function's callee. | ||
| 468 | /// Includes an operand as the return value. | 473 | /// Includes an operand as the return value. |
| 469 | /// Includes a token source location. | 474 | /// Includes a token source location. |
| 470 | /// Uses the `un_tok` union field. | 475 | /// Uses the `un_tok` union field. |
| ... | @@ -1231,6 +1236,7 @@ pub const Inst = struct { | ... | @@ -1231,6 +1236,7 @@ pub const Inst = struct { |
| 1231 | .condbr_inline, | 1236 | .condbr_inline, |
| 1232 | .compile_error, | 1237 | .compile_error, |
| 1233 | .ret_node, | 1238 | .ret_node, |
| 1239 | .ret_load, | ||
| 1234 | .ret_coerce, | 1240 | .ret_coerce, |
| 1235 | .ret_err_value, | 1241 | .ret_err_value, |
| 1236 | .@"unreachable", | 1242 | .@"unreachable", |
| ... | @@ -1335,6 +1341,7 @@ pub const Inst = struct { | ... | @@ -1335,6 +1341,7 @@ pub const Inst = struct { |
| 1335 | .param_type = .param_type, | 1341 | .param_type = .param_type, |
| 1336 | .ref = .un_tok, | 1342 | .ref = .un_tok, |
| 1337 | .ret_node = .un_node, | 1343 | .ret_node = .un_node, |
| 1344 | .ret_load = .un_node, | ||
| 1338 | .ret_coerce = .un_tok, | 1345 | .ret_coerce = .un_tok, |
| 1339 | .ret_err_value = .str_tok, | 1346 | .ret_err_value = .str_tok, |
| 1340 | .ret_err_value_code = .str_tok, | 1347 | .ret_err_value_code = .str_tok, |
| ... | @@ -2912,6 +2919,7 @@ const Writer = struct { | ... | @@ -2912,6 +2919,7 @@ const Writer = struct { |
| 2912 | .ensure_result_used, | 2919 | .ensure_result_used, |
| 2913 | .ensure_result_non_error, | 2920 | .ensure_result_non_error, |
| 2914 | .ret_node, | 2921 | .ret_node, |
| 2922 | .ret_load, | ||
| 2915 | .resolve_inferred_alloc, | 2923 | .resolve_inferred_alloc, |
| 2916 | .optional_type, | 2924 | .optional_type, |
| 2917 | .optional_payload_safe, | 2925 | .optional_payload_safe, |
test/behavior/generics.zig+5-29| ... | @@ -28,16 +28,7 @@ test "simple generic fn" { | ... | @@ -28,16 +28,7 @@ test "simple generic fn" { |
| 28 | } | 28 | } |
| 29 | 29 | ||
| 30 | fn max(comptime T: type, a: T, b: T) T { | 30 | fn max(comptime T: type, a: T, b: T) T { |
| 31 | if (!builtin.zig_is_stage2) { | 31 | return if (a > b) a else b; |
| 32 | // TODO: stage2 is incorrectly emitting AIR that allocates a result | ||
| 33 | // value, stores to it, but then returns void instead of the result. | ||
| 34 | return if (a > b) a else b; | ||
| 35 | } | ||
| 36 | if (a > b) { | ||
| 37 | return a; | ||
| 38 | } else { | ||
| 39 | return b; | ||
| 40 | } | ||
| 41 | } | 32 | } |
| 42 | 33 | ||
| 43 | fn add(comptime a: i32, b: i32) i32 { | 34 | fn add(comptime a: i32, b: i32) i32 { |
| ... | @@ -70,29 +61,14 @@ test "fn with comptime args" { | ... | @@ -70,29 +61,14 @@ test "fn with comptime args" { |
| 70 | test "anytype params" { | 61 | test "anytype params" { |
| 71 | try expect(max_i32(12, 34) == 34); | 62 | try expect(max_i32(12, 34) == 34); |
| 72 | try expect(max_f64(1.2, 3.4) == 3.4); | 63 | try expect(max_f64(1.2, 3.4) == 3.4); |
| 73 | if (!builtin.zig_is_stage2) { | 64 | comptime { |
| 74 | // TODO: stage2 is incorrectly hitting the following problem: | 65 | try expect(max_i32(12, 34) == 34); |
| 75 | // error: unable to resolve comptime value | 66 | try expect(max_f64(1.2, 3.4) == 3.4); |
| 76 | // return max_anytype(a, b); | ||
| 77 | // ^ | ||
| 78 | comptime { | ||
| 79 | try expect(max_i32(12, 34) == 34); | ||
| 80 | try expect(max_f64(1.2, 3.4) == 3.4); | ||
| 81 | } | ||
| 82 | } | 67 | } |
| 83 | } | 68 | } |
| 84 | 69 | ||
| 85 | fn max_anytype(a: anytype, b: anytype) @TypeOf(a, b) { | 70 | fn max_anytype(a: anytype, b: anytype) @TypeOf(a, b) { |
| 86 | if (!builtin.zig_is_stage2) { | 71 | return if (a > b) a else b; |
| 87 | // TODO: stage2 is incorrectly emitting AIR that allocates a result | ||
| 88 | // value, stores to it, but then returns void instead of the result. | ||
| 89 | return if (a > b) a else b; | ||
| 90 | } | ||
| 91 | if (a > b) { | ||
| 92 | return a; | ||
| 93 | } else { | ||
| 94 | return b; | ||
| 95 | } | ||
| 96 | } | 72 | } |
| 97 | 73 | ||
| 98 | fn max_i32(a: i32, b: i32) i32 { | 74 | fn max_i32(a: i32, b: i32) i32 { |
test/stage2/cbe.zig+5-1| ... | @@ -240,6 +240,10 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -240,6 +240,10 @@ pub fn addCases(ctx: *TestContext) !void { |
| 240 | if (host_supports_custom_stack_size) { | 240 | if (host_supports_custom_stack_size) { |
| 241 | var case = ctx.exeFromCompiledC("@setEvalBranchQuota", .{}); | 241 | var case = ctx.exeFromCompiledC("@setEvalBranchQuota", .{}); |
| 242 | 242 | ||
| 243 | // TODO when adding result location support to function calls, revisit this test | ||
| 244 | // case. It can go back to what it was before, with `y` being comptime known. | ||
| 245 | // Because the ret_ptr will passed in with the inline fn call, and there will | ||
| 246 | // only be 1 store to it, and it will be comptime known. | ||
| 243 | case.addCompareOutput( | 247 | case.addCompareOutput( |
| 244 | \\pub export fn main() i32 { | 248 | \\pub export fn main() i32 { |
| 245 | \\ @setEvalBranchQuota(1001); | 249 | \\ @setEvalBranchQuota(1001); |
| ... | @@ -247,7 +251,7 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -247,7 +251,7 @@ pub fn addCases(ctx: *TestContext) !void { |
| 247 | \\ return y - 1; | 251 | \\ return y - 1; |
| 248 | \\} | 252 | \\} |
| 249 | \\ | 253 | \\ |
| 250 | \\fn rec(n: usize) callconv(.Inline) usize { | 254 | \\inline fn rec(n: i32) i32 { |
| 251 | \\ if (n <= 1) return n; | 255 | \\ if (n <= 1) return n; |
| 252 | \\ return rec(n - 1); | 256 | \\ return rec(n - 1); |
| 253 | \\} | 257 | \\} |