authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-31 18:05:37-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-03-31 18:05:37-07:00
log3cebaaad1ca16a9e0203ed8c1684d0ce72da9487
treea153e50f75fad0a1b6c683ad5c06e28e102ff848
parent08eedc962d8e2582db8fb5b4a50114f2913519fd

astgen: improved handling of coercion

GenZir struct now has rl_ty_inst field which tracks the result location type (if any) a block expects all of its results to be coerced to. Remove a redundant coercion on const local initialization with a specified type. Switch expressions, during elision of store_to_block_ptr instructions, now re-purpose them to be type coercion when the block has a type in the result location.

4 files changed, 227 insertions(+), 105 deletions(-)

src/AstGen.zig+65-22
......@@ -1415,6 +1415,7 @@ fn varDecl(
14151415 const type_inst = try typeExpr(gz, &init_scope.base, var_decl.ast.type_node);
14161416 opt_type_inst = type_inst;
14171417 init_scope.rl_ptr = try init_scope.addUnNode(.alloc, type_inst, node);
1418 init_scope.rl_ty_inst = type_inst;
14181419 } else {
14191420 const alloc = try init_scope.addUnNode(.alloc_inferred, undefined, node);
14201421 resolve_inferred_alloc = alloc;
......@@ -1441,20 +1442,13 @@ fn varDecl(
14411442 parent_zir.appendAssumeCapacity(src_inst);
14421443 }
14431444 assert(parent_zir.items.len == expected_len);
1444 const casted_init = if (opt_type_inst != .none)
1445 try gz.addPlNode(.as_node, var_decl.ast.type_node, zir.Inst.As{
1446 .dest_type = opt_type_inst,
1447 .operand = init_inst,
1448 })
1449 else
1450 init_inst;
14511445
14521446 const sub_scope = try block_arena.create(Scope.LocalVal);
14531447 sub_scope.* = .{
14541448 .parent = scope,
14551449 .gen_zir = gz,
14561450 .name = ident_name,
1457 .inst = casted_init,
1451 .inst = init_inst,
14581452 .src = name_src,
14591453 };
14601454 return &sub_scope.base;
......@@ -3029,25 +3023,48 @@ fn switchExpr(
30293023 // all prongs, except for prongs that ended with a noreturn instruction.
30303024 // Elide all the `store_to_block_ptr` instructions.
30313025
3026 // The break instructions need to have their operands coerced if the
3027 // switch's result location is a `ty`. In this case we overwrite the
3028 // `store_to_block_ptr` instruction with an `as` instruction and repurpose
3029 // it as the break operand.
3030
30323031 var extra_index: usize = 0;
30333032 extra_index += 2;
30343033 extra_index += @boolToInt(multi_cases_len != 0);
3035 if (special_prong != .none) {
3034 if (special_prong != .none) special_prong: {
30363035 const body_len_index = extra_index;
30373036 const body_len = scalar_cases_payload.items[extra_index];
30383037 extra_index += 1;
3038 if (body_len < 2) {
3039 extra_index += body_len;
3040 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
3041 break :special_prong;
3042 }
30393043 extra_index += body_len - 2;
30403044 const store_inst = scalar_cases_payload.items[extra_index];
3041 if (zir_tags[store_inst] == .store_to_block_ptr) {
3042 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
3043 scalar_cases_payload.items[body_len_index] -= 1;
3045 if (zir_tags[store_inst] != .store_to_block_ptr) {
3046 extra_index += 2;
30443047 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
3048 break :special_prong;
3049 }
3050 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
3051 if (block_scope.rl_ty_inst != .none) {
30453052 extra_index += 1;
3046 astgen.extra.appendAssumeCapacity(scalar_cases_payload.items[extra_index]);
3053 const break_inst = scalar_cases_payload.items[extra_index];
30473054 extra_index += 1;
3055 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
3056 zir_tags[store_inst] = .as;
3057 zir_datas[store_inst].bin = .{
3058 .lhs = block_scope.rl_ty_inst,
3059 .rhs = zir_datas[break_inst].@"break".operand,
3060 };
3061 zir_datas[break_inst].@"break".operand = astgen.indexToRef(store_inst);
30483062 } else {
3049 extra_index += 2;
3063 scalar_cases_payload.items[body_len_index] -= 1;
30503064 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
3065 extra_index += 1;
3066 astgen.extra.appendAssumeCapacity(scalar_cases_payload.items[extra_index]);
3067 extra_index += 1;
30513068 }
30523069 } else {
30533070 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[0..extra_index]);
......@@ -3066,16 +3083,29 @@ fn switchExpr(
30663083 }
30673084 extra_index += body_len - 2;
30683085 const store_inst = scalar_cases_payload.items[extra_index];
3069 if (zir_tags[store_inst] == .store_to_block_ptr) {
3086 if (zir_tags[store_inst] != .store_to_block_ptr) {
3087 extra_index += 2;
3088 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
3089 continue;
3090 }
3091 if (block_scope.rl_ty_inst != .none) {
3092 extra_index += 1;
3093 const break_inst = scalar_cases_payload.items[extra_index];
3094 extra_index += 1;
3095 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
3096 zir_tags[store_inst] = .as;
3097 zir_datas[store_inst].bin = .{
3098 .lhs = block_scope.rl_ty_inst,
3099 .rhs = zir_datas[break_inst].@"break".operand,
3100 };
3101 zir_datas[break_inst].@"break".operand = astgen.indexToRef(store_inst);
3102 } else {
30703103 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
30713104 scalar_cases_payload.items[body_len_index] -= 1;
30723105 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
30733106 extra_index += 1;
30743107 astgen.extra.appendAssumeCapacity(scalar_cases_payload.items[extra_index]);
30753108 extra_index += 1;
3076 } else {
3077 extra_index += 2;
3078 astgen.extra.appendSliceAssumeCapacity(scalar_cases_payload.items[start_index..extra_index]);
30793109 }
30803110 }
30813111 extra_index = 0;
......@@ -3098,16 +3128,29 @@ fn switchExpr(
30983128 }
30993129 extra_index += body_len - 2;
31003130 const store_inst = multi_cases_payload.items[extra_index];
3101 if (zir_tags[store_inst] == .store_to_block_ptr) {
3131 if (zir_tags[store_inst] != .store_to_block_ptr) {
3132 extra_index += 2;
3133 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
3134 continue;
3135 }
3136 if (block_scope.rl_ty_inst != .none) {
3137 extra_index += 1;
3138 const break_inst = multi_cases_payload.items[extra_index];
3139 extra_index += 1;
3140 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
3141 zir_tags[store_inst] = .as;
3142 zir_datas[store_inst].bin = .{
3143 .lhs = block_scope.rl_ty_inst,
3144 .rhs = zir_datas[break_inst].@"break".operand,
3145 };
3146 zir_datas[break_inst].@"break".operand = astgen.indexToRef(store_inst);
3147 } else {
31023148 assert(zir_datas[store_inst].bin.lhs == block_scope.rl_ptr);
31033149 multi_cases_payload.items[body_len_index] -= 1;
31043150 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
31053151 extra_index += 1;
31063152 astgen.extra.appendAssumeCapacity(multi_cases_payload.items[extra_index]);
31073153 extra_index += 1;
3108 } else {
3109 extra_index += 2;
3110 astgen.extra.appendSliceAssumeCapacity(multi_cases_payload.items[start_index..extra_index]);
31113154 }
31123155 }
31133156
src/Module.zig+8-1
......@@ -942,6 +942,8 @@ pub const Scope = struct {
942942 break_result_loc: AstGen.ResultLoc = undefined,
943943 /// When a block has a pointer result location, here it is.
944944 rl_ptr: zir.Inst.Ref = .none,
945 /// When a block has a type result location, here it is.
946 rl_ty_inst: zir.Inst.Ref = .none,
945947 /// Keeps track of how many branches of a block did not actually
946948 /// consume the result location. astgen uses this to figure out
947949 /// whether to rely on break instructions or writing to the result
......@@ -1001,7 +1003,11 @@ pub const Scope = struct {
10011003 // we emit ZIR for the block break instructions to have the result values,
10021004 // and then rvalue() on that to pass the value to the result location.
10031005 switch (parent_rl) {
1004 .discard, .none, .ty, .ptr, .ref => {
1006 .ty => |ty_inst| {
1007 gz.rl_ty_inst = ty_inst;
1008 gz.break_result_loc = parent_rl;
1009 },
1010 .discard, .none, .ptr, .ref => {
10051011 gz.break_result_loc = parent_rl;
10061012 },
10071013
......@@ -1016,6 +1022,7 @@ pub const Scope = struct {
10161022 },
10171023
10181024 .block_ptr => |parent_block_scope| {
1025 gz.rl_ty_inst = parent_block_scope.rl_ty_inst;
10191026 gz.rl_ptr = parent_block_scope.rl_ptr;
10201027 gz.break_result_loc = .{ .block_ptr = gz };
10211028 },
src/Sema.zig+88-82
......@@ -4104,102 +4104,108 @@ fn coerce(
41044104 }
41054105 assert(inst.ty.zigTypeTag() != .Undefined);
41064106
4107 // null to ?T
4108 if (dest_type.zigTypeTag() == .Optional and inst.ty.zigTypeTag() == .Null) {
4109 return sema.mod.constInst(sema.arena, inst_src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
4110 }
4111
4112 // T to ?T
4113 if (dest_type.zigTypeTag() == .Optional) {
4114 var buf: Type.Payload.ElemType = undefined;
4115 const child_type = dest_type.optionalChild(&buf);
4116 if (child_type.eql(inst.ty)) {
4117 return sema.wrapOptional(block, dest_type, inst);
4118 } else if (try sema.coerceNum(block, child_type, inst)) |some| {
4119 return sema.wrapOptional(block, dest_type, some);
4120 }
4121 }
4122
41234107 // T to E!T or E to E!T
41244108 if (dest_type.tag() == .error_union) {
41254109 return try sema.wrapErrorUnion(block, dest_type, inst);
41264110 }
41274111
4128 // Coercions where the source is a single pointer to an array.
4129 src_array_ptr: {
4130 if (!inst.ty.isSinglePointer()) break :src_array_ptr;
4131 const array_type = inst.ty.elemType();
4132 if (array_type.zigTypeTag() != .Array) break :src_array_ptr;
4133 const array_elem_type = array_type.elemType();
4134 if (inst.ty.isConstPtr() and !dest_type.isConstPtr()) break :src_array_ptr;
4135 if (inst.ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr;
4136
4137 const dst_elem_type = dest_type.elemType();
4138 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) {
4139 .ok => {},
4140 .no_match => break :src_array_ptr,
4141 }
4142
4143 switch (dest_type.ptrSize()) {
4144 .Slice => {
4145 // *[N]T to []T
4146 return sema.coerceArrayPtrToSlice(block, dest_type, inst);
4147 },
4148 .C => {
4149 // *[N]T to [*c]T
4150 return sema.coerceArrayPtrToMany(block, dest_type, inst);
4151 },
4152 .Many => {
4153 // *[N]T to [*]T
4154 // *[N:s]T to [*:s]T
4155 const src_sentinel = array_type.sentinel();
4156 const dst_sentinel = dest_type.sentinel();
4157 if (src_sentinel == null and dst_sentinel == null)
4158 return sema.coerceArrayPtrToMany(block, dest_type, inst);
4159
4160 if (src_sentinel) |src_s| {
4161 if (dst_sentinel) |dst_s| {
4162 if (src_s.eql(dst_s)) {
4163 return sema.coerceArrayPtrToMany(block, dest_type, inst);
4164 }
4165 }
4166 }
4167 },
4168 .One => {},
4169 }
4170 }
4171
41724112 // comptime known number to other number
41734113 if (try sema.coerceNum(block, dest_type, inst)) |some|
41744114 return some;
41754115
41764116 const target = sema.mod.getTarget();
41774117
4178 // integer widening
4179 if (inst.ty.zigTypeTag() == .Int and dest_type.zigTypeTag() == .Int) {
4180 assert(inst.value() == null); // handled above
4118 switch (dest_type.zigTypeTag()) {
4119 .Optional => {
4120 // null to ?T
4121 if (inst.ty.zigTypeTag() == .Null) {
4122 return sema.mod.constInst(sema.arena, inst_src, .{ .ty = dest_type, .val = Value.initTag(.null_value) });
4123 }
41814124
4182 const src_info = inst.ty.intInfo(target);
4183 const dst_info = dest_type.intInfo(target);
4184 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
4185 // small enough unsigned ints can get casted to large enough signed ints
4186 (src_info.signedness == .signed and dst_info.signedness == .unsigned and dst_info.bits > src_info.bits))
4187 {
4188 try sema.requireRuntimeBlock(block, inst_src);
4189 return block.addUnOp(inst_src, dest_type, .intcast, inst);
4190 }
4191 }
4125 // T to ?T
4126 var buf: Type.Payload.ElemType = undefined;
4127 const child_type = dest_type.optionalChild(&buf);
4128 if (child_type.eql(inst.ty)) {
4129 return sema.wrapOptional(block, dest_type, inst);
4130 } else if (try sema.coerceNum(block, child_type, inst)) |some| {
4131 return sema.wrapOptional(block, dest_type, some);
4132 }
4133 },
4134 .Pointer => {
4135 // Coercions where the source is a single pointer to an array.
4136 src_array_ptr: {
4137 if (!inst.ty.isSinglePointer()) break :src_array_ptr;
4138 const array_type = inst.ty.elemType();
4139 if (array_type.zigTypeTag() != .Array) break :src_array_ptr;
4140 const array_elem_type = array_type.elemType();
4141 if (inst.ty.isConstPtr() and !dest_type.isConstPtr()) break :src_array_ptr;
4142 if (inst.ty.isVolatilePtr() and !dest_type.isVolatilePtr()) break :src_array_ptr;
4143
4144 const dst_elem_type = dest_type.elemType();
4145 switch (coerceInMemoryAllowed(dst_elem_type, array_elem_type)) {
4146 .ok => {},
4147 .no_match => break :src_array_ptr,
4148 }
41924149
4193 // float widening
4194 if (inst.ty.zigTypeTag() == .Float and dest_type.zigTypeTag() == .Float) {
4195 assert(inst.value() == null); // handled above
4150 switch (dest_type.ptrSize()) {
4151 .Slice => {
4152 // *[N]T to []T
4153 return sema.coerceArrayPtrToSlice(block, dest_type, inst);
4154 },
4155 .C => {
4156 // *[N]T to [*c]T
4157 return sema.coerceArrayPtrToMany(block, dest_type, inst);
4158 },
4159 .Many => {
4160 // *[N]T to [*]T
4161 // *[N:s]T to [*:s]T
4162 const src_sentinel = array_type.sentinel();
4163 const dst_sentinel = dest_type.sentinel();
4164 if (src_sentinel == null and dst_sentinel == null)
4165 return sema.coerceArrayPtrToMany(block, dest_type, inst);
41964166
4197 const src_bits = inst.ty.floatBits(target);
4198 const dst_bits = dest_type.floatBits(target);
4199 if (dst_bits >= src_bits) {
4200 try sema.requireRuntimeBlock(block, inst_src);
4201 return block.addUnOp(inst_src, dest_type, .floatcast, inst);
4202 }
4167 if (src_sentinel) |src_s| {
4168 if (dst_sentinel) |dst_s| {
4169 if (src_s.eql(dst_s)) {
4170 return sema.coerceArrayPtrToMany(block, dest_type, inst);
4171 }
4172 }
4173 }
4174 },
4175 .One => {},
4176 }
4177 }
4178 },
4179 .Int => {
4180 // integer widening
4181 if (inst.ty.zigTypeTag() == .Int) {
4182 assert(inst.value() == null); // handled above
4183
4184 const dst_info = dest_type.intInfo(target);
4185 const src_info = inst.ty.intInfo(target);
4186 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
4187 // small enough unsigned ints can get casted to large enough signed ints
4188 (src_info.signedness == .signed and dst_info.signedness == .unsigned and dst_info.bits > src_info.bits))
4189 {
4190 try sema.requireRuntimeBlock(block, inst_src);
4191 return block.addUnOp(inst_src, dest_type, .intcast, inst);
4192 }
4193 }
4194 },
4195 .Float => {
4196 // float widening
4197 if (inst.ty.zigTypeTag() == .Float) {
4198 assert(inst.value() == null); // handled above
4199
4200 const src_bits = inst.ty.floatBits(target);
4201 const dst_bits = dest_type.floatBits(target);
4202 if (dst_bits >= src_bits) {
4203 try sema.requireRuntimeBlock(block, inst_src);
4204 return block.addUnOp(inst_src, dest_type, .floatcast, inst);
4205 }
4206 }
4207 },
4208 else => {},
42034209 }
42044210
42054211 return sema.mod.fail(&block.base, inst_src, "expected {}, found {}", .{ dest_type, inst.ty });
test/stage2/cbe.zig+66
......@@ -279,6 +279,72 @@ pub fn addCases(ctx: *TestContext) !void {
279279 \\ return a - 4;
280280 \\}
281281 , "");
282
283 // Switch expression missing else case.
284 case.addError(
285 \\export fn main() c_int {
286 \\ var cond: c_int = 0;
287 \\ const a: c_int = switch (cond) {
288 \\ 1 => 1,
289 \\ 2 => 2,
290 \\ 3 => 3,
291 \\ 4 => 4,
292 \\ };
293 \\ return a - 4;
294 \\}
295 , &.{":3:22: error: switch must handle all possibilities"});
296
297 // Switch expression, has an unreachable prong.
298 case.addCompareOutput(
299 \\export fn main() c_int {
300 \\ var cond: c_int = 0;
301 \\ const a: c_int = switch (cond) {
302 \\ 1 => 1,
303 \\ 2 => 2,
304 \\ 99...300, 12 => 3,
305 \\ 0 => 4,
306 \\ 13 => unreachable,
307 \\ else => 5,
308 \\ };
309 \\ return a - 4;
310 \\}
311 , "");
312
313 // Switch expression, has an unreachable prong and prongs write
314 // to result locations.
315 case.addCompareOutput(
316 \\export fn main() c_int {
317 \\ var cond: c_int = 0;
318 \\ var a: c_int = switch (cond) {
319 \\ 1 => 1,
320 \\ 2 => 2,
321 \\ 99...300, 12 => 3,
322 \\ 0 => 4,
323 \\ 13 => unreachable,
324 \\ else => 5,
325 \\ };
326 \\ return a - 4;
327 \\}
328 , "");
329
330 // Switch expression has duplicate case value.
331 case.addError(
332 \\export fn main() c_int {
333 \\ var cond: c_int = 0;
334 \\ const a: c_int = switch (cond) {
335 \\ 1 => 1,
336 \\ 2 => 2,
337 \\ 96, 11...13, 97 => 3,
338 \\ 0 => 4,
339 \\ 90, 12 => 100,
340 \\ else => 5,
341 \\ };
342 \\ return a - 4;
343 \\}
344 , &.{
345 ":8:13: error: duplicate switch value",
346 ":6:15: note: previous value here",
347 });
282348 }
283349 //{
284350 // var case = ctx.exeFromCompiledC("optionals", .{});