aboutsummaryrefslogtreecommitdiff
path: root/src/Sema.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2021-03-31 18:05:37 -0700
committerAndrew Kelley <andrew@ziglang.org>2021-03-31 18:05:37 -0700
commit3cebaaad1ca16a9e0203ed8c1684d0ce72da9487 (patch)
treea153e50f75fad0a1b6c683ad5c06e28e102ff848 /src/Sema.zig
parent08eedc962d8e2582db8fb5b4a50114f2913519fd (diff)
downloadzig-3cebaaad1ca16a9e0203ed8c1684d0ce72da9487.tar.gz
zig-3cebaaad1ca16a9e0203ed8c1684d0ce72da9487.zip
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.
Diffstat (limited to 'src/Sema.zig')
-rw-r--r--src/Sema.zig170
1 files changed, 88 insertions, 82 deletions
diff --git a/src/Sema.zig b/src/Sema.zig
index 5ea1836fbe..504edfe97b 100644
--- a/src/Sema.zig
+++ b/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 });