| author | |
| committer | |
| log | 13f04e3012b6b2eee141923f9780fce55f7a999d |
| tree | fd8d164d7926d76a1e967b89283322ee6ad88bb0 |
| parent | d481acc7dbebb5501b5fef608ee1f6b13c442c6a |
* ZIR: add two instructions:
- ret_err_value_code
- ret_err_value
* AstGen: add countDefers and utilize it to emit more efficient ZIR for
return expressions in the presence of defers.
* AstGen: implement |err| payloads for `errdefer` syntax.
- There is not an "unused capture" error for it yet.
* AstGen: `return error.Foo` syntax gets a hot path in return
expressions, using the new ZIR instructions. This also is part of
implementing inferred error sets, since we need to tell Sema to add
an error value to the inferred error set before it gets coerced.
* Sema: implement `@setCold`.
- Implement `@setCold` support for C backend.
* `@panic` and regular safety panics such as `unreachable` now properly
invoke `std.builtin.panic`.
* C backend: improve pointer and function value rendering.
* C linker: fix redundant typedefs.
* Add Type.error_set_inferred.
* Fix Value.format for enum_literal, enum_field_index, bytes.
* Remove the C backend test that checks for identical text
I measured a 14% reduction in Total ZIR Bytes from master branch
for std/os.zig.11 files changed, 650 insertions(+), 182 deletions(-)
src/AstGen.zig+124-17| ... | @@ -1860,7 +1860,7 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const ast.Nod | ... | @@ -1860,7 +1860,7 @@ fn blockExprStmts(gz: *GenZir, parent_scope: *Scope, statements: []const ast.Nod |
| 1860 | } | 1860 | } |
| 1861 | } | 1861 | } |
| 1862 | 1862 | ||
| 1863 | try genDefers(gz, parent_scope, scope, .none); | 1863 | try genDefers(gz, parent_scope, scope, .normal_only); |
| 1864 | try checkUsed(gz, parent_scope, scope); | 1864 | try checkUsed(gz, parent_scope, scope); |
| 1865 | } | 1865 | } |
| 1866 | 1866 | ||
| ... | @@ -2102,6 +2102,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner | ... | @@ -2102,6 +2102,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner |
| 2102 | .@"resume", | 2102 | .@"resume", |
| 2103 | .@"await", | 2103 | .@"await", |
| 2104 | .await_nosuspend, | 2104 | .await_nosuspend, |
| 2105 | .ret_err_value_code, | ||
| 2105 | .extended, | 2106 | .extended, |
| 2106 | => break :b false, | 2107 | => break :b false, |
| 2107 | 2108 | ||
| ... | @@ -2113,6 +2114,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner | ... | @@ -2113,6 +2114,7 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner |
| 2113 | .compile_error, | 2114 | .compile_error, |
| 2114 | .ret_node, | 2115 | .ret_node, |
| 2115 | .ret_coerce, | 2116 | .ret_coerce, |
| 2117 | .ret_err_value, | ||
| 2116 | .@"unreachable", | 2118 | .@"unreachable", |
| 2117 | .repeat, | 2119 | .repeat, |
| 2118 | .repeat_inline, | 2120 | .repeat_inline, |
| ... | @@ -2162,13 +2164,63 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner | ... | @@ -2162,13 +2164,63 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner |
| 2162 | return noreturn_src_node; | 2164 | return noreturn_src_node; |
| 2163 | } | 2165 | } |
| 2164 | 2166 | ||
| 2167 | fn countDefers(astgen: *AstGen, outer_scope: *Scope, inner_scope: *Scope) struct { | ||
| 2168 | have_any: bool, | ||
| 2169 | have_normal: bool, | ||
| 2170 | have_err: bool, | ||
| 2171 | need_err_code: bool, | ||
| 2172 | } { | ||
| 2173 | const tree = astgen.tree; | ||
| 2174 | const node_datas = tree.nodes.items(.data); | ||
| 2175 | |||
| 2176 | var have_normal = false; | ||
| 2177 | var have_err = false; | ||
| 2178 | var need_err_code = false; | ||
| 2179 | var scope = inner_scope; | ||
| 2180 | while (scope != outer_scope) { | ||
| 2181 | switch (scope.tag) { | ||
| 2182 | .gen_zir => scope = scope.cast(GenZir).?.parent, | ||
| 2183 | .local_val => scope = scope.cast(Scope.LocalVal).?.parent, | ||
| 2184 | .local_ptr => scope = scope.cast(Scope.LocalPtr).?.parent, | ||
| 2185 | .defer_normal => { | ||
| 2186 | const defer_scope = scope.cast(Scope.Defer).?; | ||
| 2187 | scope = defer_scope.parent; | ||
| 2188 | |||
| 2189 | have_normal = true; | ||
| 2190 | }, | ||
| 2191 | .defer_error => { | ||
| 2192 | const defer_scope = scope.cast(Scope.Defer).?; | ||
| 2193 | scope = defer_scope.parent; | ||
| 2194 | |||
| 2195 | have_err = true; | ||
| 2196 | |||
| 2197 | const have_err_payload = node_datas[defer_scope.defer_node].lhs != 0; | ||
| 2198 | need_err_code = need_err_code or have_err_payload; | ||
| 2199 | }, | ||
| 2200 | .namespace => unreachable, | ||
| 2201 | .top => unreachable, | ||
| 2202 | } | ||
| 2203 | } | ||
| 2204 | return .{ | ||
| 2205 | .have_any = have_normal or have_err, | ||
| 2206 | .have_normal = have_normal, | ||
| 2207 | .have_err = have_err, | ||
| 2208 | .need_err_code = need_err_code, | ||
| 2209 | }; | ||
| 2210 | } | ||
| 2211 | |||
| 2212 | const DefersToEmit = union(enum) { | ||
| 2213 | both: Zir.Inst.Ref, // err code | ||
| 2214 | both_sans_err, | ||
| 2215 | normal_only, | ||
| 2216 | }; | ||
| 2217 | |||
| 2165 | fn genDefers( | 2218 | fn genDefers( |
| 2166 | gz: *GenZir, | 2219 | gz: *GenZir, |
| 2167 | outer_scope: *Scope, | 2220 | outer_scope: *Scope, |
| 2168 | inner_scope: *Scope, | 2221 | inner_scope: *Scope, |
| 2169 | err_code: Zir.Inst.Ref, | 2222 | which_ones: DefersToEmit, |
| 2170 | ) InnerError!void { | 2223 | ) InnerError!void { |
| 2171 | _ = err_code; | ||
| 2172 | const astgen = gz.astgen; | 2224 | const astgen = gz.astgen; |
| 2173 | const tree = astgen.tree; | 2225 | const tree = astgen.tree; |
| 2174 | const node_datas = tree.nodes.items(.data); | 2226 | const node_datas = tree.nodes.items(.data); |
| ... | @@ -2191,12 +2243,37 @@ fn genDefers( | ... | @@ -2191,12 +2243,37 @@ fn genDefers( |
| 2191 | .defer_error => { | 2243 | .defer_error => { |
| 2192 | const defer_scope = scope.cast(Scope.Defer).?; | 2244 | const defer_scope = scope.cast(Scope.Defer).?; |
| 2193 | scope = defer_scope.parent; | 2245 | scope = defer_scope.parent; |
| 2194 | if (err_code == .none) continue; | 2246 | switch (which_ones) { |
| 2195 | const expr_node = node_datas[defer_scope.defer_node].rhs; | 2247 | .both_sans_err => { |
| 2196 | const prev_in_defer = gz.in_defer; | 2248 | const expr_node = node_datas[defer_scope.defer_node].rhs; |
| 2197 | gz.in_defer = true; | 2249 | const prev_in_defer = gz.in_defer; |
| 2198 | defer gz.in_defer = prev_in_defer; | 2250 | gz.in_defer = true; |
| 2199 | _ = try unusedResultExpr(gz, defer_scope.parent, expr_node); | 2251 | defer gz.in_defer = prev_in_defer; |
| 2252 | _ = try unusedResultExpr(gz, defer_scope.parent, expr_node); | ||
| 2253 | }, | ||
| 2254 | .both => |err_code| { | ||
| 2255 | const expr_node = node_datas[defer_scope.defer_node].rhs; | ||
| 2256 | const payload_token = node_datas[defer_scope.defer_node].lhs; | ||
| 2257 | const prev_in_defer = gz.in_defer; | ||
| 2258 | gz.in_defer = true; | ||
| 2259 | defer gz.in_defer = prev_in_defer; | ||
| 2260 | var local_val_scope: Scope.LocalVal = undefined; | ||
| 2261 | const sub_scope = if (payload_token == 0) defer_scope.parent else blk: { | ||
| 2262 | const ident_name = try astgen.identAsString(payload_token); | ||
| 2263 | local_val_scope = .{ | ||
| 2264 | .parent = defer_scope.parent, | ||
| 2265 | .gen_zir = gz, | ||
| 2266 | .name = ident_name, | ||
| 2267 | .inst = err_code, | ||
| 2268 | .token_src = payload_token, | ||
| 2269 | .id_cat = .@"capture", | ||
| 2270 | }; | ||
| 2271 | break :blk &local_val_scope.base; | ||
| 2272 | }; | ||
| 2273 | _ = try unusedResultExpr(gz, sub_scope, expr_node); | ||
| 2274 | }, | ||
| 2275 | .normal_only => continue, | ||
| 2276 | } | ||
| 2200 | }, | 2277 | }, |
| 2201 | .namespace => unreachable, | 2278 | .namespace => unreachable, |
| 2202 | .top => unreachable, | 2279 | .top => unreachable, |
| ... | @@ -4564,7 +4641,7 @@ fn tryExpr( | ... | @@ -4564,7 +4641,7 @@ fn tryExpr( |
| 4564 | defer then_scope.instructions.deinit(astgen.gpa); | 4641 | defer then_scope.instructions.deinit(astgen.gpa); |
| 4565 | 4642 | ||
| 4566 | const err_code = try then_scope.addUnNode(err_ops[1], operand, node); | 4643 | const err_code = try then_scope.addUnNode(err_ops[1], operand, node); |
| 4567 | try genDefers(&then_scope, &fn_block.base, scope, err_code); | 4644 | try genDefers(&then_scope, &fn_block.base, scope, .{ .both = err_code }); |
| 4568 | const then_result = try then_scope.addUnNode(.ret_node, err_code, node); | 4645 | const then_result = try then_scope.addUnNode(.ret_node, err_code, node); |
| 4569 | 4646 | ||
| 4570 | var else_scope = parent_gz.makeSubBlock(scope); | 4647 | var else_scope = parent_gz.makeSubBlock(scope); |
| ... | @@ -6090,17 +6167,37 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref | ... | @@ -6090,17 +6167,37 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref |
| 6090 | const astgen = gz.astgen; | 6167 | const astgen = gz.astgen; |
| 6091 | const tree = astgen.tree; | 6168 | const tree = astgen.tree; |
| 6092 | const node_datas = tree.nodes.items(.data); | 6169 | const node_datas = tree.nodes.items(.data); |
| 6170 | const node_tags = tree.nodes.items(.tag); | ||
| 6093 | 6171 | ||
| 6094 | if (gz.in_defer) return astgen.failNode(node, "cannot return from defer expression", .{}); | 6172 | if (gz.in_defer) return astgen.failNode(node, "cannot return from defer expression", .{}); |
| 6095 | 6173 | ||
| 6174 | const defer_outer = &astgen.fn_block.?.base; | ||
| 6175 | |||
| 6096 | const operand_node = node_datas[node].lhs; | 6176 | const operand_node = node_datas[node].lhs; |
| 6097 | if (operand_node == 0) { | 6177 | if (operand_node == 0) { |
| 6098 | // Returning a void value; skip error defers. | 6178 | // Returning a void value; skip error defers. |
| 6099 | try genDefers(gz, &astgen.fn_block.?.base, scope, .none); | 6179 | try genDefers(gz, defer_outer, scope, .normal_only); |
| 6100 | _ = try gz.addUnNode(.ret_node, .void_value, node); | 6180 | _ = try gz.addUnNode(.ret_node, .void_value, node); |
| 6101 | return Zir.Inst.Ref.unreachable_value; | 6181 | return Zir.Inst.Ref.unreachable_value; |
| 6102 | } | 6182 | } |
| 6103 | 6183 | ||
| 6184 | if (node_tags[operand_node] == .error_value) { | ||
| 6185 | // Hot path for `return error.Foo`. This bypasses result location logic as well as logic | ||
| 6186 | // for detecting whether to add something to the function's inferred error set. | ||
| 6187 | const ident_token = node_datas[operand_node].rhs; | ||
| 6188 | const err_name_str_index = try astgen.identAsString(ident_token); | ||
| 6189 | const defer_counts = countDefers(astgen, defer_outer, scope); | ||
| 6190 | if (!defer_counts.need_err_code) { | ||
| 6191 | try genDefers(gz, defer_outer, scope, .both_sans_err); | ||
| 6192 | _ = try gz.addStrTok(.ret_err_value, err_name_str_index, ident_token); | ||
| 6193 | return Zir.Inst.Ref.unreachable_value; | ||
| 6194 | } | ||
| 6195 | const err_code = try gz.addStrTok(.ret_err_value_code, err_name_str_index, ident_token); | ||
| 6196 | try genDefers(gz, defer_outer, scope, .{ .both = err_code }); | ||
| 6197 | _ = try gz.addUnNode(.ret_node, err_code, node); | ||
| 6198 | return Zir.Inst.Ref.unreachable_value; | ||
| 6199 | } | ||
| 6200 | |||
| 6104 | const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node)) .{ | 6201 | const rl: ResultLoc = if (nodeMayNeedMemoryLocation(tree, operand_node)) .{ |
| 6105 | .ptr = try gz.addNodeExtended(.ret_ptr, node), | 6202 | .ptr = try gz.addNodeExtended(.ret_ptr, node), |
| 6106 | } else .{ | 6203 | } else .{ |
| ... | @@ -6111,31 +6208,41 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref | ... | @@ -6111,31 +6208,41 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref |
| 6111 | switch (nodeMayEvalToError(tree, operand_node)) { | 6208 | switch (nodeMayEvalToError(tree, operand_node)) { |
| 6112 | .never => { | 6209 | .never => { |
| 6113 | // Returning a value that cannot be an error; skip error defers. | 6210 | // Returning a value that cannot be an error; skip error defers. |
| 6114 | try genDefers(gz, &astgen.fn_block.?.base, scope, .none); | 6211 | try genDefers(gz, defer_outer, scope, .normal_only); |
| 6115 | _ = try gz.addUnNode(.ret_node, operand, node); | 6212 | _ = try gz.addUnNode(.ret_node, operand, node); |
| 6116 | return Zir.Inst.Ref.unreachable_value; | 6213 | return Zir.Inst.Ref.unreachable_value; |
| 6117 | }, | 6214 | }, |
| 6118 | .always => { | 6215 | .always => { |
| 6119 | // Value is always an error. Emit both error defers and regular defers. | 6216 | // Value is always an error. Emit both error defers and regular defers. |
| 6120 | const err_code = try gz.addUnNode(.err_union_code, operand, node); | 6217 | const err_code = try gz.addUnNode(.err_union_code, operand, node); |
| 6121 | try genDefers(gz, &astgen.fn_block.?.base, scope, err_code); | 6218 | try genDefers(gz, defer_outer, scope, .{ .both = err_code }); |
| 6122 | _ = try gz.addUnNode(.ret_node, operand, node); | 6219 | _ = try gz.addUnNode(.ret_node, operand, node); |
| 6123 | return Zir.Inst.Ref.unreachable_value; | 6220 | return Zir.Inst.Ref.unreachable_value; |
| 6124 | }, | 6221 | }, |
| 6125 | .maybe => { | 6222 | .maybe => { |
| 6223 | const defer_counts = countDefers(astgen, defer_outer, scope); | ||
| 6224 | if (!defer_counts.have_err) { | ||
| 6225 | // Only regular defers; no branch needed. | ||
| 6226 | try genDefers(gz, defer_outer, scope, .normal_only); | ||
| 6227 | _ = try gz.addUnNode(.ret_node, operand, node); | ||
| 6228 | return Zir.Inst.Ref.unreachable_value; | ||
| 6229 | } | ||
| 6230 | |||
| 6126 | // Emit conditional branch for generating errdefers. | 6231 | // Emit conditional branch for generating errdefers. |
| 6127 | const is_err = try gz.addUnNode(.is_err, operand, node); | 6232 | const is_err = try gz.addUnNode(.is_err, operand, node); |
| 6128 | const condbr = try gz.addCondBr(.condbr, node); | 6233 | const condbr = try gz.addCondBr(.condbr, node); |
| 6129 | 6234 | ||
| 6130 | var then_scope = gz.makeSubBlock(scope); | 6235 | var then_scope = gz.makeSubBlock(scope); |
| 6131 | defer then_scope.instructions.deinit(astgen.gpa); | 6236 | defer then_scope.instructions.deinit(astgen.gpa); |
| 6132 | const err_code = try then_scope.addUnNode(.err_union_code, operand, node); | 6237 | const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{ |
| 6133 | try genDefers(&then_scope, &astgen.fn_block.?.base, scope, err_code); | 6238 | .both = try then_scope.addUnNode(.err_union_code, operand, node), |
| 6239 | }; | ||
| 6240 | try genDefers(&then_scope, defer_outer, scope, which_ones); | ||
| 6134 | _ = try then_scope.addUnNode(.ret_node, operand, node); | 6241 | _ = try then_scope.addUnNode(.ret_node, operand, node); |
| 6135 | 6242 | ||
| 6136 | var else_scope = gz.makeSubBlock(scope); | 6243 | var else_scope = gz.makeSubBlock(scope); |
| 6137 | defer else_scope.instructions.deinit(astgen.gpa); | 6244 | defer else_scope.instructions.deinit(astgen.gpa); |
| 6138 | try genDefers(&else_scope, &astgen.fn_block.?.base, scope, .none); | 6245 | try genDefers(&else_scope, defer_outer, scope, .normal_only); |
| 6139 | _ = try else_scope.addUnNode(.ret_node, operand, node); | 6246 | _ = try else_scope.addUnNode(.ret_node, operand, node); |
| 6140 | 6247 | ||
| 6141 | try setCondBrPayload(condbr, is_err, &then_scope, &else_scope); | 6248 | try setCondBrPayload(condbr, is_err, &then_scope, &else_scope); |
| ... | @@ -6885,7 +6992,7 @@ fn builtinCall( | ... | @@ -6885,7 +6992,7 @@ fn builtinCall( |
| 6885 | .field => { | 6992 | .field => { |
| 6886 | const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]); | 6993 | const field_name = try comptimeExpr(gz, scope, .{ .ty = .const_slice_u8_type }, params[1]); |
| 6887 | if (rl == .ref) { | 6994 | if (rl == .ref) { |
| 6888 | return try gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{ | 6995 | return gz.addPlNode(.field_ptr_named, node, Zir.Inst.FieldNamed{ |
| 6889 | .lhs = try expr(gz, scope, .ref, params[0]), | 6996 | .lhs = try expr(gz, scope, .ref, params[0]), |
| 6890 | .field_name = field_name, | 6997 | .field_name = field_name, |
| 6891 | }); | 6998 | }); |
src/Module.zig+4| ... | @@ -755,6 +755,7 @@ pub const Fn = struct { | ... | @@ -755,6 +755,7 @@ pub const Fn = struct { |
| 755 | rbrace_column: u16, | 755 | rbrace_column: u16, |
| 756 | 756 | ||
| 757 | state: Analysis, | 757 | state: Analysis, |
| 758 | is_cold: bool = false, | ||
| 758 | 759 | ||
| 759 | pub const Analysis = enum { | 760 | pub const Analysis = enum { |
| 760 | queued, | 761 | queued, |
| ... | @@ -3453,6 +3454,9 @@ pub fn clearDecl( | ... | @@ -3453,6 +3454,9 @@ pub fn clearDecl( |
| 3453 | for (decl.dependencies.keys()) |dep| { | 3454 | for (decl.dependencies.keys()) |dep| { |
| 3454 | dep.removeDependant(decl); | 3455 | dep.removeDependant(decl); |
| 3455 | if (dep.dependants.count() == 0 and !dep.deletion_flag) { | 3456 | if (dep.dependants.count() == 0 and !dep.deletion_flag) { |
| 3457 | log.debug("insert {*} ({s}) dependant {*} ({s}) into deletion set", .{ | ||
| 3458 | decl, decl.name, dep, dep.name, | ||
| 3459 | }); | ||
| 3456 | // We don't recursively perform a deletion here, because during the update, | 3460 | // We don't recursively perform a deletion here, because during the update, |
| 3457 | // another reference to it may turn up. | 3461 | // another reference to it may turn up. |
| 3458 | dep.deletion_flag = true; | 3462 | dep.deletion_flag = true; |
src/Sema.zig+148-63| ... | @@ -244,6 +244,7 @@ pub fn analyzeBody( | ... | @@ -244,6 +244,7 @@ pub fn analyzeBody( |
| 244 | .ptr_type => try sema.zirPtrType(block, inst), | 244 | .ptr_type => try sema.zirPtrType(block, inst), |
| 245 | .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst), | 245 | .ptr_type_simple => try sema.zirPtrTypeSimple(block, inst), |
| 246 | .ref => try sema.zirRef(block, inst), | 246 | .ref => try sema.zirRef(block, inst), |
| 247 | .ret_err_value_code => try sema.zirRetErrValueCode(block, inst), | ||
| 247 | .shl => try sema.zirShl(block, inst), | 248 | .shl => try sema.zirShl(block, inst), |
| 248 | .shr => try sema.zirShr(block, inst), | 249 | .shr => try sema.zirShr(block, inst), |
| 249 | .slice_end => try sema.zirSliceEnd(block, inst), | 250 | .slice_end => try sema.zirSliceEnd(block, inst), |
| ... | @@ -380,8 +381,9 @@ pub fn analyzeBody( | ... | @@ -380,8 +381,9 @@ pub fn analyzeBody( |
| 380 | .condbr => return sema.zirCondbr(block, inst), | 381 | .condbr => return sema.zirCondbr(block, inst), |
| 381 | .@"break" => return sema.zirBreak(block, inst), | 382 | .@"break" => return sema.zirBreak(block, inst), |
| 382 | .compile_error => return sema.zirCompileError(block, inst), | 383 | .compile_error => return sema.zirCompileError(block, inst), |
| 383 | .ret_coerce => return sema.zirRetTok(block, inst, true), | 384 | .ret_coerce => return sema.zirRetCoerce(block, inst, true), |
| 384 | .ret_node => return sema.zirRetNode(block, inst), | 385 | .ret_node => return sema.zirRetNode(block, inst), |
| 386 | .ret_err_value => return sema.zirRetErrValue(block, inst), | ||
| 385 | .@"unreachable" => return sema.zirUnreachable(block, inst), | 387 | .@"unreachable" => return sema.zirUnreachable(block, inst), |
| 386 | .repeat => return sema.zirRepeat(block, inst), | 388 | .repeat => return sema.zirRepeat(block, inst), |
| 387 | .panic => return sema.zirPanic(block, inst), | 389 | .panic => return sema.zirPanic(block, inst), |
| ... | @@ -587,6 +589,19 @@ pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) error{OutOfMemory}!*ir.In | ... | @@ -587,6 +589,19 @@ pub fn resolveInst(sema: *Sema, zir_ref: Zir.Inst.Ref) error{OutOfMemory}!*ir.In |
| 587 | return sema.inst_map.get(@intCast(u32, i)).?; | 589 | return sema.inst_map.get(@intCast(u32, i)).?; |
| 588 | } | 590 | } |
| 589 | 591 | ||
| 592 | fn resolveConstBool( | ||
| 593 | sema: *Sema, | ||
| 594 | block: *Scope.Block, | ||
| 595 | src: LazySrcLoc, | ||
| 596 | zir_ref: Zir.Inst.Ref, | ||
| 597 | ) !bool { | ||
| 598 | const air_inst = try sema.resolveInst(zir_ref); | ||
| 599 | const wanted_type = Type.initTag(.bool); | ||
| 600 | const coerced_inst = try sema.coerce(block, wanted_type, air_inst, src); | ||
| 601 | const val = try sema.resolveConstValue(block, src, coerced_inst); | ||
| 602 | return val.toBool(); | ||
| 603 | } | ||
| 604 | |||
| 590 | fn resolveConstString( | 605 | fn resolveConstString( |
| 591 | sema: *Sema, | 606 | sema: *Sema, |
| 592 | block: *Scope.Block, | 607 | block: *Scope.Block, |
| ... | @@ -1754,8 +1769,9 @@ fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError! | ... | @@ -1754,8 +1769,9 @@ fn zirRepeat(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError! |
| 1754 | fn zirPanic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!Zir.Inst.Index { | 1769 | fn zirPanic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!Zir.Inst.Index { |
| 1755 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 1770 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 1756 | const src: LazySrcLoc = inst_data.src(); | 1771 | const src: LazySrcLoc = inst_data.src(); |
| 1757 | return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirPanic", .{}); | 1772 | const msg_inst = try sema.resolveInst(inst_data.operand); |
| 1758 | //return always_noreturn; | 1773 | |
| 1774 | return sema.panicWithMsg(block, src, msg_inst); | ||
| 1759 | } | 1775 | } |
| 1760 | 1776 | ||
| 1761 | fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { | 1777 | fn zirLoop(sema: *Sema, parent_block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { |
| ... | @@ -2028,8 +2044,10 @@ fn zirSetAlignStack(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne | ... | @@ -2028,8 +2044,10 @@ fn zirSetAlignStack(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inne |
| 2028 | 2044 | ||
| 2029 | fn zirSetCold(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { | 2045 | fn zirSetCold(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { |
| 2030 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 2046 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 2031 | const src: LazySrcLoc = inst_data.src(); | 2047 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 2032 | return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirSetCold", .{}); | 2048 | const is_cold = try sema.resolveConstBool(block, operand_src, inst_data.operand); |
| 2049 | const func = sema.func orelse return; // does nothing outside a function | ||
| 2050 | func.is_cold = is_cold; | ||
| 2033 | } | 2051 | } |
| 2034 | 2052 | ||
| 2035 | fn zirSetFloatMode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { | 2053 | fn zirSetFloatMode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { |
| ... | @@ -2041,11 +2059,7 @@ fn zirSetFloatMode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner | ... | @@ -2041,11 +2059,7 @@ fn zirSetFloatMode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner |
| 2041 | fn zirSetRuntimeSafety(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { | 2059 | fn zirSetRuntimeSafety(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { |
| 2042 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; | 2060 | const inst_data = sema.code.instructions.items(.data)[inst].un_node; |
| 2043 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; | 2061 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 2044 | 2062 | block.want_safety = try sema.resolveConstBool(block, operand_src, inst_data.operand); | |
| 2045 | const op = try sema.resolveInst(inst_data.operand); | ||
| 2046 | const op_coerced = try sema.coerce(block, Type.initTag(.bool), op, operand_src); | ||
| 2047 | const b = (try sema.resolveConstValue(block, operand_src, op_coerced)).toBool(); | ||
| 2048 | block.want_safety = b; | ||
| 2049 | } | 2063 | } |
| 2050 | 2064 | ||
| 2051 | fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { | 2065 | fn zirBreakpoint(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { |
| ... | @@ -2190,21 +2204,27 @@ fn zirCall( | ... | @@ -2190,21 +2204,27 @@ fn zirCall( |
| 2190 | const extra = sema.code.extraData(Zir.Inst.Call, inst_data.payload_index); | 2204 | const extra = sema.code.extraData(Zir.Inst.Call, inst_data.payload_index); |
| 2191 | const args = sema.code.refSlice(extra.end, extra.data.args_len); | 2205 | const args = sema.code.refSlice(extra.end, extra.data.args_len); |
| 2192 | 2206 | ||
| 2193 | return sema.analyzeCall(block, extra.data.callee, func_src, call_src, modifier, ensure_result_used, args); | 2207 | const func = try sema.resolveInst(extra.data.callee); |
| 2208 | // TODO handle function calls of generic functions | ||
| 2209 | const resolved_args = try sema.arena.alloc(*Inst, args.len); | ||
| 2210 | for (args) |zir_arg, i| { | ||
| 2211 | // the args are already casted to the result of a param type instruction. | ||
| 2212 | resolved_args[i] = try sema.resolveInst(zir_arg); | ||
| 2213 | } | ||
| 2214 | |||
| 2215 | return sema.analyzeCall(block, func, func_src, call_src, modifier, ensure_result_used, resolved_args); | ||
| 2194 | } | 2216 | } |
| 2195 | 2217 | ||
| 2196 | fn analyzeCall( | 2218 | fn analyzeCall( |
| 2197 | sema: *Sema, | 2219 | sema: *Sema, |
| 2198 | block: *Scope.Block, | 2220 | block: *Scope.Block, |
| 2199 | zir_func: Zir.Inst.Ref, | 2221 | func: *ir.Inst, |
| 2200 | func_src: LazySrcLoc, | 2222 | func_src: LazySrcLoc, |
| 2201 | call_src: LazySrcLoc, | 2223 | call_src: LazySrcLoc, |
| 2202 | modifier: std.builtin.CallOptions.Modifier, | 2224 | modifier: std.builtin.CallOptions.Modifier, |
| 2203 | ensure_result_used: bool, | 2225 | ensure_result_used: bool, |
| 2204 | zir_args: []const Zir.Inst.Ref, | 2226 | args: []const *ir.Inst, |
| 2205 | ) InnerError!*ir.Inst { | 2227 | ) InnerError!*ir.Inst { |
| 2206 | const func = try sema.resolveInst(zir_func); | ||
| 2207 | |||
| 2208 | if (func.ty.zigTypeTag() != .Fn) | 2228 | if (func.ty.zigTypeTag() != .Fn) |
| 2209 | return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func.ty}); | 2229 | return sema.mod.fail(&block.base, func_src, "type '{}' not a function", .{func.ty}); |
| 2210 | 2230 | ||
| ... | @@ -2221,22 +2241,22 @@ fn analyzeCall( | ... | @@ -2221,22 +2241,22 @@ fn analyzeCall( |
| 2221 | const fn_params_len = func.ty.fnParamLen(); | 2241 | const fn_params_len = func.ty.fnParamLen(); |
| 2222 | if (func.ty.fnIsVarArgs()) { | 2242 | if (func.ty.fnIsVarArgs()) { |
| 2223 | assert(cc == .C); | 2243 | assert(cc == .C); |
| 2224 | if (zir_args.len < fn_params_len) { | 2244 | if (args.len < fn_params_len) { |
| 2225 | // TODO add error note: declared here | 2245 | // TODO add error note: declared here |
| 2226 | return sema.mod.fail( | 2246 | return sema.mod.fail( |
| 2227 | &block.base, | 2247 | &block.base, |
| 2228 | func_src, | 2248 | func_src, |
| 2229 | "expected at least {d} argument(s), found {d}", | 2249 | "expected at least {d} argument(s), found {d}", |
| 2230 | .{ fn_params_len, zir_args.len }, | 2250 | .{ fn_params_len, args.len }, |
| 2231 | ); | 2251 | ); |
| 2232 | } | 2252 | } |
| 2233 | } else if (fn_params_len != zir_args.len) { | 2253 | } else if (fn_params_len != args.len) { |
| 2234 | // TODO add error note: declared here | 2254 | // TODO add error note: declared here |
| 2235 | return sema.mod.fail( | 2255 | return sema.mod.fail( |
| 2236 | &block.base, | 2256 | &block.base, |
| 2237 | func_src, | 2257 | func_src, |
| 2238 | "expected {d} argument(s), found {d}", | 2258 | "expected {d} argument(s), found {d}", |
| 2239 | .{ fn_params_len, zir_args.len }, | 2259 | .{ fn_params_len, args.len }, |
| 2240 | ); | 2260 | ); |
| 2241 | } | 2261 | } |
| 2242 | 2262 | ||
| ... | @@ -2256,13 +2276,6 @@ fn analyzeCall( | ... | @@ -2256,13 +2276,6 @@ fn analyzeCall( |
| 2256 | }), | 2276 | }), |
| 2257 | } | 2277 | } |
| 2258 | 2278 | ||
| 2259 | // TODO handle function calls of generic functions | ||
| 2260 | const casted_args = try sema.arena.alloc(*Inst, zir_args.len); | ||
| 2261 | for (zir_args) |zir_arg, i| { | ||
| 2262 | // the args are already casted to the result of a param type instruction. | ||
| 2263 | casted_args[i] = try sema.resolveInst(zir_arg); | ||
| 2264 | } | ||
| 2265 | |||
| 2266 | const ret_type = func.ty.fnReturnType(); | 2279 | const ret_type = func.ty.fnReturnType(); |
| 2267 | 2280 | ||
| 2268 | const is_comptime_call = block.is_comptime or modifier == .compile_time; | 2281 | const is_comptime_call = block.is_comptime or modifier == .compile_time; |
| ... | @@ -2323,7 +2336,7 @@ fn analyzeCall( | ... | @@ -2323,7 +2336,7 @@ fn analyzeCall( |
| 2323 | defer sema.func = parent_func; | 2336 | defer sema.func = parent_func; |
| 2324 | 2337 | ||
| 2325 | const parent_param_inst_list = sema.param_inst_list; | 2338 | const parent_param_inst_list = sema.param_inst_list; |
| 2326 | sema.param_inst_list = casted_args; | 2339 | sema.param_inst_list = args; |
| 2327 | defer sema.param_inst_list = parent_param_inst_list; | 2340 | defer sema.param_inst_list = parent_param_inst_list; |
| 2328 | 2341 | ||
| 2329 | const parent_next_arg_index = sema.next_arg_index; | 2342 | const parent_next_arg_index = sema.next_arg_index; |
| ... | @@ -2357,7 +2370,7 @@ fn analyzeCall( | ... | @@ -2357,7 +2370,7 @@ fn analyzeCall( |
| 2357 | break :res result; | 2370 | break :res result; |
| 2358 | } else res: { | 2371 | } else res: { |
| 2359 | try sema.requireRuntimeBlock(block, call_src); | 2372 | try sema.requireRuntimeBlock(block, call_src); |
| 2360 | break :res try block.addCall(call_src, ret_type, func, casted_args); | 2373 | break :res try block.addCall(call_src, ret_type, func, args); |
| 2361 | }; | 2374 | }; |
| 2362 | 2375 | ||
| 2363 | if (ensure_result_used) { | 2376 | if (ensure_result_used) { |
| ... | @@ -3081,28 +3094,31 @@ fn funcCommon( | ... | @@ -3081,28 +3094,31 @@ fn funcCommon( |
| 3081 | ) InnerError!*Inst { | 3094 | ) InnerError!*Inst { |
| 3082 | const src: LazySrcLoc = .{ .node_offset = src_node_offset }; | 3095 | const src: LazySrcLoc = .{ .node_offset = src_node_offset }; |
| 3083 | const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; | 3096 | const ret_ty_src: LazySrcLoc = .{ .node_offset_fn_type_ret_ty = src_node_offset }; |
| 3084 | const return_type = try sema.resolveType(block, ret_ty_src, zir_return_type); | 3097 | const bare_return_type = try sema.resolveType(block, ret_ty_src, zir_return_type); |
| 3085 | 3098 | ||
| 3086 | const mod = sema.mod; | 3099 | const mod = sema.mod; |
| 3087 | 3100 | ||
| 3101 | const new_func = if (body_inst == 0) undefined else try sema.gpa.create(Module.Fn); | ||
| 3102 | errdefer if (body_inst != 0) sema.gpa.destroy(new_func); | ||
| 3103 | |||
| 3088 | const fn_ty: Type = fn_ty: { | 3104 | const fn_ty: Type = fn_ty: { |
| 3089 | // Hot path for some common function types. | 3105 | // Hot path for some common function types. |
| 3090 | if (zir_param_types.len == 0 and !var_args and align_val.tag() == .null_value and | 3106 | if (zir_param_types.len == 0 and !var_args and align_val.tag() == .null_value and |
| 3091 | !inferred_error_set) | 3107 | !inferred_error_set) |
| 3092 | { | 3108 | { |
| 3093 | if (return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { | 3109 | if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Unspecified) { |
| 3094 | break :fn_ty Type.initTag(.fn_noreturn_no_args); | 3110 | break :fn_ty Type.initTag(.fn_noreturn_no_args); |
| 3095 | } | 3111 | } |
| 3096 | 3112 | ||
| 3097 | if (return_type.zigTypeTag() == .Void and cc == .Unspecified) { | 3113 | if (bare_return_type.zigTypeTag() == .Void and cc == .Unspecified) { |
| 3098 | break :fn_ty Type.initTag(.fn_void_no_args); | 3114 | break :fn_ty Type.initTag(.fn_void_no_args); |
| 3099 | } | 3115 | } |
| 3100 | 3116 | ||
| 3101 | if (return_type.zigTypeTag() == .NoReturn and cc == .Naked) { | 3117 | if (bare_return_type.zigTypeTag() == .NoReturn and cc == .Naked) { |
| 3102 | break :fn_ty Type.initTag(.fn_naked_noreturn_no_args); | 3118 | break :fn_ty Type.initTag(.fn_naked_noreturn_no_args); |
| 3103 | } | 3119 | } |
| 3104 | 3120 | ||
| 3105 | if (return_type.zigTypeTag() == .Void and cc == .C) { | 3121 | if (bare_return_type.zigTypeTag() == .Void and cc == .C) { |
| 3106 | break :fn_ty Type.initTag(.fn_ccc_void_no_args); | 3122 | break :fn_ty Type.initTag(.fn_ccc_void_no_args); |
| 3107 | } | 3123 | } |
| 3108 | } | 3124 | } |
| ... | @@ -3120,9 +3136,13 @@ fn funcCommon( | ... | @@ -3120,9 +3136,13 @@ fn funcCommon( |
| 3120 | return mod.fail(&block.base, src, "TODO implement support for function prototypes to have alignment specified", .{}); | 3136 | return mod.fail(&block.base, src, "TODO implement support for function prototypes to have alignment specified", .{}); |
| 3121 | } | 3137 | } |
| 3122 | 3138 | ||
| 3123 | if (inferred_error_set) { | 3139 | const return_type = if (!inferred_error_set) bare_return_type else blk: { |
| 3124 | return mod.fail(&block.base, src, "TODO implement functions with inferred error sets", .{}); | 3140 | const error_set_ty = try Type.Tag.error_set_inferred.create(sema.arena, new_func); |
| 3125 | } | 3141 | break :blk try Type.Tag.error_union.create(sema.arena, .{ |
| 3142 | .error_set = error_set_ty, | ||
| 3143 | .payload = bare_return_type, | ||
| 3144 | }); | ||
| 3145 | }; | ||
| 3126 | 3146 | ||
| 3127 | break :fn_ty try Type.Tag.function.create(sema.arena, .{ | 3147 | break :fn_ty try Type.Tag.function.create(sema.arena, .{ |
| 3128 | .param_types = param_types, | 3148 | .param_types = param_types, |
| ... | @@ -3188,7 +3208,6 @@ fn funcCommon( | ... | @@ -3188,7 +3208,6 @@ fn funcCommon( |
| 3188 | const anal_state: Module.Fn.Analysis = if (is_inline) .inline_only else .queued; | 3208 | const anal_state: Module.Fn.Analysis = if (is_inline) .inline_only else .queued; |
| 3189 | 3209 | ||
| 3190 | const fn_payload = try sema.arena.create(Value.Payload.Function); | 3210 | const fn_payload = try sema.arena.create(Value.Payload.Function); |
| 3191 | const new_func = try sema.gpa.create(Module.Fn); | ||
| 3192 | new_func.* = .{ | 3211 | new_func.* = .{ |
| 3193 | .state = anal_state, | 3212 | .state = anal_state, |
| 3194 | .zir_body_inst = body_inst, | 3213 | .zir_body_inst = body_inst, |
| ... | @@ -4542,6 +4561,12 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError! | ... | @@ -4542,6 +4561,12 @@ fn zirImport(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError! |
| 4542 | return mod.constType(sema.arena, src, file_root_decl.ty); | 4561 | return mod.constType(sema.arena, src, file_root_decl.ty); |
| 4543 | } | 4562 | } |
| 4544 | 4563 | ||
| 4564 | fn zirRetErrValueCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { | ||
| 4565 | _ = block; | ||
| 4566 | _ = inst; | ||
| 4567 | return sema.mod.fail(&block.base, sema.src, "TODO implement zirRetErrValueCode", .{}); | ||
| 4568 | } | ||
| 4569 | |||
| 4545 | fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { | 4570 | fn zirShl(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { |
| 4546 | const tracy = trace(@src()); | 4571 | const tracy = trace(@src()); |
| 4547 | defer tracy.end(); | 4572 | defer tracy.end(); |
| ... | @@ -5388,7 +5413,24 @@ fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerE | ... | @@ -5388,7 +5413,24 @@ fn zirUnreachable(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerE |
| 5388 | } | 5413 | } |
| 5389 | } | 5414 | } |
| 5390 | 5415 | ||
| 5391 | fn zirRetTok( | 5416 | fn zirRetErrValue( |
| 5417 | sema: *Sema, | ||
| 5418 | block: *Scope.Block, | ||
| 5419 | inst: Zir.Inst.Index, | ||
| 5420 | ) InnerError!Zir.Inst.Index { | ||
| 5421 | const inst_data = sema.code.instructions.items(.data)[inst].str_tok; | ||
| 5422 | const err_name = inst_data.get(sema.code); | ||
| 5423 | const src = inst_data.src(); | ||
| 5424 | |||
| 5425 | // Add the error tag to the inferred error set of the in-scope function. | ||
| 5426 | // Return the error code from the function. | ||
| 5427 | |||
| 5428 | _ = inst_data; | ||
| 5429 | _ = err_name; | ||
| 5430 | return sema.mod.fail(&block.base, src, "TODO: Sema.zirRetErrValueCode", .{}); | ||
| 5431 | } | ||
| 5432 | |||
| 5433 | fn zirRetCoerce( | ||
| 5392 | sema: *Sema, | 5434 | sema: *Sema, |
| 5393 | block: *Scope.Block, | 5435 | block: *Scope.Block, |
| 5394 | inst: Zir.Inst.Index, | 5436 | inst: Zir.Inst.Index, |
| ... | @@ -6195,6 +6237,10 @@ fn zirFuncExtended( | ... | @@ -6195,6 +6237,10 @@ fn zirFuncExtended( |
| 6195 | src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; | 6237 | src_locs = sema.code.extraData(Zir.Inst.Func.SrcLocs, extra_index).data; |
| 6196 | } | 6238 | } |
| 6197 | 6239 | ||
| 6240 | const is_var_args = small.is_var_args; | ||
| 6241 | const is_inferred_error = small.is_inferred_error; | ||
| 6242 | const is_extern = small.is_extern; | ||
| 6243 | |||
| 6198 | return sema.funcCommon( | 6244 | return sema.funcCommon( |
| 6199 | block, | 6245 | block, |
| 6200 | extra.data.src_node, | 6246 | extra.data.src_node, |
| ... | @@ -6203,9 +6249,9 @@ fn zirFuncExtended( | ... | @@ -6203,9 +6249,9 @@ fn zirFuncExtended( |
| 6203 | extra.data.return_type, | 6249 | extra.data.return_type, |
| 6204 | cc, | 6250 | cc, |
| 6205 | align_val, | 6251 | align_val, |
| 6206 | small.is_var_args, | 6252 | is_var_args, |
| 6207 | small.is_inferred_error, | 6253 | is_inferred_error, |
| 6208 | small.is_extern, | 6254 | is_extern, |
| 6209 | src_locs, | 6255 | src_locs, |
| 6210 | lib_name, | 6256 | lib_name, |
| 6211 | ); | 6257 | ); |
| ... | @@ -6357,15 +6403,51 @@ fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id: | ... | @@ -6357,15 +6403,51 @@ fn addSafetyCheck(sema: *Sema, parent_block: *Scope.Block, ok: *Inst, panic_id: |
| 6357 | try parent_block.instructions.append(sema.gpa, &block_inst.base); | 6403 | try parent_block.instructions.append(sema.gpa, &block_inst.base); |
| 6358 | } | 6404 | } |
| 6359 | 6405 | ||
| 6360 | fn safetyPanic(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, panic_id: PanicId) !Zir.Inst.Index { | 6406 | fn panicWithMsg( |
| 6361 | _ = sema; | 6407 | sema: *Sema, |
| 6362 | _ = panic_id; | 6408 | block: *Scope.Block, |
| 6363 | // TODO Once we have a panic function to call, call it here instead of breakpoint. | 6409 | src: LazySrcLoc, |
| 6364 | _ = try block.addNoOp(src, Type.initTag(.void), .breakpoint); | 6410 | msg_inst: *ir.Inst, |
| 6365 | _ = try block.addNoOp(src, Type.initTag(.noreturn), .unreach); | 6411 | ) !Zir.Inst.Index { |
| 6412 | const mod = sema.mod; | ||
| 6413 | const arena = sema.arena; | ||
| 6414 | const panic_fn = try sema.getBuiltin(block, src, "panic"); | ||
| 6415 | const unresolved_stack_trace_ty = try sema.getBuiltinType(block, src, "StackTrace"); | ||
| 6416 | const stack_trace_ty = try sema.resolveTypeFields(block, src, unresolved_stack_trace_ty); | ||
| 6417 | const ptr_stack_trace_ty = try mod.simplePtrType(arena, stack_trace_ty, true, .One); | ||
| 6418 | const null_stack_trace = try mod.constInst(arena, src, .{ | ||
| 6419 | .ty = try mod.optionalType(arena, ptr_stack_trace_ty), | ||
| 6420 | .val = Value.initTag(.null_value), | ||
| 6421 | }); | ||
| 6422 | const args = try arena.create([2]*ir.Inst); | ||
| 6423 | args.* = .{ msg_inst, null_stack_trace }; | ||
| 6424 | _ = try sema.analyzeCall(block, panic_fn, src, src, .auto, false, args); | ||
| 6366 | return always_noreturn; | 6425 | return always_noreturn; |
| 6367 | } | 6426 | } |
| 6368 | 6427 | ||
| 6428 | fn safetyPanic( | ||
| 6429 | sema: *Sema, | ||
| 6430 | block: *Scope.Block, | ||
| 6431 | src: LazySrcLoc, | ||
| 6432 | panic_id: PanicId, | ||
| 6433 | ) !Zir.Inst.Index { | ||
| 6434 | const mod = sema.mod; | ||
| 6435 | const arena = sema.arena; | ||
| 6436 | const msg = switch (panic_id) { | ||
| 6437 | .unreach => "reached unreachable code", | ||
| 6438 | .unwrap_null => "attempt to use null value", | ||
| 6439 | .unwrap_errunion => "unreachable error occurred", | ||
| 6440 | .cast_to_null => "cast causes pointer to be null", | ||
| 6441 | .incorrect_alignment => "incorrect alignment", | ||
| 6442 | .invalid_error_code => "invalid error code", | ||
| 6443 | }; | ||
| 6444 | const msg_inst = try mod.constInst(arena, src, .{ | ||
| 6445 | .ty = Type.initTag(.const_slice_u8), | ||
| 6446 | .val = try Value.Tag.ref_val.create(arena, try Value.Tag.bytes.create(arena, msg)), | ||
| 6447 | }); | ||
| 6448 | return sema.panicWithMsg(block, src, msg_inst); | ||
| 6449 | } | ||
| 6450 | |||
| 6369 | fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { | 6451 | fn emitBackwardBranch(sema: *Sema, block: *Scope.Block, src: LazySrcLoc) !void { |
| 6370 | sema.branch_count += 1; | 6452 | sema.branch_count += 1; |
| 6371 | if (sema.branch_count > sema.branch_quota) { | 6453 | if (sema.branch_count > sema.branch_quota) { |
| ... | @@ -7377,15 +7459,13 @@ fn wrapOptional(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) | ... | @@ -7377,15 +7459,13 @@ fn wrapOptional(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) |
| 7377 | } | 7459 | } |
| 7378 | 7460 | ||
| 7379 | fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { | 7461 | fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst) !*Inst { |
| 7380 | // TODO deal with inferred error sets | ||
| 7381 | const err_union = dest_type.castTag(.error_union).?; | 7462 | const err_union = dest_type.castTag(.error_union).?; |
| 7382 | if (inst.value()) |val| { | 7463 | if (inst.value()) |val| { |
| 7383 | const to_wrap = if (inst.ty.zigTypeTag() != .ErrorSet) blk: { | 7464 | if (inst.ty.zigTypeTag() != .ErrorSet) { |
| 7384 | _ = try sema.coerce(block, err_union.data.payload, inst, inst.src); | 7465 | _ = try sema.coerce(block, err_union.data.payload, inst, inst.src); |
| 7385 | break :blk val; | ||
| 7386 | } else switch (err_union.data.error_set.tag()) { | 7466 | } else switch (err_union.data.error_set.tag()) { |
| 7387 | .anyerror => val, | 7467 | .anyerror => {}, |
| 7388 | .error_set_single => blk: { | 7468 | .error_set_single => { |
| 7389 | const expected_name = val.castTag(.@"error").?.data.name; | 7469 | const expected_name = val.castTag(.@"error").?.data.name; |
| 7390 | const n = err_union.data.error_set.castTag(.error_set_single).?.data; | 7470 | const n = err_union.data.error_set.castTag(.error_set_single).?.data; |
| 7391 | if (!mem.eql(u8, expected_name, n)) { | 7471 | if (!mem.eql(u8, expected_name, n)) { |
| ... | @@ -7396,9 +7476,8 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst | ... | @@ -7396,9 +7476,8 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst |
| 7396 | .{ err_union.data.error_set, inst.ty }, | 7476 | .{ err_union.data.error_set, inst.ty }, |
| 7397 | ); | 7477 | ); |
| 7398 | } | 7478 | } |
| 7399 | break :blk val; | ||
| 7400 | }, | 7479 | }, |
| 7401 | .error_set => blk: { | 7480 | .error_set => { |
| 7402 | const expected_name = val.castTag(.@"error").?.data.name; | 7481 | const expected_name = val.castTag(.@"error").?.data.name; |
| 7403 | const error_set = err_union.data.error_set.castTag(.error_set).?.data; | 7482 | const error_set = err_union.data.error_set.castTag(.error_set).?.data; |
| 7404 | const names = error_set.names_ptr[0..error_set.names_len]; | 7483 | const names = error_set.names_ptr[0..error_set.names_len]; |
| ... | @@ -7415,18 +7494,14 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst | ... | @@ -7415,18 +7494,14 @@ fn wrapErrorUnion(sema: *Sema, block: *Scope.Block, dest_type: Type, inst: *Inst |
| 7415 | .{ err_union.data.error_set, inst.ty }, | 7494 | .{ err_union.data.error_set, inst.ty }, |
| 7416 | ); | 7495 | ); |
| 7417 | } | 7496 | } |
| 7418 | break :blk val; | ||
| 7419 | }, | 7497 | }, |
| 7420 | else => unreachable, | 7498 | else => unreachable, |
| 7421 | }; | 7499 | } |
| 7422 | 7500 | ||
| 7423 | return sema.mod.constInst(sema.arena, inst.src, .{ | 7501 | return sema.mod.constInst(sema.arena, inst.src, .{ |
| 7424 | .ty = dest_type, | 7502 | .ty = dest_type, |
| 7425 | // creating a SubValue for the error_union payload | 7503 | // creating a SubValue for the error_union payload |
| 7426 | .val = try Value.Tag.error_union.create( | 7504 | .val = try Value.Tag.error_union.create(sema.arena, val), |
| 7427 | sema.arena, | ||
| 7428 | to_wrap, | ||
| 7429 | ), | ||
| 7430 | }); | 7505 | }); |
| 7431 | } | 7506 | } |
| 7432 | 7507 | ||
| ... | @@ -7573,12 +7648,12 @@ fn resolveBuiltinTypeFields( | ... | @@ -7573,12 +7648,12 @@ fn resolveBuiltinTypeFields( |
| 7573 | return sema.resolveTypeFields(block, src, resolved_ty); | 7648 | return sema.resolveTypeFields(block, src, resolved_ty); |
| 7574 | } | 7649 | } |
| 7575 | 7650 | ||
| 7576 | fn getBuiltinType( | 7651 | fn getBuiltin( |
| 7577 | sema: *Sema, | 7652 | sema: *Sema, |
| 7578 | block: *Scope.Block, | 7653 | block: *Scope.Block, |
| 7579 | src: LazySrcLoc, | 7654 | src: LazySrcLoc, |
| 7580 | name: []const u8, | 7655 | name: []const u8, |
| 7581 | ) InnerError!Type { | 7656 | ) InnerError!*ir.Inst { |
| 7582 | const mod = sema.mod; | 7657 | const mod = sema.mod; |
| 7583 | const std_pkg = mod.root_pkg.table.get("std").?; | 7658 | const std_pkg = mod.root_pkg.table.get("std").?; |
| 7584 | const std_file = (mod.importPkg(std_pkg) catch unreachable).file; | 7659 | const std_file = (mod.importPkg(std_pkg) catch unreachable).file; |
| ... | @@ -7596,7 +7671,16 @@ fn getBuiltinType( | ... | @@ -7596,7 +7671,16 @@ fn getBuiltinType( |
| 7596 | builtin_ty.getNamespace().?, | 7671 | builtin_ty.getNamespace().?, |
| 7597 | name, | 7672 | name, |
| 7598 | ); | 7673 | ); |
| 7599 | const ty_inst = try sema.analyzeLoad(block, src, opt_ty_inst.?, src); | 7674 | return sema.analyzeLoad(block, src, opt_ty_inst.?, src); |
| 7675 | } | ||
| 7676 | |||
| 7677 | fn getBuiltinType( | ||
| 7678 | sema: *Sema, | ||
| 7679 | block: *Scope.Block, | ||
| 7680 | src: LazySrcLoc, | ||
| 7681 | name: []const u8, | ||
| 7682 | ) InnerError!Type { | ||
| 7683 | const ty_inst = try sema.getBuiltin(block, src, name); | ||
| 7600 | return sema.resolveAirAsType(block, src, ty_inst); | 7684 | return sema.resolveAirAsType(block, src, ty_inst); |
| 7601 | } | 7685 | } |
| 7602 | 7686 | ||
| ... | @@ -7662,6 +7746,7 @@ fn typeHasOnePossibleValue( | ... | @@ -7662,6 +7746,7 @@ fn typeHasOnePossibleValue( |
| 7662 | .error_union, | 7746 | .error_union, |
| 7663 | .error_set, | 7747 | .error_set, |
| 7664 | .error_set_single, | 7748 | .error_set_single, |
| 7749 | .error_set_inferred, | ||
| 7665 | .@"opaque", | 7750 | .@"opaque", |
| 7666 | .var_args_param, | 7751 | .var_args_param, |
| 7667 | .manyptr_u8, | 7752 | .manyptr_u8, |
src/Zir.zig+23-4| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | //! Zig Intermediate Representation. Astgen.zig converts AST nodes to these | 1 | //! Zig Intermediate Representation. Astgen.zig converts AST nodes to these |
| 2 | //! untyped IR instructions. Next, Sema.zig processes these into TZIR. | 2 | //! untyped IR instructions. Next, Sema.zig processes these into AIR. |
| 3 | //! The minimum amount of information needed to represent a list of ZIR instructions. | 3 | //! The minimum amount of information needed to represent a list of ZIR instructions. |
| 4 | //! Once this structure is completed, it can be used to generate TZIR, followed by | 4 | //! Once this structure is completed, it can be used to generate AIR, followed by |
| 5 | //! machine code, without any memory access into the AST tree token list, node list, | 5 | //! machine code, without any memory access into the AST tree token list, node list, |
| 6 | //! or source bytes. Exceptions include: | 6 | //! or source bytes. Exceptions include: |
| 7 | //! * Compile errors, which may need to reach into these data structures to | 7 | //! * Compile errors, which may need to reach into these data structures to |
| ... | @@ -416,8 +416,8 @@ pub const Inst = struct { | ... | @@ -416,8 +416,8 @@ pub const Inst = struct { |
| 416 | /// A labeled block of code that loops forever. At the end of the body will have either | 416 | /// A labeled block of code that loops forever. At the end of the body will have either |
| 417 | /// a `repeat` instruction or a `repeat_inline` instruction. | 417 | /// a `repeat` instruction or a `repeat_inline` instruction. |
| 418 | /// Uses the `pl_node` field. The AST node is either a for loop or while loop. | 418 | /// Uses the `pl_node` field. The AST node is either a for loop or while loop. |
| 419 | /// This ZIR instruction is needed because TZIR does not (yet?) match ZIR, and Sema | 419 | /// This ZIR instruction is needed because AIR does not (yet?) match ZIR, and Sema |
| 420 | /// needs to emit more than 1 TZIR block for this instruction. | 420 | /// needs to emit more than 1 AIR block for this instruction. |
| 421 | /// The payload is `Block`. | 421 | /// The payload is `Block`. |
| 422 | loop, | 422 | loop, |
| 423 | /// Sends runtime control flow back to the beginning of the current block. | 423 | /// Sends runtime control flow back to the beginning of the current block. |
| ... | @@ -466,6 +466,19 @@ pub const Inst = struct { | ... | @@ -466,6 +466,19 @@ pub const Inst = struct { |
| 466 | /// Uses the `un_tok` union field. | 466 | /// Uses the `un_tok` union field. |
| 467 | /// The operand needs to get coerced to the function's return type. | 467 | /// The operand needs to get coerced to the function's return type. |
| 468 | ret_coerce, | 468 | ret_coerce, |
| 469 | /// Sends control flow back to the function's callee. | ||
| 470 | /// The return operand is `error.foo` where `foo` is given by the string. | ||
| 471 | /// If the current function has an inferred error set, the error given by the | ||
| 472 | /// name is added to it. | ||
| 473 | /// Uses the `str_tok` union field. | ||
| 474 | ret_err_value, | ||
| 475 | /// A string name is provided which is an anonymous error set value. | ||
| 476 | /// If the current function has an inferred error set, the error given by the | ||
| 477 | /// name is added to it. | ||
| 478 | /// Results in the error code. Note that control flow is not diverted with | ||
| 479 | /// this instruction; a following 'ret' instruction will do the diversion. | ||
| 480 | /// Uses the `str_tok` union field. | ||
| 481 | ret_err_value_code, | ||
| 469 | /// Create a pointer type that does not have a sentinel, alignment, or bit range specified. | 482 | /// Create a pointer type that does not have a sentinel, alignment, or bit range specified. |
| 470 | /// Uses the `ptr_type_simple` union field. | 483 | /// Uses the `ptr_type_simple` union field. |
| 471 | ptr_type_simple, | 484 | ptr_type_simple, |
| ... | @@ -1193,6 +1206,7 @@ pub const Inst = struct { | ... | @@ -1193,6 +1206,7 @@ pub const Inst = struct { |
| 1193 | .@"resume", | 1206 | .@"resume", |
| 1194 | .@"await", | 1207 | .@"await", |
| 1195 | .await_nosuspend, | 1208 | .await_nosuspend, |
| 1209 | .ret_err_value_code, | ||
| 1196 | .extended, | 1210 | .extended, |
| 1197 | => false, | 1211 | => false, |
| 1198 | 1212 | ||
| ... | @@ -1203,6 +1217,7 @@ pub const Inst = struct { | ... | @@ -1203,6 +1217,7 @@ pub const Inst = struct { |
| 1203 | .compile_error, | 1217 | .compile_error, |
| 1204 | .ret_node, | 1218 | .ret_node, |
| 1205 | .ret_coerce, | 1219 | .ret_coerce, |
| 1220 | .ret_err_value, | ||
| 1206 | .@"unreachable", | 1221 | .@"unreachable", |
| 1207 | .repeat, | 1222 | .repeat, |
| 1208 | .repeat_inline, | 1223 | .repeat_inline, |
| ... | @@ -1307,6 +1322,8 @@ pub const Inst = struct { | ... | @@ -1307,6 +1322,8 @@ pub const Inst = struct { |
| 1307 | .ref = .un_tok, | 1322 | .ref = .un_tok, |
| 1308 | .ret_node = .un_node, | 1323 | .ret_node = .un_node, |
| 1309 | .ret_coerce = .un_tok, | 1324 | .ret_coerce = .un_tok, |
| 1325 | .ret_err_value = .str_tok, | ||
| 1326 | .ret_err_value_code = .str_tok, | ||
| 1310 | .ptr_type_simple = .ptr_type_simple, | 1327 | .ptr_type_simple = .ptr_type_simple, |
| 1311 | .ptr_type = .ptr_type, | 1328 | .ptr_type = .ptr_type, |
| 1312 | .slice_start = .pl_node, | 1329 | .slice_start = .pl_node, |
| ... | @@ -3077,6 +3094,8 @@ const Writer = struct { | ... | @@ -3077,6 +3094,8 @@ const Writer = struct { |
| 3077 | .decl_val, | 3094 | .decl_val, |
| 3078 | .import, | 3095 | .import, |
| 3079 | .arg, | 3096 | .arg, |
| 3097 | .ret_err_value, | ||
| 3098 | .ret_err_value_code, | ||
| 3080 | => try self.writeStrTok(stream, inst), | 3099 | => try self.writeStrTok(stream, inst), |
| 3081 | 3100 | ||
| 3082 | .func => try self.writeFunc(stream, inst, false), | 3101 | .func => try self.writeFunc(stream, inst, false), |
src/air.zig+11-11| ... | @@ -672,15 +672,15 @@ pub const Body = struct { | ... | @@ -672,15 +672,15 @@ pub const Body = struct { |
| 672 | /// For debugging purposes, prints a function representation to stderr. | 672 | /// For debugging purposes, prints a function representation to stderr. |
| 673 | pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void { | 673 | pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void { |
| 674 | const allocator = old_module.gpa; | 674 | const allocator = old_module.gpa; |
| 675 | var ctx: DumpTzir = .{ | 675 | var ctx: DumpAir = .{ |
| 676 | .allocator = allocator, | 676 | .allocator = allocator, |
| 677 | .arena = std.heap.ArenaAllocator.init(allocator), | 677 | .arena = std.heap.ArenaAllocator.init(allocator), |
| 678 | .old_module = &old_module, | 678 | .old_module = &old_module, |
| 679 | .module_fn = module_fn, | 679 | .module_fn = module_fn, |
| 680 | .indent = 2, | 680 | .indent = 2, |
| 681 | .inst_table = DumpTzir.InstTable.init(allocator), | 681 | .inst_table = DumpAir.InstTable.init(allocator), |
| 682 | .partial_inst_table = DumpTzir.InstTable.init(allocator), | 682 | .partial_inst_table = DumpAir.InstTable.init(allocator), |
| 683 | .const_table = DumpTzir.InstTable.init(allocator), | 683 | .const_table = DumpAir.InstTable.init(allocator), |
| 684 | }; | 684 | }; |
| 685 | defer ctx.inst_table.deinit(); | 685 | defer ctx.inst_table.deinit(); |
| 686 | defer ctx.partial_inst_table.deinit(); | 686 | defer ctx.partial_inst_table.deinit(); |
| ... | @@ -695,12 +695,12 @@ pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void { | ... | @@ -695,12 +695,12 @@ pub fn dumpFn(old_module: Module, module_fn: *Module.Fn) void { |
| 695 | .dependency_failure => std.debug.print("(dependency_failure)", .{}), | 695 | .dependency_failure => std.debug.print("(dependency_failure)", .{}), |
| 696 | .success => { | 696 | .success => { |
| 697 | const writer = std.io.getStdErr().writer(); | 697 | const writer = std.io.getStdErr().writer(); |
| 698 | ctx.dump(module_fn.body, writer) catch @panic("failed to dump TZIR"); | 698 | ctx.dump(module_fn.body, writer) catch @panic("failed to dump AIR"); |
| 699 | }, | 699 | }, |
| 700 | } | 700 | } |
| 701 | } | 701 | } |
| 702 | 702 | ||
| 703 | const DumpTzir = struct { | 703 | const DumpAir = struct { |
| 704 | allocator: *std.mem.Allocator, | 704 | allocator: *std.mem.Allocator, |
| 705 | arena: std.heap.ArenaAllocator, | 705 | arena: std.heap.ArenaAllocator, |
| 706 | old_module: *const Module, | 706 | old_module: *const Module, |
| ... | @@ -718,7 +718,7 @@ const DumpTzir = struct { | ... | @@ -718,7 +718,7 @@ const DumpTzir = struct { |
| 718 | /// TODO: Improve this code to include a stack of Body and store the instructions | 718 | /// TODO: Improve this code to include a stack of Body and store the instructions |
| 719 | /// in there. Now we are putting all the instructions in a function local table, | 719 | /// in there. Now we are putting all the instructions in a function local table, |
| 720 | /// however instructions that are in a Body can be thown away when the Body ends. | 720 | /// however instructions that are in a Body can be thown away when the Body ends. |
| 721 | fn dump(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) !void { | 721 | fn dump(dtz: *DumpAir, body: Body, writer: std.fs.File.Writer) !void { |
| 722 | // First pass to pre-populate the table so that we can show even invalid references. | 722 | // First pass to pre-populate the table so that we can show even invalid references. |
| 723 | // Must iterate the same order we iterate the second time. | 723 | // Must iterate the same order we iterate the second time. |
| 724 | // We also look for constants and put them in the const_table. | 724 | // We also look for constants and put them in the const_table. |
| ... | @@ -737,7 +737,7 @@ const DumpTzir = struct { | ... | @@ -737,7 +737,7 @@ const DumpTzir = struct { |
| 737 | return dtz.dumpBody(body, writer); | 737 | return dtz.dumpBody(body, writer); |
| 738 | } | 738 | } |
| 739 | 739 | ||
| 740 | fn fetchInstsAndResolveConsts(dtz: *DumpTzir, body: Body) error{OutOfMemory}!void { | 740 | fn fetchInstsAndResolveConsts(dtz: *DumpAir, body: Body) error{OutOfMemory}!void { |
| 741 | for (body.instructions) |inst| { | 741 | for (body.instructions) |inst| { |
| 742 | try dtz.inst_table.put(inst, dtz.next_index); | 742 | try dtz.inst_table.put(inst, dtz.next_index); |
| 743 | dtz.next_index += 1; | 743 | dtz.next_index += 1; |
| ... | @@ -865,7 +865,7 @@ const DumpTzir = struct { | ... | @@ -865,7 +865,7 @@ const DumpTzir = struct { |
| 865 | } | 865 | } |
| 866 | } | 866 | } |
| 867 | 867 | ||
| 868 | fn dumpBody(dtz: *DumpTzir, body: Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void { | 868 | fn dumpBody(dtz: *DumpAir, body: Body, writer: std.fs.File.Writer) (std.fs.File.WriteError || error{OutOfMemory})!void { |
| 869 | for (body.instructions) |inst| { | 869 | for (body.instructions) |inst| { |
| 870 | const my_index = dtz.next_partial_index; | 870 | const my_index = dtz.next_partial_index; |
| 871 | try dtz.partial_inst_table.put(inst, my_index); | 871 | try dtz.partial_inst_table.put(inst, my_index); |
| ... | @@ -1150,7 +1150,7 @@ const DumpTzir = struct { | ... | @@ -1150,7 +1150,7 @@ const DumpTzir = struct { |
| 1150 | } | 1150 | } |
| 1151 | } | 1151 | } |
| 1152 | 1152 | ||
| 1153 | fn writeInst(dtz: *DumpTzir, writer: std.fs.File.Writer, inst: *Inst) !?usize { | 1153 | fn writeInst(dtz: *DumpAir, writer: std.fs.File.Writer, inst: *Inst) !?usize { |
| 1154 | if (dtz.partial_inst_table.get(inst)) |operand_index| { | 1154 | if (dtz.partial_inst_table.get(inst)) |operand_index| { |
| 1155 | try writer.print("%{d}", .{operand_index}); | 1155 | try writer.print("%{d}", .{operand_index}); |
| 1156 | return null; | 1156 | return null; |
| ... | @@ -1166,7 +1166,7 @@ const DumpTzir = struct { | ... | @@ -1166,7 +1166,7 @@ const DumpTzir = struct { |
| 1166 | } | 1166 | } |
| 1167 | } | 1167 | } |
| 1168 | 1168 | ||
| 1169 | fn findConst(dtz: *DumpTzir, operand: *Inst) !void { | 1169 | fn findConst(dtz: *DumpAir, operand: *Inst) !void { |
| 1170 | if (operand.tag == .constant) { | 1170 | if (operand.tag == .constant) { |
| 1171 | try dtz.const_table.put(operand, dtz.next_const_index); | 1171 | try dtz.const_table.put(operand, dtz.next_const_index); |
| 1172 | dtz.next_const_index += 1; | 1172 | dtz.next_const_index += 1; |
src/codegen/c.zig+229-60| ... | @@ -39,7 +39,12 @@ const BlockData = struct { | ... | @@ -39,7 +39,12 @@ const BlockData = struct { |
| 39 | }; | 39 | }; |
| 40 | 40 | ||
| 41 | pub const CValueMap = std.AutoHashMap(*Inst, CValue); | 41 | pub const CValueMap = std.AutoHashMap(*Inst, CValue); |
| 42 | pub const TypedefMap = std.HashMap(Type, struct { name: []const u8, rendered: []u8 }, Type.HashContext, std.hash_map.default_max_load_percentage); | 42 | pub const TypedefMap = std.HashMap( |
| 43 | Type, | ||
| 44 | struct { name: []const u8, rendered: []u8 }, | ||
| 45 | Type.HashContext, | ||
| 46 | std.hash_map.default_max_load_percentage, | ||
| 47 | ); | ||
| 43 | 48 | ||
| 44 | fn formatTypeAsCIdentifier( | 49 | fn formatTypeAsCIdentifier( |
| 45 | data: Type, | 50 | data: Type, |
| ... | @@ -151,14 +156,49 @@ pub const Object = struct { | ... | @@ -151,14 +156,49 @@ pub const Object = struct { |
| 151 | render_ty = render_ty.elemType(); | 156 | render_ty = render_ty.elemType(); |
| 152 | } | 157 | } |
| 153 | 158 | ||
| 154 | try o.dg.renderType(w, render_ty); | 159 | if (render_ty.zigTypeTag() == .Fn) { |
| 155 | 160 | const ret_ty = render_ty.fnReturnType(); | |
| 156 | const const_prefix = switch (mutability) { | 161 | if (ret_ty.zigTypeTag() == .NoReturn) { |
| 157 | .Const => "const ", | 162 | // noreturn attribute is not allowed here. |
| 158 | .Mut => "", | 163 | try w.writeAll("void"); |
| 159 | }; | 164 | } else { |
| 160 | try w.print(" {s}", .{const_prefix}); | 165 | try o.dg.renderType(w, ret_ty); |
| 161 | try o.writeCValue(w, name); | 166 | } |
| 167 | try w.writeAll(" (*"); | ||
| 168 | switch (mutability) { | ||
| 169 | .Const => try w.writeAll("const "), | ||
| 170 | .Mut => {}, | ||
| 171 | } | ||
| 172 | try o.writeCValue(w, name); | ||
| 173 | try w.writeAll(")("); | ||
| 174 | const param_len = render_ty.fnParamLen(); | ||
| 175 | const is_var_args = render_ty.fnIsVarArgs(); | ||
| 176 | if (param_len == 0 and !is_var_args) | ||
| 177 | try w.writeAll("void") | ||
| 178 | else { | ||
| 179 | var index: usize = 0; | ||
| 180 | while (index < param_len) : (index += 1) { | ||
| 181 | if (index > 0) { | ||
| 182 | try w.writeAll(", "); | ||
| 183 | } | ||
| 184 | try o.dg.renderType(w, render_ty.fnParamType(index)); | ||
| 185 | } | ||
| 186 | } | ||
| 187 | if (is_var_args) { | ||
| 188 | if (param_len != 0) try w.writeAll(", "); | ||
| 189 | try w.writeAll("..."); | ||
| 190 | } | ||
| 191 | try w.writeByte(')'); | ||
| 192 | } else { | ||
| 193 | try o.dg.renderType(w, render_ty); | ||
| 194 | |||
| 195 | const const_prefix = switch (mutability) { | ||
| 196 | .Const => "const ", | ||
| 197 | .Mut => "", | ||
| 198 | }; | ||
| 199 | try w.print(" {s}", .{const_prefix}); | ||
| 200 | try o.writeCValue(w, name); | ||
| 201 | } | ||
| 162 | try w.writeAll(suffix.items); | 202 | try w.writeAll(suffix.items); |
| 163 | } | 203 | } |
| 164 | }; | 204 | }; |
| ... | @@ -196,35 +236,72 @@ pub const DeclGen = struct { | ... | @@ -196,35 +236,72 @@ pub const DeclGen = struct { |
| 196 | return writer.print("{d}", .{val.toSignedInt()}); | 236 | return writer.print("{d}", .{val.toSignedInt()}); |
| 197 | return writer.print("{d}", .{val.toUnsignedInt()}); | 237 | return writer.print("{d}", .{val.toUnsignedInt()}); |
| 198 | }, | 238 | }, |
| 199 | .Pointer => switch (val.tag()) { | 239 | .Pointer => switch (t.ptrSize()) { |
| 200 | .null_value, .zero => try writer.writeAll("NULL"), | 240 | .Slice => { |
| 201 | .one => try writer.writeAll("1"), | 241 | try writer.writeByte('('); |
| 202 | .decl_ref => { | 242 | try dg.renderType(writer, t); |
| 203 | const decl = val.castTag(.decl_ref).?.data; | 243 | try writer.writeAll("){"); |
| 204 | 244 | var buf: Type.Payload.ElemType = undefined; | |
| 205 | // Determine if we must pointer cast. | 245 | try dg.renderValue(writer, t.slicePtrFieldType(&buf), val); |
| 206 | assert(decl.has_tv); | 246 | try writer.writeAll(", "); |
| 207 | if (t.eql(decl.ty)) { | 247 | try writer.print("{d}", .{val.sliceLen()}); |
| 208 | try writer.print("&{s}", .{decl.name}); | 248 | try writer.writeAll("}"); |
| 209 | } else { | ||
| 210 | try writer.writeAll("("); | ||
| 211 | try dg.renderType(writer, t); | ||
| 212 | try writer.print(")&{s}", .{decl.name}); | ||
| 213 | } | ||
| 214 | }, | ||
| 215 | .function => { | ||
| 216 | const func = val.castTag(.function).?.data; | ||
| 217 | try writer.print("{s}", .{func.owner_decl.name}); | ||
| 218 | }, | 249 | }, |
| 219 | .extern_fn => { | 250 | else => switch (val.tag()) { |
| 220 | const decl = val.castTag(.extern_fn).?.data; | 251 | .null_value, .zero => try writer.writeAll("NULL"), |
| 221 | try writer.print("{s}", .{decl.name}); | 252 | .one => try writer.writeAll("1"), |
| 253 | .decl_ref => { | ||
| 254 | const decl = val.castTag(.decl_ref).?.data; | ||
| 255 | |||
| 256 | // Determine if we must pointer cast. | ||
| 257 | assert(decl.has_tv); | ||
| 258 | if (t.eql(decl.ty)) { | ||
| 259 | try writer.print("&{s}", .{decl.name}); | ||
| 260 | } else { | ||
| 261 | try writer.writeAll("("); | ||
| 262 | try dg.renderType(writer, t); | ||
| 263 | try writer.print(")&{s}", .{decl.name}); | ||
| 264 | } | ||
| 265 | }, | ||
| 266 | .function => { | ||
| 267 | const func = val.castTag(.function).?.data; | ||
| 268 | try writer.print("{s}", .{func.owner_decl.name}); | ||
| 269 | }, | ||
| 270 | .extern_fn => { | ||
| 271 | const decl = val.castTag(.extern_fn).?.data; | ||
| 272 | try writer.print("{s}", .{decl.name}); | ||
| 273 | }, | ||
| 274 | else => switch (t.ptrSize()) { | ||
| 275 | .Slice => unreachable, | ||
| 276 | .Many => { | ||
| 277 | if (val.castTag(.ref_val)) |ref_val_payload| { | ||
| 278 | const sub_val = ref_val_payload.data; | ||
| 279 | if (sub_val.castTag(.bytes)) |bytes_payload| { | ||
| 280 | const bytes = bytes_payload.data; | ||
| 281 | try writer.writeByte('('); | ||
| 282 | try dg.renderType(writer, t); | ||
| 283 | // TODO: make our own C string escape instead of using std.zig.fmtEscapes | ||
| 284 | try writer.print(")\"{}\"", .{std.zig.fmtEscapes(bytes)}); | ||
| 285 | } else { | ||
| 286 | unreachable; | ||
| 287 | } | ||
| 288 | } else { | ||
| 289 | unreachable; | ||
| 290 | } | ||
| 291 | }, | ||
| 292 | .One => { | ||
| 293 | var arena = std.heap.ArenaAllocator.init(dg.module.gpa); | ||
| 294 | defer arena.deinit(); | ||
| 295 | |||
| 296 | const elem_ty = t.elemType(); | ||
| 297 | const elem_val = try val.pointerDeref(&arena.allocator); | ||
| 298 | |||
| 299 | try writer.writeAll("&"); | ||
| 300 | try dg.renderValue(writer, elem_ty, elem_val); | ||
| 301 | }, | ||
| 302 | .C => unreachable, | ||
| 303 | }, | ||
| 222 | }, | 304 | }, |
| 223 | else => |e| return dg.fail( | ||
| 224 | .{ .node_offset = 0 }, | ||
| 225 | "TODO: C backend: implement Pointer value {s}", | ||
| 226 | .{@tagName(e)}, | ||
| 227 | ), | ||
| 228 | }, | 305 | }, |
| 229 | .Array => { | 306 | .Array => { |
| 230 | // First try specific tag representations for more efficiency. | 307 | // First try specific tag representations for more efficiency. |
| ... | @@ -329,6 +406,32 @@ pub const DeclGen = struct { | ... | @@ -329,6 +406,32 @@ pub const DeclGen = struct { |
| 329 | }, | 406 | }, |
| 330 | } | 407 | } |
| 331 | }, | 408 | }, |
| 409 | .Fn => switch (val.tag()) { | ||
| 410 | .null_value, .zero => try writer.writeAll("NULL"), | ||
| 411 | .one => try writer.writeAll("1"), | ||
| 412 | .decl_ref => { | ||
| 413 | const decl = val.castTag(.decl_ref).?.data; | ||
| 414 | |||
| 415 | // Determine if we must pointer cast. | ||
| 416 | assert(decl.has_tv); | ||
| 417 | if (t.eql(decl.ty)) { | ||
| 418 | try writer.print("&{s}", .{decl.name}); | ||
| 419 | } else { | ||
| 420 | try writer.writeAll("("); | ||
| 421 | try dg.renderType(writer, t); | ||
| 422 | try writer.print(")&{s}", .{decl.name}); | ||
| 423 | } | ||
| 424 | }, | ||
| 425 | .function => { | ||
| 426 | const func = val.castTag(.function).?.data; | ||
| 427 | try writer.print("{s}", .{func.owner_decl.name}); | ||
| 428 | }, | ||
| 429 | .extern_fn => { | ||
| 430 | const decl = val.castTag(.extern_fn).?.data; | ||
| 431 | try writer.print("{s}", .{decl.name}); | ||
| 432 | }, | ||
| 433 | else => unreachable, | ||
| 434 | }, | ||
| 332 | else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement value {s}", .{ | 435 | else => |e| return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement value {s}", .{ |
| 333 | @tagName(e), | 436 | @tagName(e), |
| 334 | }), | 437 | }), |
| ... | @@ -339,6 +442,12 @@ pub const DeclGen = struct { | ... | @@ -339,6 +442,12 @@ pub const DeclGen = struct { |
| 339 | if (!is_global) { | 442 | if (!is_global) { |
| 340 | try w.writeAll("static "); | 443 | try w.writeAll("static "); |
| 341 | } | 444 | } |
| 445 | if (dg.decl.val.castTag(.function)) |func_payload| { | ||
| 446 | const func: *Module.Fn = func_payload.data; | ||
| 447 | if (func.is_cold) { | ||
| 448 | try w.writeAll("ZIG_COLD "); | ||
| 449 | } | ||
| 450 | } | ||
| 342 | try dg.renderType(w, dg.decl.ty.fnReturnType()); | 451 | try dg.renderType(w, dg.decl.ty.fnReturnType()); |
| 343 | const decl_name = mem.span(dg.decl.name); | 452 | const decl_name = mem.span(dg.decl.name); |
| 344 | try w.print(" {s}(", .{decl_name}); | 453 | try w.print(" {s}(", .{decl_name}); |
| ... | @@ -413,7 +522,35 @@ pub const DeclGen = struct { | ... | @@ -413,7 +522,35 @@ pub const DeclGen = struct { |
| 413 | 522 | ||
| 414 | .Pointer => { | 523 | .Pointer => { |
| 415 | if (t.isSlice()) { | 524 | if (t.isSlice()) { |
| 416 | return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement slices", .{}); | 525 | if (dg.typedefs.get(t)) |some| { |
| 526 | return w.writeAll(some.name); | ||
| 527 | } | ||
| 528 | |||
| 529 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | ||
| 530 | defer buffer.deinit(); | ||
| 531 | const bw = buffer.writer(); | ||
| 532 | |||
| 533 | try bw.writeAll("typedef struct { "); | ||
| 534 | const elem_type = t.elemType(); | ||
| 535 | try dg.renderType(bw, elem_type); | ||
| 536 | try bw.writeAll(" *"); | ||
| 537 | if (t.isConstPtr()) { | ||
| 538 | try bw.writeAll("const "); | ||
| 539 | } | ||
| 540 | if (t.isVolatilePtr()) { | ||
| 541 | try bw.writeAll("volatile "); | ||
| 542 | } | ||
| 543 | try bw.writeAll("ptr; size_t len; } "); | ||
| 544 | const name_index = buffer.items.len; | ||
| 545 | try bw.print("zig_L_{s};\n", .{typeToCIdentifier(elem_type)}); | ||
| 546 | |||
| 547 | const rendered = buffer.toOwnedSlice(); | ||
| 548 | errdefer dg.typedefs.allocator.free(rendered); | ||
| 549 | const name = rendered[name_index .. rendered.len - 2]; | ||
| 550 | |||
| 551 | try dg.typedefs.ensureUnusedCapacity(1); | ||
| 552 | try w.writeAll(name); | ||
| 553 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); | ||
| 417 | } else { | 554 | } else { |
| 418 | try dg.renderType(w, t.elemType()); | 555 | try dg.renderType(w, t.elemType()); |
| 419 | try w.writeAll(" *"); | 556 | try w.writeAll(" *"); |
| ... | @@ -446,13 +583,13 @@ pub const DeclGen = struct { | ... | @@ -446,13 +583,13 @@ pub const DeclGen = struct { |
| 446 | try dg.renderType(bw, child_type); | 583 | try dg.renderType(bw, child_type); |
| 447 | try bw.writeAll(" payload; bool is_null; } "); | 584 | try bw.writeAll(" payload; bool is_null; } "); |
| 448 | const name_index = buffer.items.len; | 585 | const name_index = buffer.items.len; |
| 449 | try bw.print("zig_opt_{s}_t;\n", .{typeToCIdentifier(child_type)}); | 586 | try bw.print("zig_Q_{s};\n", .{typeToCIdentifier(child_type)}); |
| 450 | 587 | ||
| 451 | const rendered = buffer.toOwnedSlice(); | 588 | const rendered = buffer.toOwnedSlice(); |
| 452 | errdefer dg.typedefs.allocator.free(rendered); | 589 | errdefer dg.typedefs.allocator.free(rendered); |
| 453 | const name = rendered[name_index .. rendered.len - 2]; | 590 | const name = rendered[name_index .. rendered.len - 2]; |
| 454 | 591 | ||
| 455 | try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1); | 592 | try dg.typedefs.ensureUnusedCapacity(1); |
| 456 | try w.writeAll(name); | 593 | try w.writeAll(name); |
| 457 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); | 594 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); |
| 458 | }, | 595 | }, |
| ... | @@ -465,7 +602,7 @@ pub const DeclGen = struct { | ... | @@ -465,7 +602,7 @@ pub const DeclGen = struct { |
| 465 | return w.writeAll(some.name); | 602 | return w.writeAll(some.name); |
| 466 | } | 603 | } |
| 467 | const child_type = t.errorUnionChild(); | 604 | const child_type = t.errorUnionChild(); |
| 468 | const set_type = t.errorUnionSet(); | 605 | const err_set_type = t.errorUnionSet(); |
| 469 | 606 | ||
| 470 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); | 607 | var buffer = std.ArrayList(u8).init(dg.typedefs.allocator); |
| 471 | defer buffer.deinit(); | 608 | defer buffer.deinit(); |
| ... | @@ -475,13 +612,20 @@ pub const DeclGen = struct { | ... | @@ -475,13 +612,20 @@ pub const DeclGen = struct { |
| 475 | try dg.renderType(bw, child_type); | 612 | try dg.renderType(bw, child_type); |
| 476 | try bw.writeAll(" payload; uint16_t error; } "); | 613 | try bw.writeAll(" payload; uint16_t error; } "); |
| 477 | const name_index = buffer.items.len; | 614 | const name_index = buffer.items.len; |
| 478 | try bw.print("zig_err_union_{s}_{s}_t;\n", .{ typeToCIdentifier(set_type), typeToCIdentifier(child_type) }); | 615 | if (err_set_type.castTag(.error_set_inferred)) |inf_err_set_payload| { |
| 616 | const func = inf_err_set_payload.data; | ||
| 617 | try bw.print("zig_E_{s};\n", .{func.owner_decl.name}); | ||
| 618 | } else { | ||
| 619 | try bw.print("zig_E_{s}_{s};\n", .{ | ||
| 620 | typeToCIdentifier(err_set_type), typeToCIdentifier(child_type), | ||
| 621 | }); | ||
| 622 | } | ||
| 479 | 623 | ||
| 480 | const rendered = buffer.toOwnedSlice(); | 624 | const rendered = buffer.toOwnedSlice(); |
| 481 | errdefer dg.typedefs.allocator.free(rendered); | 625 | errdefer dg.typedefs.allocator.free(rendered); |
| 482 | const name = rendered[name_index .. rendered.len - 2]; | 626 | const name = rendered[name_index .. rendered.len - 2]; |
| 483 | 627 | ||
| 484 | try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1); | 628 | try dg.typedefs.ensureUnusedCapacity(1); |
| 485 | try w.writeAll(name); | 629 | try w.writeAll(name); |
| 486 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); | 630 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); |
| 487 | }, | 631 | }, |
| ... | @@ -514,7 +658,7 @@ pub const DeclGen = struct { | ... | @@ -514,7 +658,7 @@ pub const DeclGen = struct { |
| 514 | errdefer dg.typedefs.allocator.free(rendered); | 658 | errdefer dg.typedefs.allocator.free(rendered); |
| 515 | const name = rendered[name_start .. rendered.len - 2]; | 659 | const name = rendered[name_start .. rendered.len - 2]; |
| 516 | 660 | ||
| 517 | try dg.typedefs.ensureCapacity(dg.typedefs.capacity() + 1); | 661 | try dg.typedefs.ensureUnusedCapacity(1); |
| 518 | try w.writeAll(name); | 662 | try w.writeAll(name); |
| 519 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); | 663 | dg.typedefs.putAssumeCapacityNoClobber(t, .{ .name = name, .rendered = rendered }); |
| 520 | }, | 664 | }, |
| ... | @@ -526,7 +670,28 @@ pub const DeclGen = struct { | ... | @@ -526,7 +670,28 @@ pub const DeclGen = struct { |
| 526 | try dg.renderType(w, int_tag_ty); | 670 | try dg.renderType(w, int_tag_ty); |
| 527 | }, | 671 | }, |
| 528 | .Union => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Union", .{}), | 672 | .Union => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Union", .{}), |
| 529 | .Fn => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Fn", .{}), | 673 | .Fn => { |
| 674 | try dg.renderType(w, t.fnReturnType()); | ||
| 675 | try w.writeAll(" (*)("); | ||
| 676 | const param_len = t.fnParamLen(); | ||
| 677 | const is_var_args = t.fnIsVarArgs(); | ||
| 678 | if (param_len == 0 and !is_var_args) | ||
| 679 | try w.writeAll("void") | ||
| 680 | else { | ||
| 681 | var index: usize = 0; | ||
| 682 | while (index < param_len) : (index += 1) { | ||
| 683 | if (index > 0) { | ||
| 684 | try w.writeAll(", "); | ||
| 685 | } | ||
| 686 | try dg.renderType(w, t.fnParamType(index)); | ||
| 687 | } | ||
| 688 | } | ||
| 689 | if (is_var_args) { | ||
| 690 | if (param_len != 0) try w.writeAll(", "); | ||
| 691 | try w.writeAll("..."); | ||
| 692 | } | ||
| 693 | try w.writeByte(')'); | ||
| 694 | }, | ||
| 530 | .Opaque => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Opaque", .{}), | 695 | .Opaque => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Opaque", .{}), |
| 531 | .Frame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Frame", .{}), | 696 | .Frame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type Frame", .{}), |
| 532 | .AnyFrame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type AnyFrame", .{}), | 697 | .AnyFrame => return dg.fail(.{ .node_offset = 0 }, "TODO: C backend: implement type AnyFrame", .{}), |
| ... | @@ -569,23 +734,27 @@ pub fn genDecl(o: *Object) !void { | ... | @@ -569,23 +734,27 @@ pub fn genDecl(o: *Object) !void { |
| 569 | .val = o.dg.decl.val, | 734 | .val = o.dg.decl.val, |
| 570 | }; | 735 | }; |
| 571 | if (tv.val.castTag(.function)) |func_payload| { | 736 | if (tv.val.castTag(.function)) |func_payload| { |
| 572 | const is_global = o.dg.declIsGlobal(tv); | ||
| 573 | const fwd_decl_writer = o.dg.fwd_decl.writer(); | ||
| 574 | if (is_global) { | ||
| 575 | try fwd_decl_writer.writeAll("ZIG_EXTERN_C "); | ||
| 576 | } | ||
| 577 | try o.dg.renderFunctionSignature(fwd_decl_writer, is_global); | ||
| 578 | try fwd_decl_writer.writeAll(";\n"); | ||
| 579 | |||
| 580 | const func: *Module.Fn = func_payload.data; | 737 | const func: *Module.Fn = func_payload.data; |
| 581 | try o.indent_writer.insertNewline(); | 738 | if (func.owner_decl == o.dg.decl) { |
| 582 | try o.dg.renderFunctionSignature(o.writer(), is_global); | 739 | const is_global = o.dg.declIsGlobal(tv); |
| 740 | const fwd_decl_writer = o.dg.fwd_decl.writer(); | ||
| 741 | if (is_global) { | ||
| 742 | try fwd_decl_writer.writeAll("ZIG_EXTERN_C "); | ||
| 743 | } | ||
| 744 | try o.dg.renderFunctionSignature(fwd_decl_writer, is_global); | ||
| 745 | try fwd_decl_writer.writeAll(";\n"); | ||
| 583 | 746 | ||
| 584 | try o.writer().writeByte(' '); | 747 | try o.indent_writer.insertNewline(); |
| 585 | try genBody(o, func.body); | 748 | try o.dg.renderFunctionSignature(o.writer(), is_global); |
| 586 | 749 | ||
| 587 | try o.indent_writer.insertNewline(); | 750 | try o.writer().writeByte(' '); |
| 588 | } else if (tv.val.tag() == .extern_fn) { | 751 | try genBody(o, func.body); |
| 752 | |||
| 753 | try o.indent_writer.insertNewline(); | ||
| 754 | return; | ||
| 755 | } | ||
| 756 | } | ||
| 757 | if (tv.val.tag() == .extern_fn) { | ||
| 589 | const writer = o.writer(); | 758 | const writer = o.writer(); |
| 590 | try writer.writeAll("ZIG_EXTERN_C "); | 759 | try writer.writeAll("ZIG_EXTERN_C "); |
| 591 | try o.dg.renderFunctionSignature(writer, true); | 760 | try o.dg.renderFunctionSignature(writer, true); |
| ... | @@ -644,9 +813,9 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { | ... | @@ -644,9 +813,9 @@ pub fn genHeader(dg: *DeclGen) error{ AnalysisFail, OutOfMemory }!void { |
| 644 | const is_global = dg.declIsGlobal(tv); | 813 | const is_global = dg.declIsGlobal(tv); |
| 645 | if (is_global) { | 814 | if (is_global) { |
| 646 | try writer.writeAll("ZIG_EXTERN_C "); | 815 | try writer.writeAll("ZIG_EXTERN_C "); |
| 816 | try dg.renderFunctionSignature(writer, is_global); | ||
| 817 | try dg.fwd_decl.appendSlice(";\n"); | ||
| 647 | } | 818 | } |
| 648 | try dg.renderFunctionSignature(writer, is_global); | ||
| 649 | try dg.fwd_decl.appendSlice(";\n"); | ||
| 650 | }, | 819 | }, |
| 651 | else => {}, | 820 | else => {}, |
| 652 | } | 821 | } |
src/link/C.zig+4-6| ... | @@ -207,7 +207,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { | ... | @@ -207,7 +207,7 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 207 | } | 207 | } |
| 208 | 208 | ||
| 209 | var fn_count: usize = 0; | 209 | var fn_count: usize = 0; |
| 210 | var typedefs = std.HashMap(Type, []const u8, Type.HashContext, std.hash_map.default_max_load_percentage).init(comp.gpa); | 210 | var typedefs = std.HashMap(Type, void, Type.HashContext, std.hash_map.default_max_load_percentage).init(comp.gpa); |
| 211 | defer typedefs.deinit(); | 211 | defer typedefs.deinit(); |
| 212 | 212 | ||
| 213 | // Typedefs, forward decls and non-functions first. | 213 | // Typedefs, forward decls and non-functions first. |
| ... | @@ -217,14 +217,12 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { | ... | @@ -217,14 +217,12 @@ pub fn flushModule(self: *C, comp: *Compilation) !void { |
| 217 | if (!decl.has_tv) continue; | 217 | if (!decl.has_tv) continue; |
| 218 | const buf = buf: { | 218 | const buf = buf: { |
| 219 | if (decl.val.castTag(.function)) |_| { | 219 | if (decl.val.castTag(.function)) |_| { |
| 220 | try typedefs.ensureUnusedCapacity(decl.fn_link.c.typedefs.count()); | ||
| 220 | var it = decl.fn_link.c.typedefs.iterator(); | 221 | var it = decl.fn_link.c.typedefs.iterator(); |
| 221 | while (it.next()) |new| { | 222 | while (it.next()) |new| { |
| 222 | if (typedefs.get(new.key_ptr.*)) |previous| { | 223 | const gop = typedefs.getOrPutAssumeCapacity(new.key_ptr.*); |
| 223 | try err_typedef_writer.print("typedef {s} {s};\n", .{ previous, new.value_ptr.name }); | 224 | if (!gop.found_existing) { |
| 224 | } else { | ||
| 225 | try typedefs.ensureCapacity(typedefs.capacity() + 1); | ||
| 226 | try err_typedef_writer.writeAll(new.value_ptr.rendered); | 225 | try err_typedef_writer.writeAll(new.value_ptr.rendered); |
| 227 | typedefs.putAssumeCapacityNoClobber(new.key_ptr.*, new.value_ptr.name); | ||
| 228 | } | 226 | } |
| 229 | } | 227 | } |
| 230 | fn_count += 1; | 228 | fn_count += 1; |
src/link/C/zig.h+6| ... | @@ -12,6 +12,12 @@ | ... | @@ -12,6 +12,12 @@ |
| 12 | #define zig_threadlocal zig_threadlocal_unavailable | 12 | #define zig_threadlocal zig_threadlocal_unavailable |
| 13 | #endif | 13 | #endif |
| 14 | 14 | ||
| 15 | #if __GNUC__ | ||
| 16 | #define ZIG_COLD __attribute__ ((cold)) | ||
| 17 | #else | ||
| 18 | #define ZIG_COLD | ||
| 19 | #endif | ||
| 20 | |||
| 15 | #if __STDC_VERSION__ >= 199901L | 21 | #if __STDC_VERSION__ >= 199901L |
| 16 | #define ZIG_RESTRICT restrict | 22 | #define ZIG_RESTRICT restrict |
| 17 | #elif defined(__GNUC__) | 23 | #elif defined(__GNUC__) |
src/type.zig+79-3| ... | @@ -58,7 +58,7 @@ pub const Type = extern union { | ... | @@ -58,7 +58,7 @@ pub const Type = extern union { |
| 58 | .bool => return .Bool, | 58 | .bool => return .Bool, |
| 59 | .void => return .Void, | 59 | .void => return .Void, |
| 60 | .type => return .Type, | 60 | .type => return .Type, |
| 61 | .error_set, .error_set_single, .anyerror => return .ErrorSet, | 61 | .error_set, .error_set_single, .anyerror, .error_set_inferred => return .ErrorSet, |
| 62 | .comptime_int => return .ComptimeInt, | 62 | .comptime_int => return .ComptimeInt, |
| 63 | .comptime_float => return .ComptimeFloat, | 63 | .comptime_float => return .ComptimeFloat, |
| 64 | .noreturn => return .NoReturn, | 64 | .noreturn => return .NoReturn, |
| ... | @@ -689,7 +689,15 @@ pub const Type = extern union { | ... | @@ -689,7 +689,15 @@ pub const Type = extern union { |
| 689 | .optional_single_mut_pointer, | 689 | .optional_single_mut_pointer, |
| 690 | .optional_single_const_pointer, | 690 | .optional_single_const_pointer, |
| 691 | .anyframe_T, | 691 | .anyframe_T, |
| 692 | => return self.copyPayloadShallow(allocator, Payload.ElemType), | 692 | => { |
| 693 | const payload = self.cast(Payload.ElemType).?; | ||
| 694 | const new_payload = try allocator.create(Payload.ElemType); | ||
| 695 | new_payload.* = .{ | ||
| 696 | .base = .{ .tag = payload.base.tag }, | ||
| 697 | .data = try payload.data.copy(allocator), | ||
| 698 | }; | ||
| 699 | return Type{ .ptr_otherwise = &new_payload.base }; | ||
| 700 | }, | ||
| 693 | 701 | ||
| 694 | .int_signed, | 702 | .int_signed, |
| 695 | .int_unsigned, | 703 | .int_unsigned, |
| ... | @@ -756,6 +764,7 @@ pub const Type = extern union { | ... | @@ -756,6 +764,7 @@ pub const Type = extern union { |
| 756 | }); | 764 | }); |
| 757 | }, | 765 | }, |
| 758 | .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), | 766 | .error_set => return self.copyPayloadShallow(allocator, Payload.ErrorSet), |
| 767 | .error_set_inferred => return self.copyPayloadShallow(allocator, Payload.ErrorSetInferred), | ||
| 759 | .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name), | 768 | .error_set_single => return self.copyPayloadShallow(allocator, Payload.Name), |
| 760 | .empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope), | 769 | .empty_struct => return self.copyPayloadShallow(allocator, Payload.ContainerScope), |
| 761 | .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct), | 770 | .@"struct" => return self.copyPayloadShallow(allocator, Payload.Struct), |
| ... | @@ -1031,6 +1040,10 @@ pub const Type = extern union { | ... | @@ -1031,6 +1040,10 @@ pub const Type = extern union { |
| 1031 | const error_set = ty.castTag(.error_set).?.data; | 1040 | const error_set = ty.castTag(.error_set).?.data; |
| 1032 | return writer.writeAll(std.mem.spanZ(error_set.owner_decl.name)); | 1041 | return writer.writeAll(std.mem.spanZ(error_set.owner_decl.name)); |
| 1033 | }, | 1042 | }, |
| 1043 | .error_set_inferred => { | ||
| 1044 | const func = ty.castTag(.error_set_inferred).?.data; | ||
| 1045 | return writer.print("(inferred error set of {s})", .{func.owner_decl.name}); | ||
| 1046 | }, | ||
| 1034 | .error_set_single => { | 1047 | .error_set_single => { |
| 1035 | const name = ty.castTag(.error_set_single).?.data; | 1048 | const name = ty.castTag(.error_set_single).?.data; |
| 1036 | return writer.print("error{{{s}}}", .{name}); | 1049 | return writer.print("error{{{s}}}", .{name}); |
| ... | @@ -1144,6 +1157,7 @@ pub const Type = extern union { | ... | @@ -1144,6 +1157,7 @@ pub const Type = extern union { |
| 1144 | .anyerror_void_error_union, | 1157 | .anyerror_void_error_union, |
| 1145 | .error_set, | 1158 | .error_set, |
| 1146 | .error_set_single, | 1159 | .error_set_single, |
| 1160 | .error_set_inferred, | ||
| 1147 | .manyptr_u8, | 1161 | .manyptr_u8, |
| 1148 | .manyptr_const_u8, | 1162 | .manyptr_const_u8, |
| 1149 | .atomic_ordering, | 1163 | .atomic_ordering, |
| ... | @@ -1161,6 +1175,9 @@ pub const Type = extern union { | ... | @@ -1161,6 +1175,9 @@ pub const Type = extern union { |
| 1161 | .@"struct" => { | 1175 | .@"struct" => { |
| 1162 | // TODO introduce lazy value mechanism | 1176 | // TODO introduce lazy value mechanism |
| 1163 | const struct_obj = self.castTag(.@"struct").?.data; | 1177 | const struct_obj = self.castTag(.@"struct").?.data; |
| 1178 | assert(struct_obj.status == .have_field_types or | ||
| 1179 | struct_obj.status == .layout_wip or | ||
| 1180 | struct_obj.status == .have_layout); | ||
| 1164 | for (struct_obj.fields.values()) |value| { | 1181 | for (struct_obj.fields.values()) |value| { |
| 1165 | if (value.ty.hasCodeGenBits()) | 1182 | if (value.ty.hasCodeGenBits()) |
| 1166 | return true; | 1183 | return true; |
| ... | @@ -1348,6 +1365,7 @@ pub const Type = extern union { | ... | @@ -1348,6 +1365,7 @@ pub const Type = extern union { |
| 1348 | .error_set_single, | 1365 | .error_set_single, |
| 1349 | .anyerror_void_error_union, | 1366 | .anyerror_void_error_union, |
| 1350 | .anyerror, | 1367 | .anyerror, |
| 1368 | .error_set_inferred, | ||
| 1351 | => return 2, // TODO revisit this when we have the concept of the error tag type | 1369 | => return 2, // TODO revisit this when we have the concept of the error tag type |
| 1352 | 1370 | ||
| 1353 | .array, .array_sentinel => return self.elemType().abiAlignment(target), | 1371 | .array, .array_sentinel => return self.elemType().abiAlignment(target), |
| ... | @@ -1580,6 +1598,7 @@ pub const Type = extern union { | ... | @@ -1580,6 +1598,7 @@ pub const Type = extern union { |
| 1580 | .error_set_single, | 1598 | .error_set_single, |
| 1581 | .anyerror_void_error_union, | 1599 | .anyerror_void_error_union, |
| 1582 | .anyerror, | 1600 | .anyerror, |
| 1601 | .error_set_inferred, | ||
| 1583 | => return 2, // TODO revisit this when we have the concept of the error tag type | 1602 | => return 2, // TODO revisit this when we have the concept of the error tag type |
| 1584 | 1603 | ||
| 1585 | .int_signed, .int_unsigned => { | 1604 | .int_signed, .int_unsigned => { |
| ... | @@ -1744,6 +1763,7 @@ pub const Type = extern union { | ... | @@ -1744,6 +1763,7 @@ pub const Type = extern union { |
| 1744 | .error_set_single, | 1763 | .error_set_single, |
| 1745 | .anyerror_void_error_union, | 1764 | .anyerror_void_error_union, |
| 1746 | .anyerror, | 1765 | .anyerror, |
| 1766 | .error_set_inferred, | ||
| 1747 | => return 16, // TODO revisit this when we have the concept of the error tag type | 1767 | => return 16, // TODO revisit this when we have the concept of the error tag type |
| 1748 | 1768 | ||
| 1749 | .int_signed, .int_unsigned => self.cast(Payload.Bits).?.data, | 1769 | .int_signed, .int_unsigned => self.cast(Payload.Bits).?.data, |
| ... | @@ -1863,6 +1883,48 @@ pub const Type = extern union { | ... | @@ -1863,6 +1883,48 @@ pub const Type = extern union { |
| 1863 | }; | 1883 | }; |
| 1864 | } | 1884 | } |
| 1865 | 1885 | ||
| 1886 | pub fn slicePtrFieldType(self: Type, buffer: *Payload.ElemType) Type { | ||
| 1887 | switch (self.tag()) { | ||
| 1888 | .const_slice_u8 => return Type.initTag(.manyptr_const_u8), | ||
| 1889 | |||
| 1890 | .const_slice => { | ||
| 1891 | const elem_type = self.castTag(.const_slice).?.data; | ||
| 1892 | buffer.* = .{ | ||
| 1893 | .base = .{ .tag = .many_const_pointer }, | ||
| 1894 | .data = elem_type, | ||
| 1895 | }; | ||
| 1896 | return Type.initPayload(&buffer.base); | ||
| 1897 | }, | ||
| 1898 | .mut_slice => { | ||
| 1899 | const elem_type = self.castTag(.mut_slice).?.data; | ||
| 1900 | buffer.* = .{ | ||
| 1901 | .base = .{ .tag = .many_mut_pointer }, | ||
| 1902 | .data = elem_type, | ||
| 1903 | }; | ||
| 1904 | return Type.initPayload(&buffer.base); | ||
| 1905 | }, | ||
| 1906 | |||
| 1907 | .pointer => { | ||
| 1908 | const payload = self.castTag(.pointer).?.data; | ||
| 1909 | assert(payload.size == .Slice); | ||
| 1910 | if (payload.mutable) { | ||
| 1911 | buffer.* = .{ | ||
| 1912 | .base = .{ .tag = .many_mut_pointer }, | ||
| 1913 | .data = payload.pointee_type, | ||
| 1914 | }; | ||
| 1915 | } else { | ||
| 1916 | buffer.* = .{ | ||
| 1917 | .base = .{ .tag = .many_const_pointer }, | ||
| 1918 | .data = payload.pointee_type, | ||
| 1919 | }; | ||
| 1920 | } | ||
| 1921 | return Type.initPayload(&buffer.base); | ||
| 1922 | }, | ||
| 1923 | |||
| 1924 | else => unreachable, | ||
| 1925 | } | ||
| 1926 | } | ||
| 1927 | |||
| 1866 | pub fn isConstPtr(self: Type) bool { | 1928 | pub fn isConstPtr(self: Type) bool { |
| 1867 | return switch (self.tag()) { | 1929 | return switch (self.tag()) { |
| 1868 | .single_const_pointer, | 1930 | .single_const_pointer, |
| ... | @@ -1915,7 +1977,10 @@ pub const Type = extern union { | ... | @@ -1915,7 +1977,10 @@ pub const Type = extern union { |
| 1915 | /// Asserts that the type is an optional | 1977 | /// Asserts that the type is an optional |
| 1916 | pub fn isPtrLikeOptional(self: Type) bool { | 1978 | pub fn isPtrLikeOptional(self: Type) bool { |
| 1917 | switch (self.tag()) { | 1979 | switch (self.tag()) { |
| 1918 | .optional_single_const_pointer, .optional_single_mut_pointer => return true, | 1980 | .optional_single_const_pointer, |
| 1981 | .optional_single_mut_pointer, | ||
| 1982 | => return true, | ||
| 1983 | |||
| 1919 | .optional => { | 1984 | .optional => { |
| 1920 | var buf: Payload.ElemType = undefined; | 1985 | var buf: Payload.ElemType = undefined; |
| 1921 | const child_type = self.optionalChild(&buf); | 1986 | const child_type = self.optionalChild(&buf); |
| ... | @@ -2400,6 +2465,7 @@ pub const Type = extern union { | ... | @@ -2400,6 +2465,7 @@ pub const Type = extern union { |
| 2400 | .error_union, | 2465 | .error_union, |
| 2401 | .error_set, | 2466 | .error_set, |
| 2402 | .error_set_single, | 2467 | .error_set_single, |
| 2468 | .error_set_inferred, | ||
| 2403 | .@"opaque", | 2469 | .@"opaque", |
| 2404 | .var_args_param, | 2470 | .var_args_param, |
| 2405 | .manyptr_u8, | 2471 | .manyptr_u8, |
| ... | @@ -2892,6 +2958,8 @@ pub const Type = extern union { | ... | @@ -2892,6 +2958,8 @@ pub const Type = extern union { |
| 2892 | anyframe_T, | 2958 | anyframe_T, |
| 2893 | error_set, | 2959 | error_set, |
| 2894 | error_set_single, | 2960 | error_set_single, |
| 2961 | /// The type is the inferred error set of a specific function. | ||
| 2962 | error_set_inferred, | ||
| 2895 | empty_struct, | 2963 | empty_struct, |
| 2896 | @"opaque", | 2964 | @"opaque", |
| 2897 | @"struct", | 2965 | @"struct", |
| ... | @@ -2989,6 +3057,7 @@ pub const Type = extern union { | ... | @@ -2989,6 +3057,7 @@ pub const Type = extern union { |
| 2989 | => Payload.Bits, | 3057 | => Payload.Bits, |
| 2990 | 3058 | ||
| 2991 | .error_set => Payload.ErrorSet, | 3059 | .error_set => Payload.ErrorSet, |
| 3060 | .error_set_inferred => Payload.ErrorSetInferred, | ||
| 2992 | 3061 | ||
| 2993 | .array, .vector => Payload.Array, | 3062 | .array, .vector => Payload.Array, |
| 2994 | .array_sentinel => Payload.ArraySentinel, | 3063 | .array_sentinel => Payload.ArraySentinel, |
| ... | @@ -3081,6 +3150,13 @@ pub const Type = extern union { | ... | @@ -3081,6 +3150,13 @@ pub const Type = extern union { |
| 3081 | data: *Module.ErrorSet, | 3150 | data: *Module.ErrorSet, |
| 3082 | }; | 3151 | }; |
| 3083 | 3152 | ||
| 3153 | pub const ErrorSetInferred = struct { | ||
| 3154 | pub const base_tag = Tag.error_set_inferred; | ||
| 3155 | |||
| 3156 | base: Payload = Payload{ .tag = base_tag }, | ||
| 3157 | data: *Module.Fn, | ||
| 3158 | }; | ||
| 3159 | |||
| 3084 | pub const Pointer = struct { | 3160 | pub const Pointer = struct { |
| 3085 | pub const base_tag = Tag.pointer; | 3161 | pub const base_tag = Tag.pointer; |
| 3086 | 3162 |
src/value.zig+22-5| ... | @@ -483,13 +483,13 @@ pub const Value = extern union { | ... | @@ -483,13 +483,13 @@ pub const Value = extern union { |
| 483 | /// TODO this should become a debug dump() function. In order to print values in a meaningful way | 483 | /// TODO this should become a debug dump() function. In order to print values in a meaningful way |
| 484 | /// we also need access to the type. | 484 | /// we also need access to the type. |
| 485 | pub fn format( | 485 | pub fn format( |
| 486 | self: Value, | 486 | start_val: Value, |
| 487 | comptime fmt: []const u8, | 487 | comptime fmt: []const u8, |
| 488 | options: std.fmt.FormatOptions, | 488 | options: std.fmt.FormatOptions, |
| 489 | out_stream: anytype, | 489 | out_stream: anytype, |
| 490 | ) !void { | 490 | ) !void { |
| 491 | comptime assert(fmt.len == 0); | 491 | comptime assert(fmt.len == 0); |
| 492 | var val = self; | 492 | var val = start_val; |
| 493 | while (true) switch (val.tag()) { | 493 | while (true) switch (val.tag()) { |
| 494 | .u8_type => return out_stream.writeAll("u8"), | 494 | .u8_type => return out_stream.writeAll("u8"), |
| 495 | .i8_type => return out_stream.writeAll("i8"), | 495 | .i8_type => return out_stream.writeAll("i8"), |
| ... | @@ -598,9 +598,9 @@ pub const Value = extern union { | ... | @@ -598,9 +598,9 @@ pub const Value = extern union { |
| 598 | val = field_ptr.container_ptr; | 598 | val = field_ptr.container_ptr; |
| 599 | }, | 599 | }, |
| 600 | .empty_array => return out_stream.writeAll(".{}"), | 600 | .empty_array => return out_stream.writeAll(".{}"), |
| 601 | .enum_literal => return out_stream.print(".{}", .{std.zig.fmtId(self.castTag(.enum_literal).?.data)}), | 601 | .enum_literal => return out_stream.print(".{}", .{std.zig.fmtId(val.castTag(.enum_literal).?.data)}), |
| 602 | .enum_field_index => return out_stream.print("(enum field {d})", .{self.castTag(.enum_field_index).?.data}), | 602 | .enum_field_index => return out_stream.print("(enum field {d})", .{val.castTag(.enum_field_index).?.data}), |
| 603 | .bytes => return out_stream.print("\"{}\"", .{std.zig.fmtEscapes(self.castTag(.bytes).?.data)}), | 603 | .bytes => return out_stream.print("\"{}\"", .{std.zig.fmtEscapes(val.castTag(.bytes).?.data)}), |
| 604 | .repeated => { | 604 | .repeated => { |
| 605 | try out_stream.writeAll("(repeated) "); | 605 | try out_stream.writeAll("(repeated) "); |
| 606 | val = val.castTag(.repeated).?.data; | 606 | val = val.castTag(.repeated).?.data; |
| ... | @@ -1336,6 +1336,23 @@ pub const Value = extern union { | ... | @@ -1336,6 +1336,23 @@ pub const Value = extern union { |
| 1336 | }; | 1336 | }; |
| 1337 | } | 1337 | } |
| 1338 | 1338 | ||
| 1339 | pub fn sliceLen(val: Value) u64 { | ||
| 1340 | return switch (val.tag()) { | ||
| 1341 | .empty_array => 0, | ||
| 1342 | .bytes => val.castTag(.bytes).?.data.len, | ||
| 1343 | .ref_val => sliceLen(val.castTag(.ref_val).?.data), | ||
| 1344 | .decl_ref => { | ||
| 1345 | const decl = val.castTag(.decl_ref).?.data; | ||
| 1346 | if (decl.ty.zigTypeTag() == .Array) { | ||
| 1347 | return decl.ty.arrayLen(); | ||
| 1348 | } else { | ||
| 1349 | return 1; | ||
| 1350 | } | ||
| 1351 | }, | ||
| 1352 | else => unreachable, | ||
| 1353 | }; | ||
| 1354 | } | ||
| 1355 | |||
| 1339 | /// Asserts the value is a single-item pointer to an array, or an array, | 1356 | /// Asserts the value is a single-item pointer to an array, or an array, |
| 1340 | /// or an unknown-length pointer, and returns the element value at the index. | 1357 | /// or an unknown-length pointer, and returns the element value at the index. |
| 1341 | pub fn elemValue(self: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value { | 1358 | pub fn elemValue(self: Value, allocator: *Allocator, index: usize) error{OutOfMemory}!Value { |
test/stage2/cbe.zig-13| ... | @@ -804,19 +804,6 @@ pub fn addCases(ctx: *TestContext) !void { | ... | @@ -804,19 +804,6 @@ pub fn addCases(ctx: *TestContext) !void { |
| 804 | }); | 804 | }); |
| 805 | } | 805 | } |
| 806 | 806 | ||
| 807 | ctx.c("empty start function", linux_x64, | ||
| 808 | \\export fn _start() noreturn { | ||
| 809 | \\ unreachable; | ||
| 810 | \\} | ||
| 811 | , | ||
| 812 | \\ZIG_EXTERN_C zig_noreturn void _start(void); | ||
| 813 | \\ | ||
| 814 | \\zig_noreturn void _start(void) { | ||
| 815 | \\ zig_breakpoint(); | ||
| 816 | \\ zig_unreachable(); | ||
| 817 | \\} | ||
| 818 | \\ | ||
| 819 | ); | ||
| 820 | ctx.h("simple header", linux_x64, | 807 | ctx.h("simple header", linux_x64, |
| 821 | \\export fn start() void{} | 808 | \\export fn start() void{} |
| 822 | , | 809 | , |