Commit 3cebaaad1c
Changed files (4)
test
stage2
src/AstGen.zig
@@ -1415,6 +1415,7 @@ fn varDecl(
const type_inst = try typeExpr(gz, &init_scope.base, var_decl.ast.type_node);
opt_type_inst = type_inst;
init_scope.rl_ptr = try init_scope.addUnNode(.alloc, type_inst, node);
+ init_scope.rl_ty_inst = type_inst;
} else {
const alloc = try init_scope.addUnNode(.alloc_inferred, undefined, node);
resolve_inferred_alloc = alloc;
@@ -1441,20 +1442,13 @@ fn varDecl(
parent_zir.appendAssumeCapacity(src_inst);
}
assert(parent_zir.items.len == expected_len);
- const casted_init = if (opt_type_inst != .none)
- try gz.addPlNode(.as_node, var_decl.ast.type_node, zir.Inst.As{
- .dest_type = opt_type_inst,
- .operand = init_inst,
- })
- else
- init_inst;
const sub_scope = try block_arena.create(Scope.LocalVal);
sub_scope.* = .{
.parent = scope,
.gen_zir = gz,
.name = ident_name,
- .inst = casted_init,
+ .inst = init_inst,
.src = name_src,
};
return &sub_scope.base;
@@ -3029,25 +3023,48 @@ fn switchExpr(
// all prongs, except for prongs that ended with a noreturn instruction.
// Elide all the `store_to_block_ptr` instructions.
+ // The break instructions need to have their operands coerced if the
+ // switch's result location is a `ty`. In this case we overwrite the
+ // `store_to_block_ptr` instruction with an `as` instruction and repurpose
+ // it as the break operand.
+
var extra_index: usize = 0;
extra_index += 2;
extra_index += @boolToInt(multi_cases_len != 0);
- if (special_prong != .none) {
+ if (special_prong != .none) special_prong: {
const body_len_index = extra_index;
const body_len = scalar_cases_payload.items[extra_index];
extra_index += 1;
+ if (body_len < 2) {
+ extra_index += body_len;
+ astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
+ break :special_prong;
+ }
extra_index += body_len - 2;
const store_inst = scalar_cases_payload.items[extra_index];
- if (zir_tags[store_inst] == .store_to_block_ptr) {
- assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
- scalar_cases_payload.items[body_len_index] -= 1;
+ if (zir_tags[store_inst] != .store_to_block_ptr) {
+ extra_index += 2;
astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
+ break :special_prong;
+ }
+ assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
+ if (block_scope.rl_ty_inst != .none) {
extra_index += 1;
- astgen.extra.appendAssumeCapacity(scalar_cases_payload.items[extra_index]);
+ const break_inst = scalar_cases_payload.items[extra_index];
extra_index += 1;
+ astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
+ zir_tags[store_inst] = .as;
+ zir_datas[store_inst].bin = .{
+ .lhs = block_scope.rl_ty_inst,
+ .rhs = zir_datas[break_inst].@"break".operand,
+ };
+ zir_datas[break_inst].@"break".operand = astgen.indexToRef(store_inst);
} else {
- extra_index += 2;
+ scalar_cases_payload.items[body_len_index] -= 1;
astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
+ extra_index += 1;
+ astgen.extra.appendAssumeCapacity(scalar_cases_payload.items[extra_index]);
+ extra_index += 1;
}
} else {
astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
@@ -3066,16 +3083,29 @@ fn switchExpr(
}
extra_index += body_len - 2;
const store_inst = scalar_cases_payload.items[extra_index];
- if (zir_tags[store_inst] == .store_to_block_ptr) {
+ if (zir_tags[store_inst] != .store_to_block_ptr) {
+ extra_index += 2;
+ astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
+ continue;
+ }
+ if (block_scope.rl_ty_inst != .none) {
+ extra_index += 1;
+ const break_inst = scalar_cases_payload.items[extra_index];
+ extra_index += 1;
+ astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
+ zir_tags[store_inst] = .as;
+ zir_datas[store_inst].bin = .{
+ .lhs = block_scope.rl_ty_inst,
+ .rhs = zir_datas[break_inst].@"break".operand,
+ };
+ zir_datas[break_inst].@"break".operand = astgen.indexToRef(store_inst);
+ } else {
assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
scalar_cases_payload.items[body_len_index] -= 1;
astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
extra_index += 1;
astgen.extra.appendAssumeCapacity(scalar_cases_payload.items[extra_index]);
extra_index += 1;
- } else {
- extra_index += 2;
- astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
}
}
extra_index = 0;
@@ -3098,16 +3128,29 @@ fn switchExpr(
}
extra_index += body_len - 2;
const store_inst = multi_cases_payload.items[extra_index];
- if (zir_tags[store_inst] == .store_to_block_ptr) {
+ if (zir_tags[store_inst] != .store_to_block_ptr) {
+ extra_index += 2;
+ astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
+ continue;
+ }
+ if (block_scope.rl_ty_inst != .none) {
+ extra_index += 1;
+ const break_inst = multi_cases_payload.items[extra_index];
+ extra_index += 1;
+ astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
+ zir_tags[store_inst] = .as;
+ zir_datas[store_inst].bin = .{
+ .lhs = block_scope.rl_ty_inst,
+ .rhs = zir_datas[break_inst].@"break".operand,
+ };
+ zir_datas[break_inst].@"break".operand = astgen.indexToRef(store_inst);
+ } else {
assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
multi_cases_payload.items[body_len_index] -= 1;
astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
extra_index += 1;
astgen.extra.appendAssumeCapacity(multi_cases_payload.items[extra_index]);
extra_index += 1;
- } else {
- extra_index += 2;
- astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
}
}
src/Module.zig
@@ -942,6 +942,8 @@ pub const Scope = struct {
break_result_loc: AstGen.ResultLoc = undefined,
/// When a block has a pointer result location, here it is.
rl_ptr: zir.Inst.Ref = .none,
+ /// When a block has a type result location, here it is.
+ rl_ty_inst: zir.Inst.Ref = .none,
/// Keeps track of how many branches of a block did not actually
/// consume the result location. astgen uses this to figure out
/// whether to rely on break instructions or writing to the result
@@ -1001,7 +1003,11 @@ pub const Scope = struct {
// we emit ZIR for the block break instructions to have the result values,
// and then rvalue() on that to pass the value to the result location.
switch (parent_rl) {
- .discard, .none, .ty, .ptr, .ref => {
+ .ty => |ty_inst| {
+ gz.rl_ty_inst = ty_inst;
+ gz.break_result_loc = parent_rl;
+ },
+ .discard, .none, .ptr, .ref => {
gz.break_result_loc = parent_rl;
},
@@ -1016,6 +1022,7 @@ pub const Scope = struct {
},
.block_ptr => |parent_block_scope| {
+ gz.rl_ty_inst = parent_block_scope.rl_ty_inst;
gz.rl_ptr = parent_block_scope.rl_ptr;
gz.break_result_loc = .{ .block_ptr = gz };
},
src/Sema.zig
@@ -4104,102 +4104,108 @@ fn coerce(
}
assert(inst.ty.zigTypeTag() != .Undefined);
- // null to ?T
- if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) {
- return sema.mod.constInst(sema.arena, inst_src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
- }
-
- // T to ?T
- if (dest_type.zigTypeTag() == .Optional) {
- var buf: Type.Payload.ElemType = undefined;
- const child_type = dest_type.optionalChild(&buf);
- if (child_type.eql(inst.ty)) {
- return sema.wrapOptional(block, dest_type, inst);
- } else if (try sema.coerceNum(block, child_type, inst)) |some| {
- return sema.wrapOptional(block, dest_type, some);
- }
- }
-
// T to E!T or E to E!T
if (dest_type.tag() == .error_union) {
return try sema.wrapErrorUnion(block, dest_type, inst);
}
- // Coercions where the source is a single pointer to an array.
- src_array_ptr: {
- if (!inst.ty.isSinglePointer()) break :src_array_ptr;
- const array_type = inst.ty.elemType();
- if (array_type.zigTypeTag() != .Array) break :src_array_ptr;
- const array_elem_type = array_type.elemType();
- if (inst.ty.isConstPtr() and !dest_type.isConstPtr()) break :src_array_ptr;
- if (inst.ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr;
-
- const dst_elem_type = dest_type.elemType();
- switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) {
- .ok => {},
- .no_match => break :src_array_ptr,
- }
-
- switch (dest_type.ptrSize()) {
- .Slice => {
- // *[N]T to []T
- return sema.coerceArrayPtrToSlice(block, dest_type, inst);
- },
- .C => {
- // *[N]T to [*c]T
- return sema.coerceArrayPtrToMany(block, dest_type, inst);
- },
- .Many => {
- // *[N]T to [*]T
- // *[N:s]T to [*:s]T
- const src_sentinel = array_type.sentinel();
- const dst_sentinel = dest_type.sentinel();
- if (src_sentinel == null and dst_sentinel == null)
- return sema.coerceArrayPtrToMany(block, dest_type, inst);
-
- if (src_sentinel) |src_s| {
- if (dst_sentinel) |dst_s| {
- if (src_s.eql(dst_s)) {
- return sema.coerceArrayPtrToMany(block, dest_type, inst);
- }
- }
- }
- },
- .One => {},
- }
- }
-
// comptime known number to other number
if (try sema.coerceNum(block, dest_type, inst)) |some|
return some;
const target = sema.mod.getTarget();
- // integer widening
- if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {
- assert(inst.value() == null); // handled above
+ switch (dest_type.zigTypeTag()) {
+ .Optional => {
+ // null to ?T
+ if (inst.ty.zigTypeTag() == .Null) {
+ return sema.mod.constInst(sema.arena, inst_src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
+ }
- const src_info = inst.ty.intInfo(target);
- const dst_info = dest_type.intInfo(target);
- if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
- // small enough unsigned ints can get casted to large enough signed ints
- (src_info.signedness == .signed and dst_info.signedness == .unsigned and dst_info.bits > src_info.bits))
- {
- try sema.requireRuntimeBlock(block, inst_src);
- return block.addUnOp(inst_src, dest_type, .intcast, inst);
- }
- }
+ // T to ?T
+ var buf: Type.Payload.ElemType = undefined;
+ const child_type = dest_type.optionalChild(&buf);
+ if (child_type.eql(inst.ty)) {
+ return sema.wrapOptional(block, dest_type, inst);
+ } else if (try sema.coerceNum(block, child_type, inst)) |some| {
+ return sema.wrapOptional(block, dest_type, some);
+ }
+ },
+ .Pointer => {
+ // Coercions where the source is a single pointer to an array.
+ src_array_ptr: {
+ if (!inst.ty.isSinglePointer()) break :src_array_ptr;
+ const array_type = inst.ty.elemType();
+ if (array_type.zigTypeTag() != .Array) break :src_array_ptr;
+ const array_elem_type = array_type.elemType();
+ if (inst.ty.isConstPtr() and !dest_type.isConstPtr()) break :src_array_ptr;
+ if (inst.ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr;
+
+ const dst_elem_type = dest_type.elemType();
+ switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) {
+ .ok => {},
+ .no_match => break :src_array_ptr,
+ }
- // float widening
- if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) {
- assert(inst.value() == null); // handled above
+ switch (dest_type.ptrSize()) {
+ .Slice => {
+ // *[N]T to []T
+ return sema.coerceArrayPtrToSlice(block, dest_type, inst);
+ },
+ .C => {
+ // *[N]T to [*c]T
+ return sema.coerceArrayPtrToMany(block, dest_type, inst);
+ },
+ .Many => {
+ // *[N]T to [*]T
+ // *[N:s]T to [*:s]T
+ const src_sentinel = array_type.sentinel();
+ const dst_sentinel = dest_type.sentinel();
+ if (src_sentinel == null and dst_sentinel == null)
+ return sema.coerceArrayPtrToMany(block, dest_type, inst);
- const src_bits = inst.ty.floatBits(target);
- const dst_bits = dest_type.floatBits(target);
- if (dst_bits >= src_bits) {
- try sema.requireRuntimeBlock(block, inst_src);
- return block.addUnOp(inst_src, dest_type, .floatcast, inst);
- }
+ if (src_sentinel) |src_s| {
+ if (dst_sentinel) |dst_s| {
+ if (src_s.eql(dst_s)) {
+ return sema.coerceArrayPtrToMany(block, dest_type, inst);
+ }
+ }
+ }
+ },
+ .One => {},
+ }
+ }
+ },
+ .Int => {
+ // integer widening
+ if (inst.ty.zigTypeTag() == .Int) {
+ assert(inst.value() == null); // handled above
+
+ const dst_info = dest_type.intInfo(target);
+ const src_info = inst.ty.intInfo(target);
+ if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
+ // small enough unsigned ints can get casted to large enough signed ints
+ (src_info.signedness == .signed and dst_info.signedness == .unsigned and dst_info.bits > src_info.bits))
+ {
+ try sema.requireRuntimeBlock(block, inst_src);
+ return block.addUnOp(inst_src, dest_type, .intcast, inst);
+ }
+ }
+ },
+ .Float => {
+ // float widening
+ if (inst.ty.zigTypeTag() == .Float) {
+ assert(inst.value() == null); // handled above
+
+ const src_bits = inst.ty.floatBits(target);
+ const dst_bits = dest_type.floatBits(target);
+ if (dst_bits >= src_bits) {
+ try sema.requireRuntimeBlock(block, inst_src);
+ return block.addUnOp(inst_src, dest_type, .floatcast, inst);
+ }
+ }
+ },
+ else => {},
}
return sema.mod.fail(&block.base, inst_src, "expected {}, found {}", .{ dest_type, inst.ty });
test/stage2/cbe.zig
@@ -279,6 +279,72 @@ pub fn addCases(ctx: *TestContext) !void {
\\ return a - 4;
\\}
, "");
+
+ // Switch expression missing else case.
+ case.addError(
+ \\export fn main() c_int {
+ \\ var cond: c_int = 0;
+ \\ const a: c_int = switch (cond) {
+ \\ 1 => 1,
+ \\ 2 => 2,
+ \\ 3 => 3,
+ \\ 4 => 4,
+ \\ };
+ \\ return a - 4;
+ \\}
+ , &.{":3:22: error: switch must handle all possibilities"});
+
+ // Switch expression, has an unreachable prong.
+ case.addCompareOutput(
+ \\export fn main() c_int {
+ \\ var cond: c_int = 0;
+ \\ const a: c_int = switch (cond) {
+ \\ 1 => 1,
+ \\ 2 => 2,
+ \\ 99...300, 12 => 3,
+ \\ 0 => 4,
+ \\ 13 => unreachable,
+ \\ else => 5,
+ \\ };
+ \\ return a - 4;
+ \\}
+ , "");
+
+ // Switch expression, has an unreachable prong and prongs write
+ // to result locations.
+ case.addCompareOutput(
+ \\export fn main() c_int {
+ \\ var cond: c_int = 0;
+ \\ var a: c_int = switch (cond) {
+ \\ 1 => 1,
+ \\ 2 => 2,
+ \\ 99...300, 12 => 3,
+ \\ 0 => 4,
+ \\ 13 => unreachable,
+ \\ else => 5,
+ \\ };
+ \\ return a - 4;
+ \\}
+ , "");
+
+ // Switch expression has duplicate case value.
+ case.addError(
+ \\export fn main() c_int {
+ \\ var cond: c_int = 0;
+ \\ const a: c_int = switch (cond) {
+ \\ 1 => 1,
+ \\ 2 => 2,
+ \\ 96, 11...13, 97 => 3,
+ \\ 0 => 4,
+ \\ 90, 12 => 100,
+ \\ else => 5,
+ \\ };
+ \\ return a - 4;
+ \\}
+ , &.{
+ ":8:13: error: duplicate switch value",
+ ":6:15: note: previous value here",
+ });
}
//{
// var case = ctx.exeFromCompiledC("optionals", .{});