| author | |
| committer | |
| log | ecc246efa2c133aaab73032a18fed5b2c15e08ce |
| tree | 660b8f5e027066e728fbd88db5e58385940aa2d7 |
| parent | 3c2a9220edd59c2d7b50aca65e4cd0748cf2306f |
* fix wrong pointer const-ness when unwrapping optionals
* allow grouped expressions and orelse as lvalues
* ZIR for unwrapping optionals: no redundant deref
- add notes to please don't use rlWrapPtr, this function should be
deleted
* catch and orelse: better ZIR for non-lvalue: no redundant deref;
operate entirely on values. lvalue case still works properly.
- properly propagate the result location into the target expression
* Test harness: better output when tests fail due to compile errors.
* TZIR: add instruction variants. These allow fewer TZIR instructions to
be emitted from zir_sema. See the commit diff for per-instruction
documentation.
- is_null
- is_non_null
- is_null_ptr
- is_non_null_ptr
- is_err
- is_err_ptr
- optional_payload
- optional_payload_ptr
* TZIR: removed old naming convention instructions:
- isnonnull
- isnull
- iserr
- unwrap_optional
* ZIR: add instruction variants. These allow fewer ZIR instructions to
be emitted from astgen. See the commit diff for per-instruction
documentation.
- is_non_null
- is_null
- is_non_null_ptr
- is_null_ptr
- is_err
- is_err_ptr
- optional_payload_safe
- optional_payload_unsafe
- optional_payload_safe_ptr
- optional_payload_unsafe_ptr
- err_union_payload_safe
- err_union_payload_unsafe
- err_union_payload_safe_ptr
- err_union_payload_unsafe_ptr
- err_union_code
- err_union_code_ptr
* ZIR: removed old naming convention instructions:
- isnonnull
- isnull
- iserr
- unwrap_optional_safe
- unwrap_optional_unsafe
- unwrap_err_safe
- unwrap_err_unsafe
- unwrap_err_code7 files changed, 358 insertions(+), 107 deletions(-)
src/Module.zig+1-1| ... | ... | @@ -2453,7 +2453,7 @@ pub fn analyzeIsNull( |
| 2453 | 2453 | return self.constBool(scope, src, bool_value); |
| 2454 | 2454 | } |
| 2455 | 2455 | const b = try self.requireRuntimeBlock(scope, src); |
| 2456 | const inst_tag: Inst.Tag = if (invert_logic) .isnonnull else .isnull; | |
| 2456 | const inst_tag: Inst.Tag = if (invert_logic) .is_non_null else .is_null; | |
| 2457 | 2457 | return self.addUnOp(b, src, Type.initTag(.bool), inst_tag, operand); |
| 2458 | 2458 | } |
| 2459 | 2459 |
src/astgen.zig+95-29| ... | ... | @@ -118,7 +118,6 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { |
| 118 | 118 | .LabeledBlock, |
| 119 | 119 | .Break, |
| 120 | 120 | .PtrType, |
| 121 | .GroupedExpression, | |
| 122 | 121 | .ArrayType, |
| 123 | 122 | .ArrayTypeSentinel, |
| 124 | 123 | .EnumLiteral, |
| ... | ... | @@ -129,7 +128,6 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { |
| 129 | 128 | .ErrorUnion, |
| 130 | 129 | .MergeErrorSets, |
| 131 | 130 | .Range, |
| 132 | .OrElse, | |
| 133 | 131 | .Await, |
| 134 | 132 | .BitNot, |
| 135 | 133 | .Negation, |
| ... | ... | @@ -168,7 +166,14 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst { |
| 168 | 166 | }, |
| 169 | 167 | |
| 170 | 168 | // can be assigned to |
| 171 | .UnwrapOptional, .Deref, .Period, .ArrayAccess, .Identifier => {}, | |
| 169 | .UnwrapOptional, | |
| 170 | .Deref, | |
| 171 | .Period, | |
| 172 | .ArrayAccess, | |
| 173 | .Identifier, | |
| 174 | .GroupedExpression, | |
| 175 | .OrElse, | |
| 176 | => {}, | |
| 172 | 177 | } |
| 173 | 178 | return expr(mod, scope, .ref, node); |
| 174 | 179 | } |
| ... | ... | @@ -913,8 +918,12 @@ fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Si |
| 913 | 918 | const tree = scope.tree(); |
| 914 | 919 | const src = tree.token_locs[node.rtoken].start; |
| 915 | 920 | |
| 916 | const operand = try expr(mod, scope, .ref, node.lhs); | |
| 917 | return rlWrapPtr(mod, scope, rl, try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand)); | |
| 921 | const operand = try expr(mod, scope, rl, node.lhs); | |
| 922 | const op: zir.Inst.Tag = switch (rl) { | |
| 923 | .ref => .optional_payload_safe_ptr, | |
| 924 | else => .optional_payload_safe, | |
| 925 | }; | |
| 926 | return addZIRUnOp(mod, scope, src, op, operand); | |
| 918 | 927 | } |
| 919 | 928 | |
| 920 | 929 | fn containerField( |
| ... | ... | @@ -1110,6 +1119,7 @@ fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Erro |
| 1110 | 1119 | } |
| 1111 | 1120 | |
| 1112 | 1121 | // analyzing the error set results in a decl ref, so we might need to dereference it |
| 1122 | // TODO remove all callsites to rlWrapPtr | |
| 1113 | 1123 | return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{})); |
| 1114 | 1124 | } |
| 1115 | 1125 | |
| ... | ... | @@ -1123,11 +1133,61 @@ fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!* |
| 1123 | 1133 | } |
| 1124 | 1134 | |
| 1125 | 1135 | fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch) InnerError!*zir.Inst { |
| 1126 | return orelseCatchExpr(mod, scope, rl, node.lhs, node.op_token, .iserr, .unwrap_err_unsafe, node.rhs, node.payload); | |
| 1136 | switch (rl) { | |
| 1137 | .ref => return orelseCatchExpr( | |
| 1138 | mod, | |
| 1139 | scope, | |
| 1140 | rl, | |
| 1141 | node.lhs, | |
| 1142 | node.op_token, | |
| 1143 | .is_err_ptr, | |
| 1144 | .err_union_payload_unsafe_ptr, | |
| 1145 | .err_union_code_ptr, | |
| 1146 | node.rhs, | |
| 1147 | node.payload, | |
| 1148 | ), | |
| 1149 | else => return orelseCatchExpr( | |
| 1150 | mod, | |
| 1151 | scope, | |
| 1152 | rl, | |
| 1153 | node.lhs, | |
| 1154 | node.op_token, | |
| 1155 | .is_err, | |
| 1156 | .err_union_payload_unsafe, | |
| 1157 | .err_union_code, | |
| 1158 | node.rhs, | |
| 1159 | node.payload, | |
| 1160 | ), | |
| 1161 | } | |
| 1127 | 1162 | } |
| 1128 | 1163 | |
| 1129 | 1164 | fn orelseExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst { |
| 1130 | return orelseCatchExpr(mod, scope, rl, node.lhs, node.op_token, .isnonnull, .unwrap_optional_unsafe, node.rhs, null); | |
| 1165 | switch (rl) { | |
| 1166 | .ref => return orelseCatchExpr( | |
| 1167 | mod, | |
| 1168 | scope, | |
| 1169 | rl, | |
| 1170 | node.lhs, | |
| 1171 | node.op_token, | |
| 1172 | .is_null_ptr, | |
| 1173 | .optional_payload_unsafe_ptr, | |
| 1174 | undefined, | |
| 1175 | node.rhs, | |
| 1176 | null, | |
| 1177 | ), | |
| 1178 | else => return orelseCatchExpr( | |
| 1179 | mod, | |
| 1180 | scope, | |
| 1181 | rl, | |
| 1182 | node.lhs, | |
| 1183 | node.op_token, | |
| 1184 | .is_null, | |
| 1185 | .optional_payload_unsafe, | |
| 1186 | undefined, | |
| 1187 | node.rhs, | |
| 1188 | null, | |
| 1189 | ), | |
| 1190 | } | |
| 1131 | 1191 | } |
| 1132 | 1192 | |
| 1133 | 1193 | fn orelseCatchExpr( |
| ... | ... | @@ -1138,17 +1198,13 @@ fn orelseCatchExpr( |
| 1138 | 1198 | op_token: ast.TokenIndex, |
| 1139 | 1199 | cond_op: zir.Inst.Tag, |
| 1140 | 1200 | unwrap_op: zir.Inst.Tag, |
| 1201 | unwrap_code_op: zir.Inst.Tag, | |
| 1141 | 1202 | rhs: *ast.Node, |
| 1142 | 1203 | payload_node: ?*ast.Node, |
| 1143 | 1204 | ) InnerError!*zir.Inst { |
| 1144 | 1205 | const tree = scope.tree(); |
| 1145 | 1206 | const src = tree.token_locs[op_token].start; |
| 1146 | 1207 | |
| 1147 | const operand_ptr = try expr(mod, scope, .ref, lhs); | |
| 1148 | // TODO we could avoid an unnecessary copy if .iserr, .isnull took a pointer | |
| 1149 | const err_union = try addZIRUnOp(mod, scope, src, .deref, operand_ptr); | |
| 1150 | const cond = try addZIRUnOp(mod, scope, src, cond_op, err_union); | |
| 1151 | ||
| 1152 | 1208 | var block_scope: Scope.GenZIR = .{ |
| 1153 | 1209 | .parent = scope, |
| 1154 | 1210 | .decl = scope.ownerDecl().?, |
| ... | ... | @@ -1157,14 +1213,8 @@ fn orelseCatchExpr( |
| 1157 | 1213 | }; |
| 1158 | 1214 | defer block_scope.instructions.deinit(mod.gpa); |
| 1159 | 1215 | |
| 1160 | const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{ | |
| 1161 | .condition = cond, | |
| 1162 | .then_body = undefined, // populated below | |
| 1163 | .else_body = undefined, // populated below | |
| 1164 | }, .{}); | |
| 1165 | ||
| 1166 | 1216 | const block = try addZIRInstBlock(mod, scope, src, .block, .{ |
| 1167 | .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), | |
| 1217 | .instructions = undefined, // populated below | |
| 1168 | 1218 | }); |
| 1169 | 1219 | |
| 1170 | 1220 | // Most result location types can be forwarded directly; however |
| ... | ... | @@ -1175,9 +1225,18 @@ fn orelseCatchExpr( |
| 1175 | 1225 | .discard, .none, .ty, .ptr, .ref => rl, |
| 1176 | 1226 | .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block }, |
| 1177 | 1227 | }; |
| 1228 | // This could be a pointer or value depending on the `rl` parameter. | |
| 1229 | const operand = try expr(mod, &block_scope.base, branch_rl, lhs); | |
| 1230 | const cond = try addZIRUnOp(mod, &block_scope.base, src, cond_op, operand); | |
| 1231 | ||
| 1232 | const condbr = try addZIRInstSpecial(mod, &block_scope.base, src, zir.Inst.CondBr, .{ | |
| 1233 | .condition = cond, | |
| 1234 | .then_body = undefined, // populated below | |
| 1235 | .else_body = undefined, // populated below | |
| 1236 | }, .{}); | |
| 1178 | 1237 | |
| 1179 | 1238 | var then_scope: Scope.GenZIR = .{ |
| 1180 | .parent = scope, | |
| 1239 | .parent = &block_scope.base, | |
| 1181 | 1240 | .decl = block_scope.decl, |
| 1182 | 1241 | .arena = block_scope.arena, |
| 1183 | 1242 | .instructions = .{}, |
| ... | ... | @@ -1193,38 +1252,41 @@ fn orelseCatchExpr( |
| 1193 | 1252 | if (mem.eql(u8, err_name, "_")) |
| 1194 | 1253 | break :blk &then_scope.base; |
| 1195 | 1254 | |
| 1196 | const unwrapped_err_ptr = try addZIRUnOp(mod, &then_scope.base, src, .unwrap_err_code, operand_ptr); | |
| 1197 | 1255 | err_val_scope = .{ |
| 1198 | 1256 | .parent = &then_scope.base, |
| 1199 | 1257 | .gen_zir = &then_scope, |
| 1200 | 1258 | .name = err_name, |
| 1201 | .inst = try addZIRUnOp(mod, &then_scope.base, src, .deref, unwrapped_err_ptr), | |
| 1259 | .inst = try addZIRUnOp(mod, &then_scope.base, src, unwrap_code_op, operand), | |
| 1202 | 1260 | }; |
| 1203 | 1261 | break :blk &err_val_scope.base; |
| 1204 | 1262 | }; |
| 1205 | 1263 | |
| 1206 | 1264 | _ = try addZIRInst(mod, &then_scope.base, src, zir.Inst.Break, .{ |
| 1207 | 1265 | .block = block, |
| 1208 | .operand = try rlWrap(mod, then_sub_scope, .{ .ref = {} }, try expr(mod, then_sub_scope, branch_rl, rhs)), | |
| 1266 | .operand = try expr(mod, then_sub_scope, branch_rl, rhs), | |
| 1209 | 1267 | }, .{}); |
| 1210 | 1268 | |
| 1211 | 1269 | var else_scope: Scope.GenZIR = .{ |
| 1212 | .parent = scope, | |
| 1270 | .parent = &block_scope.base, | |
| 1213 | 1271 | .decl = block_scope.decl, |
| 1214 | 1272 | .arena = block_scope.arena, |
| 1215 | 1273 | .instructions = .{}, |
| 1216 | 1274 | }; |
| 1217 | 1275 | defer else_scope.instructions.deinit(mod.gpa); |
| 1218 | 1276 | |
| 1219 | const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand_ptr); | |
| 1277 | // This could be a pointer or value depending on `unwrap_op`. | |
| 1278 | const unwrapped_payload = try addZIRUnOp(mod, &else_scope.base, src, unwrap_op, operand); | |
| 1220 | 1279 | _ = try addZIRInst(mod, &else_scope.base, src, zir.Inst.Break, .{ |
| 1221 | 1280 | .block = block, |
| 1222 | 1281 | .operand = unwrapped_payload, |
| 1223 | 1282 | }, .{}); |
| 1224 | 1283 | |
| 1284 | // All branches have been generated, add the instructions to the block. | |
| 1285 | block.positionals.body.instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items); | |
| 1286 | ||
| 1225 | 1287 | condbr.positionals.then_body = .{ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items) }; |
| 1226 | 1288 | condbr.positionals.else_body = .{ .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items) }; |
| 1227 | return rlWrapPtr(mod, scope, rl, &block.base); | |
| 1289 | return &block.base; | |
| 1228 | 1290 | } |
| 1229 | 1291 | |
| 1230 | 1292 | /// Return whether the identifier names of two tokens are equal. Resolves @"" |
| ... | ... | @@ -1253,6 +1315,7 @@ fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfix |
| 1253 | 1315 | const lhs = try expr(mod, scope, .ref, node.lhs); |
| 1254 | 1316 | const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?); |
| 1255 | 1317 | |
| 1318 | // TODO remove all callsites to rlWrapPtr | |
| 1256 | 1319 | return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{})); |
| 1257 | 1320 | } |
| 1258 | 1321 | |
| ... | ... | @@ -1263,6 +1326,7 @@ fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Array |
| 1263 | 1326 | const array_ptr = try expr(mod, scope, .ref, node.lhs); |
| 1264 | 1327 | const index = try expr(mod, scope, .none, node.index_expr); |
| 1265 | 1328 | |
| 1329 | // TODO remove all callsites to rlWrapPtr | |
| 1266 | 1330 | return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ElemPtr, .{ .array_ptr = array_ptr, .index = index }, .{})); |
| 1267 | 1331 | } |
| 1268 | 1332 | |
| ... | ... | @@ -1420,13 +1484,13 @@ const CondKind = union(enum) { |
| 1420 | 1484 | const cond_ptr = try expr(mod, &block_scope.base, .ref, cond_node); |
| 1421 | 1485 | self.* = .{ .optional = cond_ptr }; |
| 1422 | 1486 | const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, cond_ptr); |
| 1423 | return try addZIRUnOp(mod, &block_scope.base, src, .isnonnull, result); | |
| 1487 | return try addZIRUnOp(mod, &block_scope.base, src, .is_non_null, result); | |
| 1424 | 1488 | }, |
| 1425 | 1489 | .err_union => { |
| 1426 | 1490 | const err_ptr = try expr(mod, &block_scope.base, .ref, cond_node); |
| 1427 | 1491 | self.* = .{ .err_union = err_ptr }; |
| 1428 | 1492 | const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, err_ptr); |
| 1429 | return try addZIRUnOp(mod, &block_scope.base, src, .iserr, result); | |
| 1493 | return try addZIRUnOp(mod, &block_scope.base, src, .is_err, result); | |
| 1430 | 1494 | }, |
| 1431 | 1495 | } |
| 1432 | 1496 | } |
| ... | ... | @@ -1456,7 +1520,7 @@ const CondKind = union(enum) { |
| 1456 | 1520 | fn elseSubScope(self: CondKind, mod: *Module, else_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope { |
| 1457 | 1521 | if (self != .err_union) return &else_scope.base; |
| 1458 | 1522 | |
| 1459 | const payload_ptr = try addZIRUnOp(mod, &else_scope.base, src, .unwrap_err_unsafe, self.err_union.?); | |
| 1523 | const payload_ptr = try addZIRUnOp(mod, &else_scope.base, src, .err_union_payload_unsafe_ptr, self.err_union.?); | |
| 1460 | 1524 | |
| 1461 | 1525 | const payload = payload_node.?.castTag(.Payload).?; |
| 1462 | 1526 | const ident_node = payload.error_symbol.castTag(.Identifier).?; |
| ... | ... | @@ -2264,6 +2328,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo |
| 2264 | 2328 | .local_ptr => { |
| 2265 | 2329 | const local_ptr = s.cast(Scope.LocalPtr).?; |
| 2266 | 2330 | if (mem.eql(u8, local_ptr.name, ident_name)) { |
| 2331 | // TODO remove all callsites to rlWrapPtr | |
| 2267 | 2332 | return rlWrapPtr(mod, scope, rl, local_ptr.ptr); |
| 2268 | 2333 | } |
| 2269 | 2334 | s = local_ptr.parent; |
| ... | ... | @@ -3047,6 +3112,7 @@ fn rlWrapVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, resul |
| 3047 | 3112 | |
| 3048 | 3113 | /// TODO go over all the callsites and see where we can introduce "by-value" ZIR instructions |
| 3049 | 3114 | /// to save ZIR memory. For example, see DeclVal vs DeclRef. |
| 3115 | /// Do not add additional callsites to this function. | |
| 3050 | 3116 | fn rlWrapPtr(mod: *Module, scope: *Scope, rl: ResultLoc, ptr: *zir.Inst) InnerError!*zir.Inst { |
| 3051 | 3117 | if (rl == .ref) return ptr; |
| 3052 | 3118 |
src/codegen.zig+31-6| ... | ... | @@ -860,9 +860,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 860 | 860 | .dbg_stmt => return self.genDbgStmt(inst.castTag(.dbg_stmt).?), |
| 861 | 861 | .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?), |
| 862 | 862 | .intcast => return self.genIntCast(inst.castTag(.intcast).?), |
| 863 | .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?), | |
| 864 | .isnull => return self.genIsNull(inst.castTag(.isnull).?), | |
| 865 | .iserr => return self.genIsErr(inst.castTag(.iserr).?), | |
| 863 | .is_non_null => return self.genIsNonNull(inst.castTag(.is_non_null).?), | |
| 864 | .is_non_null_ptr => return self.genIsNonNullPtr(inst.castTag(.is_non_null_ptr).?), | |
| 865 | .is_null => return self.genIsNull(inst.castTag(.is_null).?), | |
| 866 | .is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?), | |
| 867 | .is_err => return self.genIsErr(inst.castTag(.is_err).?), | |
| 868 | .is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?), | |
| 866 | 869 | .load => return self.genLoad(inst.castTag(.load).?), |
| 867 | 870 | .loop => return self.genLoop(inst.castTag(.loop).?), |
| 868 | 871 | .not => return self.genNot(inst.castTag(.not).?), |
| ... | ... | @@ -874,7 +877,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 874 | 877 | .sub => return self.genSub(inst.castTag(.sub).?), |
| 875 | 878 | .switchbr => return self.genSwitch(inst.castTag(.switchbr).?), |
| 876 | 879 | .unreach => return MCValue{ .unreach = {} }, |
| 877 | .unwrap_optional => return self.genUnwrapOptional(inst.castTag(.unwrap_optional).?), | |
| 880 | .optional_payload => return self.genOptionalPayload(inst.castTag(.optional_payload).?), | |
| 881 | .optional_payload_ptr => return self.genOptionalPayloadPtr(inst.castTag(.optional_payload_ptr).?), | |
| 878 | 882 | .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?), |
| 879 | 883 | .varptr => return self.genVarPtr(inst.castTag(.varptr).?), |
| 880 | 884 | .xor => return self.genXor(inst.castTag(.xor).?), |
| ... | ... | @@ -1118,12 +1122,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 1118 | 1122 | } |
| 1119 | 1123 | } |
| 1120 | 1124 | |
| 1121 | fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue { | |
| 1125 | fn genOptionalPayload(self: *Self, inst: *ir.Inst.UnOp) !MCValue { | |
| 1122 | 1126 | // No side effects, so if it's unreferenced, do nothing. |
| 1123 | 1127 | if (inst.base.isUnused()) |
| 1124 | 1128 | return MCValue.dead; |
| 1125 | 1129 | switch (arch) { |
| 1126 | else => return self.fail(inst.base.src, "TODO implement unwrap optional for {}", .{self.target.cpu.arch}), | |
| 1130 | else => return self.fail(inst.base.src, "TODO implement .optional_payload for {}", .{self.target.cpu.arch}), | |
| 1131 | } | |
| 1132 | } | |
| 1133 | ||
| 1134 | fn genOptionalPayloadPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue { | |
| 1135 | // No side effects, so if it's unreferenced, do nothing. | |
| 1136 | if (inst.base.isUnused()) | |
| 1137 | return MCValue.dead; | |
| 1138 | switch (arch) { | |
| 1139 | else => return self.fail(inst.base.src, "TODO implement .optional_payload_ptr for {}", .{self.target.cpu.arch}), | |
| 1127 | 1140 | } |
| 1128 | 1141 | } |
| 1129 | 1142 | |
| ... | ... | @@ -2306,6 +2319,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2306 | 2319 | } |
| 2307 | 2320 | } |
| 2308 | 2321 | |
| 2322 | fn genIsNullPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue { | |
| 2323 | return self.fail(inst.base.src, "TODO load the operand and call genIsNull", .{}); | |
| 2324 | } | |
| 2325 | ||
| 2309 | 2326 | fn genIsNonNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue { |
| 2310 | 2327 | // Here you can specialize this instruction if it makes sense to, otherwise the default |
| 2311 | 2328 | // will call genIsNull and invert the result. |
| ... | ... | @@ -2314,12 +2331,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { |
| 2314 | 2331 | } |
| 2315 | 2332 | } |
| 2316 | 2333 | |
| 2334 | fn genIsNonNullPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue { | |
| 2335 | return self.fail(inst.base.src, "TODO load the operand and call genIsNonNull", .{}); | |
| 2336 | } | |
| 2337 | ||
| 2317 | 2338 | fn genIsErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue { |
| 2318 | 2339 | switch (arch) { |
| 2319 | 2340 | else => return self.fail(inst.base.src, "TODO implement iserr for {}", .{self.target.cpu.arch}), |
| 2320 | 2341 | } |
| 2321 | 2342 | } |
| 2322 | 2343 | |
| 2344 | fn genIsErrPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue { | |
| 2345 | return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{}); | |
| 2346 | } | |
| 2347 | ||
| 2323 | 2348 | fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue { |
| 2324 | 2349 | // A loop is a setup to be able to jump back to the beginning. |
| 2325 | 2350 | const start_index = self.code.items.len; |
src/ir.zig+24-8| ... | ... | @@ -73,9 +73,18 @@ pub const Inst = struct { |
| 73 | 73 | condbr, |
| 74 | 74 | constant, |
| 75 | 75 | dbg_stmt, |
| 76 | isnonnull, | |
| 77 | isnull, | |
| 78 | iserr, | |
| 76 | // ?T => bool | |
| 77 | is_null, | |
| 78 | // ?T => bool (inverted logic) | |
| 79 | is_non_null, | |
| 80 | // *?T => bool | |
| 81 | is_null_ptr, | |
| 82 | // *?T => bool (inverted logic) | |
| 83 | is_non_null_ptr, | |
| 84 | // E!T => bool | |
| 85 | is_err, | |
| 86 | // *E!T => bool | |
| 87 | is_err_ptr, | |
| 79 | 88 | booland, |
| 80 | 89 | boolor, |
| 81 | 90 | /// Read a value from a pointer. |
| ... | ... | @@ -93,7 +102,10 @@ pub const Inst = struct { |
| 93 | 102 | not, |
| 94 | 103 | floatcast, |
| 95 | 104 | intcast, |
| 96 | unwrap_optional, | |
| 105 | // ?T => T | |
| 106 | optional_payload, | |
| 107 | // *?T => *T | |
| 108 | optional_payload_ptr, | |
| 97 | 109 | wrap_optional, |
| 98 | 110 | xor, |
| 99 | 111 | switchbr, |
| ... | ... | @@ -111,14 +123,18 @@ pub const Inst = struct { |
| 111 | 123 | .ret, |
| 112 | 124 | .bitcast, |
| 113 | 125 | .not, |
| 114 | .isnonnull, | |
| 115 | .isnull, | |
| 116 | .iserr, | |
| 126 | .is_non_null, | |
| 127 | .is_non_null_ptr, | |
| 128 | .is_null, | |
| 129 | .is_null_ptr, | |
| 130 | .is_err, | |
| 131 | .is_err_ptr, | |
| 117 | 132 | .ptrtoint, |
| 118 | 133 | .floatcast, |
| 119 | 134 | .intcast, |
| 120 | 135 | .load, |
| 121 | .unwrap_optional, | |
| 136 | .optional_payload, | |
| 137 | .optional_payload_ptr, | |
| 122 | 138 | .wrap_optional, |
| 123 | 139 | => UnOp, |
| 124 | 140 |
src/test.zig+4-1| ... | ... | @@ -696,7 +696,10 @@ pub const TestContext = struct { |
| 696 | 696 | var all_errors = try comp.getAllErrorsAlloc(); |
| 697 | 697 | defer all_errors.deinit(allocator); |
| 698 | 698 | if (all_errors.list.len != 0) { |
| 699 | std.debug.print("\nErrors occurred updating the compilation:\n{s}\n", .{hr}); | |
| 699 | std.debug.print( | |
| 700 | "\nCase '{s}': unexpected errors at update_index={d}:\n{s}\n", | |
| 701 | .{ case.name, update_index, hr }, | |
| 702 | ); | |
| 700 | 703 | for (all_errors.list) |err_msg| { |
| 701 | 704 | switch (err_msg) { |
| 702 | 705 | .src => |src| { |
src/zir.zig+93-37| ... | ... | @@ -174,11 +174,17 @@ pub const Inst = struct { |
| 174 | 174 | /// Make an integer type out of signedness and bit count. |
| 175 | 175 | inttype, |
| 176 | 176 | /// Return a boolean false if an optional is null. `x != null` |
| 177 | isnonnull, | |
| 177 | is_non_null, | |
| 178 | 178 | /// Return a boolean true if an optional is null. `x == null` |
| 179 | isnull, | |
| 179 | is_null, | |
| 180 | /// Return a boolean false if an optional is null. `x.* != null` | |
| 181 | is_non_null_ptr, | |
| 182 | /// Return a boolean true if an optional is null. `x.* == null` | |
| 183 | is_null_ptr, | |
| 180 | 184 | /// Return a boolean true if value is an error |
| 181 | iserr, | |
| 185 | is_err, | |
| 186 | /// Return a boolean true if dereferenced pointer is an error | |
| 187 | is_err_ptr, | |
| 182 | 188 | /// A labeled block of code that loops forever. At the end of the body it is implied |
| 183 | 189 | /// to repeat; no explicit "repeat" instruction terminates loop bodies. |
| 184 | 190 | loop, |
| ... | ... | @@ -278,16 +284,42 @@ pub const Inst = struct { |
| 278 | 284 | optional_type, |
| 279 | 285 | /// Create a union type. |
| 280 | 286 | union_type, |
| 281 | /// Unwraps an optional value 'lhs.?' | |
| 282 | unwrap_optional_safe, | |
| 283 | /// Same as previous, but without safety checks. Used for orelse, if and while | |
| 284 | unwrap_optional_unsafe, | |
| 285 | /// Gets the payload of an error union | |
| 286 | unwrap_err_safe, | |
| 287 | /// Same as previous, but without safety checks. Used for orelse, if and while | |
| 288 | unwrap_err_unsafe, | |
| 289 | /// Gets the error code value of an error union | |
| 290 | unwrap_err_code, | |
| 287 | /// ?T => T with safety. | |
| 288 | /// Given an optional value, returns the payload value, with a safety check that | |
| 289 | /// the value is non-null. Used for `orelse`, `if` and `while`. | |
| 290 | optional_payload_safe, | |
| 291 | /// ?T => T without safety. | |
| 292 | /// Given an optional value, returns the payload value. No safety checks. | |
| 293 | optional_payload_unsafe, | |
| 294 | /// *?T => *T with safety. | |
| 295 | /// Given a pointer to an optional value, returns a pointer to the payload value, | |
| 296 | /// with a safety check that the value is non-null. Used for `orelse`, `if` and `while`. | |
| 297 | optional_payload_safe_ptr, | |
| 298 | /// *?T => *T without safety. | |
| 299 | /// Given a pointer to an optional value, returns a pointer to the payload value. | |
| 300 | /// No safety checks. | |
| 301 | optional_payload_unsafe_ptr, | |
| 302 | /// E!T => T with safety. | |
| 303 | /// Given an error union value, returns the payload value, with a safety check | |
| 304 | /// that the value is not an error. Used for catch, if, and while. | |
| 305 | err_union_payload_safe, | |
| 306 | /// E!T => T without safety. | |
| 307 | /// Given an error union value, returns the payload value. No safety checks. | |
| 308 | err_union_payload_unsafe, | |
| 309 | /// *E!T => *T with safety. | |
| 310 | /// Given a pointer to an error union value, returns a pointer to the payload value, | |
| 311 | /// with a safety check that the value is not an error. Used for catch, if, and while. | |
| 312 | err_union_payload_safe_ptr, | |
| 313 | /// *E!T => *T without safety. | |
| 314 | /// Given a pointer to a error union value, returns a pointer to the payload value. | |
| 315 | /// No safety checks. | |
| 316 | err_union_payload_unsafe_ptr, | |
| 317 | /// E!T => E without safety. | |
| 318 | /// Given an error union value, returns the error code. No safety checks. | |
| 319 | err_union_code, | |
| 320 | /// *E!T => E without safety. | |
| 321 | /// Given a pointer to an error union value, returns the error code. No safety checks. | |
| 322 | err_union_code_ptr, | |
| 291 | 323 | /// Takes a *E!T and raises a compiler error if T != void |
| 292 | 324 | ensure_err_payload_void, |
| 293 | 325 | /// Create a enum literal, |
| ... | ... | @@ -320,9 +352,12 @@ pub const Inst = struct { |
| 320 | 352 | .compileerror, |
| 321 | 353 | .deref, |
| 322 | 354 | .@"return", |
| 323 | .isnull, | |
| 324 | .isnonnull, | |
| 325 | .iserr, | |
| 355 | .is_null, | |
| 356 | .is_non_null, | |
| 357 | .is_null_ptr, | |
| 358 | .is_non_null_ptr, | |
| 359 | .is_err, | |
| 360 | .is_err_ptr, | |
| 326 | 361 | .ptrtoint, |
| 327 | 362 | .ensure_result_used, |
| 328 | 363 | .ensure_result_non_error, |
| ... | ... | @@ -341,11 +376,16 @@ pub const Inst = struct { |
| 341 | 376 | .mut_slice_type, |
| 342 | 377 | .const_slice_type, |
| 343 | 378 | .optional_type, |
| 344 | .unwrap_optional_safe, | |
| 345 | .unwrap_optional_unsafe, | |
| 346 | .unwrap_err_safe, | |
| 347 | .unwrap_err_unsafe, | |
| 348 | .unwrap_err_code, | |
| 379 | .optional_payload_safe, | |
| 380 | .optional_payload_unsafe, | |
| 381 | .optional_payload_safe_ptr, | |
| 382 | .optional_payload_unsafe_ptr, | |
| 383 | .err_union_payload_safe, | |
| 384 | .err_union_payload_unsafe, | |
| 385 | .err_union_payload_safe_ptr, | |
| 386 | .err_union_payload_unsafe_ptr, | |
| 387 | .err_union_code, | |
| 388 | .err_union_code_ptr, | |
| 349 | 389 | .ensure_err_payload_void, |
| 350 | 390 | .anyframe_type, |
| 351 | 391 | .bitnot, |
| ... | ... | @@ -495,9 +535,12 @@ pub const Inst = struct { |
| 495 | 535 | .int, |
| 496 | 536 | .intcast, |
| 497 | 537 | .inttype, |
| 498 | .isnonnull, | |
| 499 | .isnull, | |
| 500 | .iserr, | |
| 538 | .is_non_null, | |
| 539 | .is_null, | |
| 540 | .is_non_null_ptr, | |
| 541 | .is_null_ptr, | |
| 542 | .is_err, | |
| 543 | .is_err_ptr, | |
| 501 | 544 | .mod_rem, |
| 502 | 545 | .mul, |
| 503 | 546 | .mulwrap, |
| ... | ... | @@ -525,11 +568,16 @@ pub const Inst = struct { |
| 525 | 568 | .typeof, |
| 526 | 569 | .xor, |
| 527 | 570 | .optional_type, |
| 528 | .unwrap_optional_safe, | |
| 529 | .unwrap_optional_unsafe, | |
| 530 | .unwrap_err_safe, | |
| 531 | .unwrap_err_unsafe, | |
| 532 | .unwrap_err_code, | |
| 571 | .optional_payload_safe, | |
| 572 | .optional_payload_unsafe, | |
| 573 | .optional_payload_safe_ptr, | |
| 574 | .optional_payload_unsafe_ptr, | |
| 575 | .err_union_payload_safe, | |
| 576 | .err_union_payload_unsafe, | |
| 577 | .err_union_payload_safe_ptr, | |
| 578 | .err_union_payload_unsafe_ptr, | |
| 579 | .err_union_code, | |
| 580 | .err_union_code_ptr, | |
| 533 | 581 | .ptr_type, |
| 534 | 582 | .ensure_err_payload_void, |
| 535 | 583 | .enum_literal, |
| ... | ... | @@ -1540,14 +1588,18 @@ const DumpTzir = struct { |
| 1540 | 1588 | .ret, |
| 1541 | 1589 | .bitcast, |
| 1542 | 1590 | .not, |
| 1543 | .isnonnull, | |
| 1544 | .isnull, | |
| 1545 | .iserr, | |
| 1591 | .is_non_null, | |
| 1592 | .is_non_null_ptr, | |
| 1593 | .is_null, | |
| 1594 | .is_null_ptr, | |
| 1595 | .is_err, | |
| 1596 | .is_err_ptr, | |
| 1546 | 1597 | .ptrtoint, |
| 1547 | 1598 | .floatcast, |
| 1548 | 1599 | .intcast, |
| 1549 | 1600 | .load, |
| 1550 | .unwrap_optional, | |
| 1601 | .optional_payload, | |
| 1602 | .optional_payload_ptr, | |
| 1551 | 1603 | .wrap_optional, |
| 1552 | 1604 | => { |
| 1553 | 1605 | const un_op = inst.cast(ir.Inst.UnOp).?; |
| ... | ... | @@ -1637,14 +1689,18 @@ const DumpTzir = struct { |
| 1637 | 1689 | .ret, |
| 1638 | 1690 | .bitcast, |
| 1639 | 1691 | .not, |
| 1640 | .isnonnull, | |
| 1641 | .isnull, | |
| 1642 | .iserr, | |
| 1692 | .is_non_null, | |
| 1693 | .is_null, | |
| 1694 | .is_non_null_ptr, | |
| 1695 | .is_null_ptr, | |
| 1696 | .is_err, | |
| 1697 | .is_err_ptr, | |
| 1643 | 1698 | .ptrtoint, |
| 1644 | 1699 | .floatcast, |
| 1645 | 1700 | .intcast, |
| 1646 | 1701 | .load, |
| 1647 | .unwrap_optional, | |
| 1702 | .optional_payload, | |
| 1703 | .optional_payload_ptr, | |
| 1648 | 1704 | .wrap_optional, |
| 1649 | 1705 | => { |
| 1650 | 1706 | const un_op = inst.cast(ir.Inst.UnOp).?; |
src/zir_sema.zig+110-25| ... | ... | @@ -127,18 +127,26 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError! |
| 127 | 127 | .cmp_gt => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_gt).?, .gt), |
| 128 | 128 | .cmp_neq => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_neq).?, .neq), |
| 129 | 129 | .condbr => return analyzeInstCondBr(mod, scope, old_inst.castTag(.condbr).?), |
| 130 | .isnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnull).?, true), | |
| 131 | .isnonnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnonnull).?, false), | |
| 132 | .iserr => return analyzeInstIsErr(mod, scope, old_inst.castTag(.iserr).?), | |
| 130 | .is_null => return isNull(mod, scope, old_inst.castTag(.is_null).?, false), | |
| 131 | .is_non_null => return isNull(mod, scope, old_inst.castTag(.is_non_null).?, true), | |
| 132 | .is_null_ptr => return isNullPtr(mod, scope, old_inst.castTag(.is_null_ptr).?, false), | |
| 133 | .is_non_null_ptr => return isNullPtr(mod, scope, old_inst.castTag(.is_non_null_ptr).?, true), | |
| 134 | .is_err => return isErr(mod, scope, old_inst.castTag(.is_err).?), | |
| 135 | .is_err_ptr => return isErrPtr(mod, scope, old_inst.castTag(.is_err_ptr).?), | |
| 133 | 136 | .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?), |
| 134 | 137 | .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?), |
| 135 | 138 | .typeof_peer => return analyzeInstTypeOfPeer(mod, scope, old_inst.castTag(.typeof_peer).?), |
| 136 | 139 | .optional_type => return analyzeInstOptionalType(mod, scope, old_inst.castTag(.optional_type).?), |
| 137 | .unwrap_optional_safe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_safe).?, true), | |
| 138 | .unwrap_optional_unsafe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_unsafe).?, false), | |
| 139 | .unwrap_err_safe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_safe).?, true), | |
| 140 | .unwrap_err_unsafe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_unsafe).?, false), | |
| 141 | .unwrap_err_code => return analyzeInstUnwrapErrCode(mod, scope, old_inst.castTag(.unwrap_err_code).?), | |
| 140 | .optional_payload_safe => return optionalPayload(mod, scope, old_inst.castTag(.optional_payload_safe).?, true), | |
| 141 | .optional_payload_unsafe => return optionalPayload(mod, scope, old_inst.castTag(.optional_payload_unsafe).?, false), | |
| 142 | .optional_payload_safe_ptr => return optionalPayloadPtr(mod, scope, old_inst.castTag(.optional_payload_safe_ptr).?, true), | |
| 143 | .optional_payload_unsafe_ptr => return optionalPayloadPtr(mod, scope, old_inst.castTag(.optional_payload_unsafe_ptr).?, false), | |
| 144 | .err_union_payload_safe => return errorUnionPayload(mod, scope, old_inst.castTag(.err_union_payload_safe).?, true), | |
| 145 | .err_union_payload_unsafe => return errorUnionPayload(mod, scope, old_inst.castTag(.err_union_payload_unsafe).?, false), | |
| 146 | .err_union_payload_safe_ptr => return errorUnionPayloadPtr(mod, scope, old_inst.castTag(.err_union_payload_safe_ptr).?, true), | |
| 147 | .err_union_payload_unsafe_ptr => return errorUnionPayloadPtr(mod, scope, old_inst.castTag(.err_union_payload_unsafe_ptr).?, false), | |
| 148 | .err_union_code => return errorUnionCode(mod, scope, old_inst.castTag(.err_union_code).?), | |
| 149 | .err_union_code_ptr => return errorUnionCodePtr(mod, scope, old_inst.castTag(.err_union_code_ptr).?), | |
| 142 | 150 | .ensure_err_payload_void => return analyzeInstEnsureErrPayloadVoid(mod, scope, old_inst.castTag(.ensure_err_payload_void).?), |
| 143 | 151 | .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?), |
| 144 | 152 | .array_type_sentinel => return analyzeInstArrayTypeSentinel(mod, scope, old_inst.castTag(.array_type_sentinel).?), |
| ... | ... | @@ -1104,48 +1112,109 @@ fn analyzeInstEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiter |
| 1104 | 1112 | }); |
| 1105 | 1113 | } |
| 1106 | 1114 | |
| 1107 | fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { | |
| 1115 | /// Pointer in, pointer out. | |
| 1116 | fn optionalPayloadPtr( | |
| 1117 | mod: *Module, | |
| 1118 | scope: *Scope, | |
| 1119 | unwrap: *zir.Inst.UnOp, | |
| 1120 | safety_check: bool, | |
| 1121 | ) InnerError!*Inst { | |
| 1108 | 1122 | const tracy = trace(@src()); |
| 1109 | 1123 | defer tracy.end(); |
| 1110 | const operand = try resolveInst(mod, scope, unwrap.positionals.operand); | |
| 1111 | assert(operand.ty.zigTypeTag() == .Pointer); | |
| 1112 | 1124 | |
| 1113 | const elem_type = operand.ty.elemType(); | |
| 1114 | if (elem_type.zigTypeTag() != .Optional) { | |
| 1115 | return mod.fail(scope, unwrap.base.src, "expected optional type, found {}", .{elem_type}); | |
| 1125 | const optional_ptr = try resolveInst(mod, scope, unwrap.positionals.operand); | |
| 1126 | assert(optional_ptr.ty.zigTypeTag() == .Pointer); | |
| 1127 | ||
| 1128 | const opt_type = optional_ptr.ty.elemType(); | |
| 1129 | if (opt_type.zigTypeTag() != .Optional) { | |
| 1130 | return mod.fail(scope, unwrap.base.src, "expected optional type, found {}", .{opt_type}); | |
| 1116 | 1131 | } |
| 1117 | 1132 | |
| 1118 | const child_type = try elem_type.optionalChildAlloc(scope.arena()); | |
| 1119 | const child_pointer = try mod.simplePtrType(scope, unwrap.base.src, child_type, operand.ty.isConstPtr(), .One); | |
| 1133 | const child_type = try opt_type.optionalChildAlloc(scope.arena()); | |
| 1134 | const child_pointer = try mod.simplePtrType(scope, unwrap.base.src, child_type, !optional_ptr.ty.isConstPtr(), .One); | |
| 1120 | 1135 | |
| 1121 | if (operand.value()) |val| { | |
| 1136 | if (optional_ptr.value()) |pointer_val| { | |
| 1137 | const val = try pointer_val.pointerDeref(scope.arena()); | |
| 1122 | 1138 | if (val.isNull()) { |
| 1123 | 1139 | return mod.fail(scope, unwrap.base.src, "unable to unwrap null", .{}); |
| 1124 | 1140 | } |
| 1141 | // The same Value represents the pointer to the optional and the payload. | |
| 1125 | 1142 | return mod.constInst(scope, unwrap.base.src, .{ |
| 1126 | 1143 | .ty = child_pointer, |
| 1144 | .val = pointer_val, | |
| 1145 | }); | |
| 1146 | } | |
| 1147 | ||
| 1148 | const b = try mod.requireRuntimeBlock(scope, unwrap.base.src); | |
| 1149 | if (safety_check and mod.wantSafety(scope)) { | |
| 1150 | const is_non_null = try mod.addUnOp(b, unwrap.base.src, Type.initTag(.bool), .is_non_null_ptr, optional_ptr); | |
| 1151 | try mod.addSafetyCheck(b, is_non_null, .unwrap_null); | |
| 1152 | } | |
| 1153 | return mod.addUnOp(b, unwrap.base.src, child_pointer, .optional_payload_ptr, optional_ptr); | |
| 1154 | } | |
| 1155 | ||
| 1156 | /// Value in, value out. | |
| 1157 | fn optionalPayload( | |
| 1158 | mod: *Module, | |
| 1159 | scope: *Scope, | |
| 1160 | unwrap: *zir.Inst.UnOp, | |
| 1161 | safety_check: bool, | |
| 1162 | ) InnerError!*Inst { | |
| 1163 | const tracy = trace(@src()); | |
| 1164 | defer tracy.end(); | |
| 1165 | ||
| 1166 | const operand = try resolveInst(mod, scope, unwrap.positionals.operand); | |
| 1167 | const opt_type = operand.ty; | |
| 1168 | if (opt_type.zigTypeTag() != .Optional) { | |
| 1169 | return mod.fail(scope, unwrap.base.src, "expected optional type, found {}", .{opt_type}); | |
| 1170 | } | |
| 1171 | ||
| 1172 | const child_type = try opt_type.optionalChildAlloc(scope.arena()); | |
| 1173 | ||
| 1174 | if (operand.value()) |val| { | |
| 1175 | if (val.isNull()) { | |
| 1176 | return mod.fail(scope, unwrap.base.src, "unable to unwrap null", .{}); | |
| 1177 | } | |
| 1178 | return mod.constInst(scope, unwrap.base.src, .{ | |
| 1179 | .ty = child_type, | |
| 1127 | 1180 | .val = val, |
| 1128 | 1181 | }); |
| 1129 | 1182 | } |
| 1130 | 1183 | |
| 1131 | 1184 | const b = try mod.requireRuntimeBlock(scope, unwrap.base.src); |
| 1132 | 1185 | if (safety_check and mod.wantSafety(scope)) { |
| 1133 | const is_non_null = try mod.addUnOp(b, unwrap.base.src, Type.initTag(.bool), .isnonnull, operand); | |
| 1186 | const is_non_null = try mod.addUnOp(b, unwrap.base.src, Type.initTag(.bool), .is_non_null, operand); | |
| 1134 | 1187 | try mod.addSafetyCheck(b, is_non_null, .unwrap_null); |
| 1135 | 1188 | } |
| 1136 | return mod.addUnOp(b, unwrap.base.src, child_pointer, .unwrap_optional, operand); | |
| 1189 | return mod.addUnOp(b, unwrap.base.src, child_type, .optional_payload, operand); | |
| 1137 | 1190 | } |
| 1138 | 1191 | |
| 1139 | fn analyzeInstUnwrapErr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { | |
| 1192 | /// Value in, value out | |
| 1193 | fn errorUnionPayload(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { | |
| 1140 | 1194 | const tracy = trace(@src()); |
| 1141 | 1195 | defer tracy.end(); |
| 1142 | return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstUnwrapErr", .{}); | |
| 1196 | return mod.fail(scope, unwrap.base.src, "TODO implement zir_sema.errorUnionPayload", .{}); | |
| 1143 | 1197 | } |
| 1144 | 1198 | |
| 1145 | fn analyzeInstUnwrapErrCode(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { | |
| 1199 | /// Pointer in, pointer out | |
| 1200 | fn errorUnionPayloadPtr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst { | |
| 1146 | 1201 | const tracy = trace(@src()); |
| 1147 | 1202 | defer tracy.end(); |
| 1148 | return mod.fail(scope, unwrap.base.src, "TODO implement analyzeInstUnwrapErrCode", .{}); | |
| 1203 | return mod.fail(scope, unwrap.base.src, "TODO implement zir_sema.errorUnionPayloadPtr", .{}); | |
| 1204 | } | |
| 1205 | ||
| 1206 | /// Value in, value out | |
| 1207 | fn errorUnionCode(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { | |
| 1208 | const tracy = trace(@src()); | |
| 1209 | defer tracy.end(); | |
| 1210 | return mod.fail(scope, unwrap.base.src, "TODO implement zir_sema.errorUnionCode", .{}); | |
| 1211 | } | |
| 1212 | ||
| 1213 | /// Pointer in, value out | |
| 1214 | fn errorUnionCodePtr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { | |
| 1215 | const tracy = trace(@src()); | |
| 1216 | defer tracy.end(); | |
| 1217 | return mod.fail(scope, unwrap.base.src, "TODO implement zir_sema.errorUnionCodePtr", .{}); | |
| 1149 | 1218 | } |
| 1150 | 1219 | |
| 1151 | 1220 | fn analyzeInstEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst { |
| ... | ... | @@ -2074,20 +2143,36 @@ fn analyzeInstBoolOp(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerEr |
| 2074 | 2143 | return mod.addBinOp(b, inst.base.src, bool_type, if (is_bool_or) .boolor else .booland, lhs, rhs); |
| 2075 | 2144 | } |
| 2076 | 2145 | |
| 2077 | fn analyzeInstIsNonNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst { | |
| 2146 | fn isNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst { | |
| 2078 | 2147 | const tracy = trace(@src()); |
| 2079 | 2148 | defer tracy.end(); |
| 2080 | 2149 | const operand = try resolveInst(mod, scope, inst.positionals.operand); |
| 2081 | 2150 | return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic); |
| 2082 | 2151 | } |
| 2083 | 2152 | |
| 2084 | fn analyzeInstIsErr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 2153 | fn isNullPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst { | |
| 2154 | const tracy = trace(@src()); | |
| 2155 | defer tracy.end(); | |
| 2156 | const ptr = try resolveInst(mod, scope, inst.positionals.operand); | |
| 2157 | const loaded = try mod.analyzeDeref(scope, inst.base.src, ptr, ptr.src); | |
| 2158 | return mod.analyzeIsNull(scope, inst.base.src, loaded, invert_logic); | |
| 2159 | } | |
| 2160 | ||
| 2161 | fn isErr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 2085 | 2162 | const tracy = trace(@src()); |
| 2086 | 2163 | defer tracy.end(); |
| 2087 | 2164 | const operand = try resolveInst(mod, scope, inst.positionals.operand); |
| 2088 | 2165 | return mod.analyzeIsErr(scope, inst.base.src, operand); |
| 2089 | 2166 | } |
| 2090 | 2167 | |
| 2168 | fn isErrPtr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst { | |
| 2169 | const tracy = trace(@src()); | |
| 2170 | defer tracy.end(); | |
| 2171 | const ptr = try resolveInst(mod, scope, inst.positionals.operand); | |
| 2172 | const loaded = try mod.analyzeDeref(scope, inst.base.src, ptr, ptr.src); | |
| 2173 | return mod.analyzeIsErr(scope, inst.base.src, loaded); | |
| 2174 | } | |
| 2175 | ||
| 2091 | 2176 | fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst { |
| 2092 | 2177 | const tracy = trace(@src()); |
| 2093 | 2178 | defer tracy.end(); |