authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-18 19:29:23-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-01-18 19:29:53-07:00
log7e56028bc7c00b884c92e2948728cbc47e5a8a09
tree660b8f5e027066e728fbd88db5e58385940aa2d7
parent6c7e66613d57aec2f2949c065ea6431ff6c31f88
parentecc246efa2c133aaab73032a18fed5b2c15e08ce

Merge branch 'stage2: rework ZIR/TZIR for optionals and error unions'

closes #7730 closes #7662

8 files changed, 404 insertions(+), 106 deletions(-)

src/Module.zig+1-1
...@@ -2453,7 +2453,7 @@ pub fn analyzeIsNull(...@@ -2453,7 +2453,7 @@ pub fn analyzeIsNull(
2453 return self.constBool(scope, src, bool_value);2453 return self.constBool(scope, src, bool_value);
2454 }2454 }
2455 const b = try self.requireRuntimeBlock(scope, src);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 return self.addUnOp(b, src, Type.initTag(.bool), inst_tag, operand);2457 return self.addUnOp(b, src, Type.initTag(.bool), inst_tag, operand);
2458}2458}
24592459
src/astgen.zig+94-28
...@@ -118,7 +118,6 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {...@@ -118,7 +118,6 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
118 .LabeledBlock,118 .LabeledBlock,
119 .Break,119 .Break,
120 .PtrType,120 .PtrType,
121 .GroupedExpression,
122 .ArrayType,121 .ArrayType,
123 .ArrayTypeSentinel,122 .ArrayTypeSentinel,
124 .EnumLiteral,123 .EnumLiteral,
...@@ -129,7 +128,6 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {...@@ -129,7 +128,6 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
129 .ErrorUnion,128 .ErrorUnion,
130 .MergeErrorSets,129 .MergeErrorSets,
131 .Range,130 .Range,
132 .OrElse,
133 .Await,131 .Await,
134 .BitNot,132 .BitNot,
135 .Negation,133 .Negation,
...@@ -168,7 +166,14 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {...@@ -168,7 +166,14 @@ fn lvalExpr(mod: *Module, scope: *Scope, node: *ast.Node) InnerError!*zir.Inst {
168 },166 },
169167
170 // can be assigned to168 // 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 return expr(mod, scope, .ref, node);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,8 +918,12 @@ fn unwrapOptional(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Si
913 const tree = scope.tree();918 const tree = scope.tree();
914 const src = tree.token_locs[node.rtoken].start;919 const src = tree.token_locs[node.rtoken].start;
915920
916 const operand = try expr(mod, scope, .ref, node.lhs);921 const operand = try expr(mod, scope, rl, node.lhs);
917 return rlWrapPtr(mod, scope, rl, try addZIRUnOp(mod, scope, src, .unwrap_optional_safe, operand));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}
919928
920fn containerField(929fn containerField(
...@@ -1110,6 +1119,7 @@ fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Erro...@@ -1110,6 +1119,7 @@ fn errorSetDecl(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Erro
1110 }1119 }
11111120
1112 // analyzing the error set results in a decl ref, so we might need to dereference it1121 // analyzing the error set results in a decl ref, so we might need to dereference it
1122 // TODO remove all callsites to rlWrapPtr
1113 return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{}));1123 return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ErrorSet, .{ .fields = fields }, .{}));
1114}1124}
11151125
...@@ -1123,11 +1133,61 @@ fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*...@@ -1123,11 +1133,61 @@ fn errorType(mod: *Module, scope: *Scope, node: *ast.Node.OneToken) InnerError!*
1123}1133}
11241134
1125fn catchExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Catch) InnerError!*zir.Inst {1135fn 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}
11281163
1129fn orelseExpr(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfixOp) InnerError!*zir.Inst {1164fn 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, .isnull, .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}
11321192
1133fn orelseCatchExpr(1193fn orelseCatchExpr(
...@@ -1138,17 +1198,13 @@ fn orelseCatchExpr(...@@ -1138,17 +1198,13 @@ fn orelseCatchExpr(
1138 op_token: ast.TokenIndex,1198 op_token: ast.TokenIndex,
1139 cond_op: zir.Inst.Tag,1199 cond_op: zir.Inst.Tag,
1140 unwrap_op: zir.Inst.Tag,1200 unwrap_op: zir.Inst.Tag,
1201 unwrap_code_op: zir.Inst.Tag,
1141 rhs: *ast.Node,1202 rhs: *ast.Node,
1142 payload_node: ?*ast.Node,1203 payload_node: ?*ast.Node,
1143) InnerError!*zir.Inst {1204) InnerError!*zir.Inst {
1144 const tree = scope.tree();1205 const tree = scope.tree();
1145 const src = tree.token_locs[op_token].start;1206 const src = tree.token_locs[op_token].start;
11461207
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 var block_scope: Scope.GenZIR = .{1208 var block_scope: Scope.GenZIR = .{
1153 .parent = scope,1209 .parent = scope,
1154 .decl = scope.ownerDecl().?,1210 .decl = scope.ownerDecl().?,
...@@ -1157,14 +1213,8 @@ fn orelseCatchExpr(...@@ -1157,14 +1213,8 @@ fn orelseCatchExpr(
1157 };1213 };
1158 defer block_scope.instructions.deinit(mod.gpa);1214 defer block_scope.instructions.deinit(mod.gpa);
11591215
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 const block = try addZIRInstBlock(mod, scope, src, .block, .{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 });
11691219
1170 // Most result location types can be forwarded directly; however1220 // Most result location types can be forwarded directly; however
...@@ -1175,9 +1225,18 @@ fn orelseCatchExpr(...@@ -1175,9 +1225,18 @@ fn orelseCatchExpr(
1175 .discard, .none, .ty, .ptr, .ref => rl,1225 .discard, .none, .ty, .ptr, .ref => rl,
1176 .inferred_ptr, .bitcasted_ptr, .block_ptr => .{ .block_ptr = block },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 }, .{});
11781237
1179 var then_scope: Scope.GenZIR = .{1238 var then_scope: Scope.GenZIR = .{
1180 .parent = scope,1239 .parent = &block_scope.base,
1181 .decl = block_scope.decl,1240 .decl = block_scope.decl,
1182 .arena = block_scope.arena,1241 .arena = block_scope.arena,
1183 .instructions = .{},1242 .instructions = .{},
...@@ -1193,12 +1252,11 @@ fn orelseCatchExpr(...@@ -1193,12 +1252,11 @@ fn orelseCatchExpr(
1193 if (mem.eql(u8, err_name, "_"))1252 if (mem.eql(u8, err_name, "_"))
1194 break :blk &then_scope.base;1253 break :blk &then_scope.base;
11951254
1196 const unwrapped_err_ptr = try addZIRUnOp(mod, &then_scope.base, src, .unwrap_err_code, operand_ptr);
1197 err_val_scope = .{1255 err_val_scope = .{
1198 .parent = &then_scope.base,1256 .parent = &then_scope.base,
1199 .gen_zir = &then_scope,1257 .gen_zir = &then_scope,
1200 .name = err_name,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 break :blk &err_val_scope.base;1261 break :blk &err_val_scope.base;
1204 };1262 };
...@@ -1209,22 +1267,26 @@ fn orelseCatchExpr(...@@ -1209,22 +1267,26 @@ fn orelseCatchExpr(
1209 }, .{});1267 }, .{});
12101268
1211 var else_scope: Scope.GenZIR = .{1269 var else_scope: Scope.GenZIR = .{
1212 .parent = scope,1270 .parent = &block_scope.base,
1213 .decl = block_scope.decl,1271 .decl = block_scope.decl,
1214 .arena = block_scope.arena,1272 .arena = block_scope.arena,
1215 .instructions = .{},1273 .instructions = .{},
1216 };1274 };
1217 defer else_scope.instructions.deinit(mod.gpa);1275 defer else_scope.instructions.deinit(mod.gpa);
12181276
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 _ = try addZIRInst(mod, &else_scope.base, src, zir.Inst.Break, .{1279 _ = try addZIRInst(mod, &else_scope.base, src, zir.Inst.Break, .{
1221 .block = block,1280 .block = block,
1222 .operand = unwrapped_payload,1281 .operand = unwrapped_payload,
1223 }, .{});1282 }, .{});
12241283
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 condbr.positionals.then_body = .{ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items) };1287 condbr.positionals.then_body = .{ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items) };
1226 condbr.positionals.else_body = .{ .instructions = try else_scope.arena.dupe(*zir.Inst, else_scope.instructions.items) };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}
12291291
1230/// Return whether the identifier names of two tokens are equal. Resolves @""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,6 +1315,7 @@ fn field(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.SimpleInfix
1253 const lhs = try expr(mod, scope, .ref, node.lhs);1315 const lhs = try expr(mod, scope, .ref, node.lhs);
1254 const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?);1316 const field_name = try identifierStringInst(mod, scope, node.rhs.castTag(.Identifier).?);
12551317
1318 // TODO remove all callsites to rlWrapPtr
1256 return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{}));1319 return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.FieldPtr, .{ .object_ptr = lhs, .field_name = field_name }, .{}));
1257}1320}
12581321
...@@ -1263,6 +1326,7 @@ fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Array...@@ -1263,6 +1326,7 @@ fn arrayAccess(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node.Array
1263 const array_ptr = try expr(mod, scope, .ref, node.lhs);1326 const array_ptr = try expr(mod, scope, .ref, node.lhs);
1264 const index = try expr(mod, scope, .none, node.index_expr);1327 const index = try expr(mod, scope, .none, node.index_expr);
12651328
1329 // TODO remove all callsites to rlWrapPtr
1266 return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ElemPtr, .{ .array_ptr = array_ptr, .index = index }, .{}));1330 return rlWrapPtr(mod, scope, rl, try addZIRInst(mod, scope, src, zir.Inst.ElemPtr, .{ .array_ptr = array_ptr, .index = index }, .{}));
1267}1331}
12681332
...@@ -1420,13 +1484,13 @@ const CondKind = union(enum) {...@@ -1420,13 +1484,13 @@ const CondKind = union(enum) {
1420 const cond_ptr = try expr(mod, &block_scope.base, .ref, cond_node);1484 const cond_ptr = try expr(mod, &block_scope.base, .ref, cond_node);
1421 self.* = .{ .optional = cond_ptr };1485 self.* = .{ .optional = cond_ptr };
1422 const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, cond_ptr);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 .err_union => {1489 .err_union => {
1426 const err_ptr = try expr(mod, &block_scope.base, .ref, cond_node);1490 const err_ptr = try expr(mod, &block_scope.base, .ref, cond_node);
1427 self.* = .{ .err_union = err_ptr };1491 self.* = .{ .err_union = err_ptr };
1428 const result = try addZIRUnOp(mod, &block_scope.base, src, .deref, err_ptr);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,7 +1520,7 @@ const CondKind = union(enum) {
1456 fn elseSubScope(self: CondKind, mod: *Module, else_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {1520 fn elseSubScope(self: CondKind, mod: *Module, else_scope: *Scope.GenZIR, src: usize, payload_node: ?*ast.Node) !*Scope {
1457 if (self != .err_union) return &else_scope.base;1521 if (self != .err_union) return &else_scope.base;
14581522
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.?);
14601524
1461 const payload = payload_node.?.castTag(.Payload).?;1525 const payload = payload_node.?.castTag(.Payload).?;
1462 const ident_node = payload.error_symbol.castTag(.Identifier).?;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,6 +2328,7 @@ fn identifier(mod: *Module, scope: *Scope, rl: ResultLoc, ident: *ast.Node.OneTo
2264 .local_ptr => {2328 .local_ptr => {
2265 const local_ptr = s.cast(Scope.LocalPtr).?;2329 const local_ptr = s.cast(Scope.LocalPtr).?;
2266 if (mem.eql(u8, local_ptr.name, ident_name)) {2330 if (mem.eql(u8, local_ptr.name, ident_name)) {
2331 // TODO remove all callsites to rlWrapPtr
2267 return rlWrapPtr(mod, scope, rl, local_ptr.ptr);2332 return rlWrapPtr(mod, scope, rl, local_ptr.ptr);
2268 }2333 }
2269 s = local_ptr.parent;2334 s = local_ptr.parent;
...@@ -3047,6 +3112,7 @@ fn rlWrapVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, resul...@@ -3047,6 +3112,7 @@ fn rlWrapVoid(mod: *Module, scope: *Scope, rl: ResultLoc, node: *ast.Node, resul
30473112
3048/// TODO go over all the callsites and see where we can introduce "by-value" ZIR instructions3113/// TODO go over all the callsites and see where we can introduce "by-value" ZIR instructions
3049/// to save ZIR memory. For example, see DeclVal vs DeclRef.3114/// to save ZIR memory. For example, see DeclVal vs DeclRef.
3115/// Do not add additional callsites to this function.
3050fn rlWrapPtr(mod: *Module, scope: *Scope, rl: ResultLoc, ptr: *zir.Inst) InnerError!*zir.Inst {3116fn rlWrapPtr(mod: *Module, scope: *Scope, rl: ResultLoc, ptr: *zir.Inst) InnerError!*zir.Inst {
3051 if (rl == .ref) return ptr;3117 if (rl == .ref) return ptr;
30523118
src/codegen.zig+31-6
...@@ -860,9 +860,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -860,9 +860,12 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
860 .dbg_stmt => return self.genDbgStmt(inst.castTag(.dbg_stmt).?),860 .dbg_stmt => return self.genDbgStmt(inst.castTag(.dbg_stmt).?),
861 .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),861 .floatcast => return self.genFloatCast(inst.castTag(.floatcast).?),
862 .intcast => return self.genIntCast(inst.castTag(.intcast).?),862 .intcast => return self.genIntCast(inst.castTag(.intcast).?),
863 .isnonnull => return self.genIsNonNull(inst.castTag(.isnonnull).?),863 .is_non_null => return self.genIsNonNull(inst.castTag(.is_non_null).?),
864 .isnull => return self.genIsNull(inst.castTag(.isnull).?),864 .is_non_null_ptr => return self.genIsNonNullPtr(inst.castTag(.is_non_null_ptr).?),
865 .iserr => return self.genIsErr(inst.castTag(.iserr).?),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 .load => return self.genLoad(inst.castTag(.load).?),869 .load => return self.genLoad(inst.castTag(.load).?),
867 .loop => return self.genLoop(inst.castTag(.loop).?),870 .loop => return self.genLoop(inst.castTag(.loop).?),
868 .not => return self.genNot(inst.castTag(.not).?),871 .not => return self.genNot(inst.castTag(.not).?),
...@@ -874,7 +877,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -874,7 +877,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
874 .sub => return self.genSub(inst.castTag(.sub).?),877 .sub => return self.genSub(inst.castTag(.sub).?),
875 .switchbr => return self.genSwitch(inst.castTag(.switchbr).?),878 .switchbr => return self.genSwitch(inst.castTag(.switchbr).?),
876 .unreach => return MCValue{ .unreach = {} },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 .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),882 .wrap_optional => return self.genWrapOptional(inst.castTag(.wrap_optional).?),
879 .varptr => return self.genVarPtr(inst.castTag(.varptr).?),883 .varptr => return self.genVarPtr(inst.castTag(.varptr).?),
880 .xor => return self.genXor(inst.castTag(.xor).?),884 .xor => return self.genXor(inst.castTag(.xor).?),
...@@ -1118,12 +1122,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -1118,12 +1122,21 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
1118 }1122 }
1119 }1123 }
11201124
1121 fn genUnwrapOptional(self: *Self, inst: *ir.Inst.UnOp) !MCValue {1125 fn genOptionalPayload(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
1122 // No side effects, so if it's unreferenced, do nothing.1126 // No side effects, so if it's unreferenced, do nothing.
1123 if (inst.base.isUnused())1127 if (inst.base.isUnused())
1124 return MCValue.dead;1128 return MCValue.dead;
1125 switch (arch) {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 }
11291142
...@@ -2306,6 +2319,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2306,6 +2319,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2306 }2319 }
2307 }2320 }
23082321
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 fn genIsNonNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {2326 fn genIsNonNull(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
2310 // Here you can specialize this instruction if it makes sense to, otherwise the default2327 // Here you can specialize this instruction if it makes sense to, otherwise the default
2311 // will call genIsNull and invert the result.2328 // will call genIsNull and invert the result.
...@@ -2314,12 +2331,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {...@@ -2314,12 +2331,20 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type {
2314 }2331 }
2315 }2332 }
23162333
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 fn genIsErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {2338 fn genIsErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue {
2318 switch (arch) {2339 switch (arch) {
2319 else => return self.fail(inst.base.src, "TODO implement iserr for {}", .{self.target.cpu.arch}),2340 else => return self.fail(inst.base.src, "TODO implement iserr for {}", .{self.target.cpu.arch}),
2320 }2341 }
2321 }2342 }
23222343
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 fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue {2348 fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue {
2324 // A loop is a setup to be able to jump back to the beginning.2349 // A loop is a setup to be able to jump back to the beginning.
2325 const start_index = self.code.items.len;2350 const start_index = self.code.items.len;
src/ir.zig+24-8
...@@ -73,9 +73,18 @@ pub const Inst = struct {...@@ -73,9 +73,18 @@ pub const Inst = struct {
73 condbr,73 condbr,
74 constant,74 constant,
75 dbg_stmt,75 dbg_stmt,
76 isnonnull,76 // ?T => bool
77 isnull,77 is_null,
78 iserr,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 booland,88 booland,
80 boolor,89 boolor,
81 /// Read a value from a pointer.90 /// Read a value from a pointer.
...@@ -93,7 +102,10 @@ pub const Inst = struct {...@@ -93,7 +102,10 @@ pub const Inst = struct {
93 not,102 not,
94 floatcast,103 floatcast,
95 intcast,104 intcast,
96 unwrap_optional,105 // ?T => T
106 optional_payload,
107 // *?T => *T
108 optional_payload_ptr,
97 wrap_optional,109 wrap_optional,
98 xor,110 xor,
99 switchbr,111 switchbr,
...@@ -111,14 +123,18 @@ pub const Inst = struct {...@@ -111,14 +123,18 @@ pub const Inst = struct {
111 .ret,123 .ret,
112 .bitcast,124 .bitcast,
113 .not,125 .not,
114 .isnonnull,126 .is_non_null,
115 .isnull,127 .is_non_null_ptr,
116 .iserr,128 .is_null,
129 .is_null_ptr,
130 .is_err,
131 .is_err_ptr,
117 .ptrtoint,132 .ptrtoint,
118 .floatcast,133 .floatcast,
119 .intcast,134 .intcast,
120 .load,135 .load,
121 .unwrap_optional,136 .optional_payload,
137 .optional_payload_ptr,
122 .wrap_optional,138 .wrap_optional,
123 => UnOp,139 => UnOp,
124140
src/test.zig+4-1
...@@ -696,7 +696,10 @@ pub const TestContext = struct {...@@ -696,7 +696,10 @@ pub const TestContext = struct {
696 var all_errors = try comp.getAllErrorsAlloc();696 var all_errors = try comp.getAllErrorsAlloc();
697 defer all_errors.deinit(allocator);697 defer all_errors.deinit(allocator);
698 if (all_errors.list.len != 0) {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 for (all_errors.list) |err_msg| {703 for (all_errors.list) |err_msg| {
701 switch (err_msg) {704 switch (err_msg) {
702 .src => |src| {705 .src => |src| {
src/zir.zig+93-37
...@@ -174,11 +174,17 @@ pub const Inst = struct {...@@ -174,11 +174,17 @@ pub const Inst = struct {
174 /// Make an integer type out of signedness and bit count.174 /// Make an integer type out of signedness and bit count.
175 inttype,175 inttype,
176 /// Return a boolean false if an optional is null. `x != null`176 /// Return a boolean false if an optional is null. `x != null`
177 isnonnull,177 is_non_null,
178 /// Return a boolean true if an optional is null. `x == null`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 /// Return a boolean true if value is an error184 /// 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 /// A labeled block of code that loops forever. At the end of the body it is implied188 /// A labeled block of code that loops forever. At the end of the body it is implied
183 /// to repeat; no explicit "repeat" instruction terminates loop bodies.189 /// to repeat; no explicit "repeat" instruction terminates loop bodies.
184 loop,190 loop,
...@@ -278,16 +284,42 @@ pub const Inst = struct {...@@ -278,16 +284,42 @@ pub const Inst = struct {
278 optional_type,284 optional_type,
279 /// Create a union type.285 /// Create a union type.
280 union_type,286 union_type,
281 /// Unwraps an optional value 'lhs.?'287 /// ?T => T with safety.
282 unwrap_optional_safe,288 /// Given an optional value, returns the payload value, with a safety check that
283 /// Same as previous, but without safety checks. Used for orelse, if and while289 /// the value is non-null. Used for `orelse`, `if` and `while`.
284 unwrap_optional_unsafe,290 optional_payload_safe,
285 /// Gets the payload of an error union291 /// ?T => T without safety.
286 unwrap_err_safe,292 /// Given an optional value, returns the payload value. No safety checks.
287 /// Same as previous, but without safety checks. Used for orelse, if and while293 optional_payload_unsafe,
288 unwrap_err_unsafe,294 /// *?T => *T with safety.
289 /// Gets the error code value of an error union295 /// Given a pointer to an optional value, returns a pointer to the payload value,
290 unwrap_err_code,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 /// Takes a *E!T and raises a compiler error if T != void323 /// Takes a *E!T and raises a compiler error if T != void
292 ensure_err_payload_void,324 ensure_err_payload_void,
293 /// Create a enum literal,325 /// Create a enum literal,
...@@ -320,9 +352,12 @@ pub const Inst = struct {...@@ -320,9 +352,12 @@ pub const Inst = struct {
320 .compileerror,352 .compileerror,
321 .deref,353 .deref,
322 .@"return",354 .@"return",
323 .isnull,355 .is_null,
324 .isnonnull,356 .is_non_null,
325 .iserr,357 .is_null_ptr,
358 .is_non_null_ptr,
359 .is_err,
360 .is_err_ptr,
326 .ptrtoint,361 .ptrtoint,
327 .ensure_result_used,362 .ensure_result_used,
328 .ensure_result_non_error,363 .ensure_result_non_error,
...@@ -341,11 +376,16 @@ pub const Inst = struct {...@@ -341,11 +376,16 @@ pub const Inst = struct {
341 .mut_slice_type,376 .mut_slice_type,
342 .const_slice_type,377 .const_slice_type,
343 .optional_type,378 .optional_type,
344 .unwrap_optional_safe,379 .optional_payload_safe,
345 .unwrap_optional_unsafe,380 .optional_payload_unsafe,
346 .unwrap_err_safe,381 .optional_payload_safe_ptr,
347 .unwrap_err_unsafe,382 .optional_payload_unsafe_ptr,
348 .unwrap_err_code,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 .ensure_err_payload_void,389 .ensure_err_payload_void,
350 .anyframe_type,390 .anyframe_type,
351 .bitnot,391 .bitnot,
...@@ -495,9 +535,12 @@ pub const Inst = struct {...@@ -495,9 +535,12 @@ pub const Inst = struct {
495 .int,535 .int,
496 .intcast,536 .intcast,
497 .inttype,537 .inttype,
498 .isnonnull,538 .is_non_null,
499 .isnull,539 .is_null,
500 .iserr,540 .is_non_null_ptr,
541 .is_null_ptr,
542 .is_err,
543 .is_err_ptr,
501 .mod_rem,544 .mod_rem,
502 .mul,545 .mul,
503 .mulwrap,546 .mulwrap,
...@@ -525,11 +568,16 @@ pub const Inst = struct {...@@ -525,11 +568,16 @@ pub const Inst = struct {
525 .typeof,568 .typeof,
526 .xor,569 .xor,
527 .optional_type,570 .optional_type,
528 .unwrap_optional_safe,571 .optional_payload_safe,
529 .unwrap_optional_unsafe,572 .optional_payload_unsafe,
530 .unwrap_err_safe,573 .optional_payload_safe_ptr,
531 .unwrap_err_unsafe,574 .optional_payload_unsafe_ptr,
532 .unwrap_err_code,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 .ptr_type,581 .ptr_type,
534 .ensure_err_payload_void,582 .ensure_err_payload_void,
535 .enum_literal,583 .enum_literal,
...@@ -1540,14 +1588,18 @@ const DumpTzir = struct {...@@ -1540,14 +1588,18 @@ const DumpTzir = struct {
1540 .ret,1588 .ret,
1541 .bitcast,1589 .bitcast,
1542 .not,1590 .not,
1543 .isnonnull,1591 .is_non_null,
1544 .isnull,1592 .is_non_null_ptr,
1545 .iserr,1593 .is_null,
1594 .is_null_ptr,
1595 .is_err,
1596 .is_err_ptr,
1546 .ptrtoint,1597 .ptrtoint,
1547 .floatcast,1598 .floatcast,
1548 .intcast,1599 .intcast,
1549 .load,1600 .load,
1550 .unwrap_optional,1601 .optional_payload,
1602 .optional_payload_ptr,
1551 .wrap_optional,1603 .wrap_optional,
1552 => {1604 => {
1553 const un_op = inst.cast(ir.Inst.UnOp).?;1605 const un_op = inst.cast(ir.Inst.UnOp).?;
...@@ -1637,14 +1689,18 @@ const DumpTzir = struct {...@@ -1637,14 +1689,18 @@ const DumpTzir = struct {
1637 .ret,1689 .ret,
1638 .bitcast,1690 .bitcast,
1639 .not,1691 .not,
1640 .isnonnull,1692 .is_non_null,
1641 .isnull,1693 .is_null,
1642 .iserr,1694 .is_non_null_ptr,
1695 .is_null_ptr,
1696 .is_err,
1697 .is_err_ptr,
1643 .ptrtoint,1698 .ptrtoint,
1644 .floatcast,1699 .floatcast,
1645 .intcast,1700 .intcast,
1646 .load,1701 .load,
1647 .unwrap_optional,1702 .optional_payload,
1703 .optional_payload_ptr,
1648 .wrap_optional,1704 .wrap_optional,
1649 => {1705 => {
1650 const un_op = inst.cast(ir.Inst.UnOp).?;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,18 +127,26 @@ pub fn analyzeInst(mod: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!
127 .cmp_gt => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_gt).?, .gt),127 .cmp_gt => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_gt).?, .gt),
128 .cmp_neq => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_neq).?, .neq),128 .cmp_neq => return analyzeInstCmp(mod, scope, old_inst.castTag(.cmp_neq).?, .neq),
129 .condbr => return analyzeInstCondBr(mod, scope, old_inst.castTag(.condbr).?),129 .condbr => return analyzeInstCondBr(mod, scope, old_inst.castTag(.condbr).?),
130 .isnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnull).?, true),130 .is_null => return isNull(mod, scope, old_inst.castTag(.is_null).?, false),
131 .isnonnull => return analyzeInstIsNonNull(mod, scope, old_inst.castTag(.isnonnull).?, false),131 .is_non_null => return isNull(mod, scope, old_inst.castTag(.is_non_null).?, true),
132 .iserr => return analyzeInstIsErr(mod, scope, old_inst.castTag(.iserr).?),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 .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?),136 .boolnot => return analyzeInstBoolNot(mod, scope, old_inst.castTag(.boolnot).?),
134 .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?),137 .typeof => return analyzeInstTypeOf(mod, scope, old_inst.castTag(.typeof).?),
135 .typeof_peer => return analyzeInstTypeOfPeer(mod, scope, old_inst.castTag(.typeof_peer).?),138 .typeof_peer => return analyzeInstTypeOfPeer(mod, scope, old_inst.castTag(.typeof_peer).?),
136 .optional_type => return analyzeInstOptionalType(mod, scope, old_inst.castTag(.optional_type).?),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),140 .optional_payload_safe => return optionalPayload(mod, scope, old_inst.castTag(.optional_payload_safe).?, true),
138 .unwrap_optional_unsafe => return analyzeInstUnwrapOptional(mod, scope, old_inst.castTag(.unwrap_optional_unsafe).?, false),141 .optional_payload_unsafe => return optionalPayload(mod, scope, old_inst.castTag(.optional_payload_unsafe).?, false),
139 .unwrap_err_safe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_safe).?, true),142 .optional_payload_safe_ptr => return optionalPayloadPtr(mod, scope, old_inst.castTag(.optional_payload_safe_ptr).?, true),
140 .unwrap_err_unsafe => return analyzeInstUnwrapErr(mod, scope, old_inst.castTag(.unwrap_err_unsafe).?, false),143 .optional_payload_unsafe_ptr => return optionalPayloadPtr(mod, scope, old_inst.castTag(.optional_payload_unsafe_ptr).?, false),
141 .unwrap_err_code => return analyzeInstUnwrapErrCode(mod, scope, old_inst.castTag(.unwrap_err_code).?),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 .ensure_err_payload_void => return analyzeInstEnsureErrPayloadVoid(mod, scope, old_inst.castTag(.ensure_err_payload_void).?),150 .ensure_err_payload_void => return analyzeInstEnsureErrPayloadVoid(mod, scope, old_inst.castTag(.ensure_err_payload_void).?),
143 .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?),151 .array_type => return analyzeInstArrayType(mod, scope, old_inst.castTag(.array_type).?),
144 .array_type_sentinel => return analyzeInstArrayTypeSentinel(mod, scope, old_inst.castTag(.array_type_sentinel).?),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,48 +1112,109 @@ fn analyzeInstEnumLiteral(mod: *Module, scope: *Scope, inst: *zir.Inst.EnumLiter
1104 });1112 });
1105}1113}
11061114
1107fn analyzeInstUnwrapOptional(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst {1115/// Pointer in, pointer out.
1116fn optionalPayloadPtr(
1117 mod: *Module,
1118 scope: *Scope,
1119 unwrap: *zir.Inst.UnOp,
1120 safety_check: bool,
1121) InnerError!*Inst {
1108 const tracy = trace(@src());1122 const tracy = trace(@src());
1109 defer tracy.end();1123 defer tracy.end();
1110 const operand = try resolveInst(mod, scope, unwrap.positionals.operand);
1111 assert(operand.ty.zigTypeTag() == .Pointer);
11121124
1113 const elem_type = operand.ty.elemType();1125 const optional_ptr = try resolveInst(mod, scope, unwrap.positionals.operand);
1114 if (elem_type.zigTypeTag() != .Optional) {1126 assert(optional_ptr.ty.zigTypeTag() == .Pointer);
1115 return mod.fail(scope, unwrap.base.src, "expected optional type, found {}", .{elem_type});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 }
11171132
1118 const child_type = try elem_type.optionalChildAlloc(scope.arena());1133 const child_type = try opt_type.optionalChildAlloc(scope.arena());
1119 const child_pointer = try mod.simplePtrType(scope, unwrap.base.src, child_type, operand.ty.isConstPtr(), .One);1134 const child_pointer = try mod.simplePtrType(scope, unwrap.base.src, child_type, !optional_ptr.ty.isConstPtr(), .One);
11201135
1121 if (operand.value()) |val| {1136 if (optional_ptr.value()) |pointer_val| {
1137 const val = try pointer_val.pointerDeref(scope.arena());
1122 if (val.isNull()) {1138 if (val.isNull()) {
1123 return mod.fail(scope, unwrap.base.src, "unable to unwrap null", .{});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 return mod.constInst(scope, unwrap.base.src, .{1142 return mod.constInst(scope, unwrap.base.src, .{
1126 .ty = child_pointer,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.
1157fn 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 .val = val,1180 .val = val,
1128 });1181 });
1129 }1182 }
11301183
1131 const b = try mod.requireRuntimeBlock(scope, unwrap.base.src);1184 const b = try mod.requireRuntimeBlock(scope, unwrap.base.src);
1132 if (safety_check and mod.wantSafety(scope)) {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 try mod.addSafetyCheck(b, is_non_null, .unwrap_null);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}
11381191
1139fn analyzeInstUnwrapErr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst {1192/// Value in, value out
1193fn errorUnionPayload(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst {
1140 const tracy = trace(@src());1194 const tracy = trace(@src());
1141 defer tracy.end();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}
11441198
1145fn analyzeInstUnwrapErrCode(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst {1199/// Pointer in, pointer out
1200fn errorUnionPayloadPtr(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp, safety_check: bool) InnerError!*Inst {
1146 const tracy = trace(@src());1201 const tracy = trace(@src());
1147 defer tracy.end();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
1207fn 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
1214fn 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}
11501219
1151fn analyzeInstEnsureErrPayloadVoid(mod: *Module, scope: *Scope, unwrap: *zir.Inst.UnOp) InnerError!*Inst {1220fn 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,20 +2143,36 @@ fn analyzeInstBoolOp(mod: *Module, scope: *Scope, inst: *zir.Inst.BinOp) InnerEr
2074 return mod.addBinOp(b, inst.base.src, bool_type, if (is_bool_or) .boolor else .booland, lhs, rhs);2143 return mod.addBinOp(b, inst.base.src, bool_type, if (is_bool_or) .boolor else .booland, lhs, rhs);
2075}2144}
20762145
2077fn analyzeInstIsNonNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst {2146fn isNull(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp, invert_logic: bool) InnerError!*Inst {
2078 const tracy = trace(@src());2147 const tracy = trace(@src());
2079 defer tracy.end();2148 defer tracy.end();
2080 const operand = try resolveInst(mod, scope, inst.positionals.operand);2149 const operand = try resolveInst(mod, scope, inst.positionals.operand);
2081 return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic);2150 return mod.analyzeIsNull(scope, inst.base.src, operand, invert_logic);
2082}2151}
20832152
2084fn analyzeInstIsErr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {2153fn 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
2161fn isErr(mod: *Module, scope: *Scope, inst: *zir.Inst.UnOp) InnerError!*Inst {
2085 const tracy = trace(@src());2162 const tracy = trace(@src());
2086 defer tracy.end();2163 defer tracy.end();
2087 const operand = try resolveInst(mod, scope, inst.positionals.operand);2164 const operand = try resolveInst(mod, scope, inst.positionals.operand);
2088 return mod.analyzeIsErr(scope, inst.base.src, operand);2165 return mod.analyzeIsErr(scope, inst.base.src, operand);
2089}2166}
20902167
2168fn 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
2091fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst {2176fn analyzeInstCondBr(mod: *Module, scope: *Scope, inst: *zir.Inst.CondBr) InnerError!*Inst {
2092 const tracy = trace(@src());2177 const tracy = trace(@src());
2093 defer tracy.end();2178 defer tracy.end();
test/stage2/test.zig+47
...@@ -1462,4 +1462,51 @@ pub fn addCases(ctx: *TestContext) !void {...@@ -1462,4 +1462,51 @@ pub fn addCases(ctx: *TestContext) !void {
1462 "",1462 "",
1463 );1463 );
1464 }1464 }
1465 {
1466 var case = ctx.exe("orelse at comptime", linux_x64);
1467 case.addCompareOutput(
1468 \\export fn _start() noreturn {
1469 \\ const i: ?u64 = 0;
1470 \\ const orelsed = i orelse 5;
1471 \\ assert(orelsed == 0);
1472 \\ exit();
1473 \\}
1474 \\fn assert(b: bool) void {
1475 \\ if (!b) unreachable;
1476 \\}
1477 \\fn exit() noreturn {
1478 \\ asm volatile ("syscall"
1479 \\ :
1480 \\ : [number] "{rax}" (231),
1481 \\ [arg1] "{rdi}" (0)
1482 \\ : "rcx", "r11", "memory"
1483 \\ );
1484 \\ unreachable;
1485 \\}
1486 ,
1487 "",
1488 );
1489 case.addCompareOutput(
1490 \\export fn _start() noreturn {
1491 \\ const i: ?u64 = null;
1492 \\ const orelsed = i orelse 5;
1493 \\ assert(orelsed == 5);
1494 \\ exit();
1495 \\}
1496 \\fn assert(b: bool) void {
1497 \\ if (!b) unreachable;
1498 \\}
1499 \\fn exit() noreturn {
1500 \\ asm volatile ("syscall"
1501 \\ :
1502 \\ : [number] "{rax}" (231),
1503 \\ [arg1] "{rdi}" (0)
1504 \\ : "rcx", "r11", "memory"
1505 \\ );
1506 \\ unreachable;
1507 \\}
1508 ,
1509 "",
1510 );
1511 }
1465}1512}