authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-21 22:35:24-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-12-21 22:35:24-07:00
log6fdf7ce0af1c3ca210c3f49de4f833afee282076
tree2896a0322e9948123b2dfd5fa0550147b9755e17
parent5d6380b38d7c83055fe29eb7f8a19b1424276cd7

Sema: simplify coercion logic

Instead of a separate function, `coerceNum` for handling comptime-known number coercion, outside of the main switch, the `coerce` function now has a single big switch statement that decides the control flow based on the zig type tag.

1 files changed, 63 insertions(+), 90 deletions(-)

src/Sema.zig+63-90
...@@ -12447,12 +12447,6 @@ fn coerce(...@@ -12447,12 +12447,6 @@ fn coerce(
12447 }12447 }
12448 assert(inst_ty.zigTypeTag() != .Undefined);12448 assert(inst_ty.zigTypeTag() != .Undefined);
1244912449
12450 // comptime known number to other number
12451 // TODO why is this a separate function? should just be flattened into the
12452 // switch expression below.
12453 if (try sema.coerceNum(block, dest_ty, inst, inst_src)) |some|
12454 return some;
12455
12456 switch (dest_ty.zigTypeTag()) {12450 switch (dest_ty.zigTypeTag()) {
12457 .Optional => {12451 .Optional => {
12458 // null to ?T12452 // null to ?T
...@@ -12584,11 +12578,31 @@ fn coerce(...@@ -12584,11 +12578,31 @@ fn coerce(
12584 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);12578 return sema.coerceCompatiblePtrs(block, dest_ty, inst, inst_src);
12585 }12579 }
12586 },12580 },
12587 .Int => {12581 .Int, .ComptimeInt => switch (inst_ty.zigTypeTag()) {
12588 // integer widening12582 .Float, .ComptimeFloat => float: {
12589 if (inst_ty.zigTypeTag() == .Int) {12583 const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :float;
12590 assert(!(try sema.isComptimeKnown(block, inst_src, inst))); // handled above
1259112584
12585 if (val.floatHasFraction()) {
12586 return sema.fail(block, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val, dest_ty });
12587 }
12588 const result_val = val.floatToInt(sema.arena, dest_ty, target) catch |err| switch (err) {
12589 error.FloatCannotFit => {
12590 return sema.fail(block, inst_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty });
12591 },
12592 else => |e| return e,
12593 };
12594 return try sema.addConstant(dest_ty, result_val);
12595 },
12596 .Int, .ComptimeInt => {
12597 if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
12598 // comptime known integer to other number
12599 if (!val.intFitsInType(dest_ty, target)) {
12600 return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_ty, val });
12601 }
12602 return try sema.addConstant(dest_ty, val);
12603 }
12604
12605 // integer widening
12592 const dst_info = dest_ty.intInfo(target);12606 const dst_info = dest_ty.intInfo(target);
12593 const src_info = inst_ty.intInfo(target);12607 const src_info = inst_ty.intInfo(target);
12594 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or12608 if ((src_info.signedness == dst_info.signedness and dst_info.bits >= src_info.bits) or
...@@ -12598,20 +12612,53 @@ fn coerce(...@@ -12598,20 +12612,53 @@ fn coerce(
12598 try sema.requireRuntimeBlock(block, inst_src);12612 try sema.requireRuntimeBlock(block, inst_src);
12599 return block.addTyOp(.intcast, dest_ty, inst);12613 return block.addTyOp(.intcast, dest_ty, inst);
12600 }12614 }
12601 }12615 },
12616 else => {},
12602 },12617 },
12603 .Float => {12618 .Float, .ComptimeFloat => switch (inst_ty.zigTypeTag()) {
12604 // float widening12619 .ComptimeFloat => {
12605 if (inst_ty.zigTypeTag() == .Float) {12620 const val = try sema.resolveConstValue(block, inst_src, inst);
12606 assert(!(try sema.isComptimeKnown(block, inst_src, inst))); // handled above12621 const result_val = try val.floatCast(sema.arena, dest_ty);
12622 return try sema.addConstant(dest_ty, result_val);
12623 },
12624 .Float => {
12625 if (try sema.resolveDefinedValue(block, inst_src, inst)) |val| {
12626 const result_val = try val.floatCast(sema.arena, dest_ty);
12627 if (!val.eql(result_val, dest_ty)) {
12628 return sema.fail(
12629 block,
12630 inst_src,
12631 "type {} cannot represent float value {}",
12632 .{ dest_ty, val },
12633 );
12634 }
12635 return try sema.addConstant(dest_ty, result_val);
12636 }
1260712637
12638 // float widening
12608 const src_bits = inst_ty.floatBits(target);12639 const src_bits = inst_ty.floatBits(target);
12609 const dst_bits = dest_ty.floatBits(target);12640 const dst_bits = dest_ty.floatBits(target);
12610 if (dst_bits >= src_bits) {12641 if (dst_bits >= src_bits) {
12611 try sema.requireRuntimeBlock(block, inst_src);12642 try sema.requireRuntimeBlock(block, inst_src);
12612 return block.addTyOp(.fpext, dest_ty, inst);12643 return block.addTyOp(.fpext, dest_ty, inst);
12613 }12644 }
12614 }12645 },
12646 .Int, .ComptimeInt => int: {
12647 const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse break :int;
12648 const result_val = try val.intToFloat(sema.arena, dest_ty, target);
12649 // TODO implement this compile error
12650 //const int_again_val = try result_val.floatToInt(sema.arena, inst_ty);
12651 //if (!int_again_val.eql(val, inst_ty)) {
12652 // return sema.fail(
12653 // block,
12654 // inst_src,
12655 // "type {} cannot represent integer value {}",
12656 // .{ dest_ty, val },
12657 // );
12658 //}
12659 return try sema.addConstant(dest_ty, result_val);
12660 },
12661 else => {},
12615 },12662 },
12616 .Enum => switch (inst_ty.zigTypeTag()) {12663 .Enum => switch (inst_ty.zigTypeTag()) {
12617 .EnumLiteral => {12664 .EnumLiteral => {
...@@ -12962,80 +13009,6 @@ fn coerceInMemoryAllowedPtrs(...@@ -12962,80 +13009,6 @@ fn coerceInMemoryAllowedPtrs(
12962 return .ok;13009 return .ok;
12963}13010}
1296413011
12965fn coerceNum(
12966 sema: *Sema,
12967 block: *Block,
12968 dest_ty: Type,
12969 inst: Air.Inst.Ref,
12970 inst_src: LazySrcLoc,
12971) CompileError!?Air.Inst.Ref {
12972 const val = (try sema.resolveDefinedValue(block, inst_src, inst)) orelse return null;
12973 const inst_ty = sema.typeOf(inst);
12974 const src_zig_tag = inst_ty.zigTypeTag();
12975 const dst_zig_tag = dest_ty.zigTypeTag();
12976
12977 const target = sema.mod.getTarget();
12978
12979 switch (dst_zig_tag) {
12980 .ComptimeInt, .Int => switch (src_zig_tag) {
12981 .Float, .ComptimeFloat => {
12982 if (val.floatHasFraction()) {
12983 return sema.fail(block, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val, dest_ty });
12984 }
12985 const result_val = val.floatToInt(sema.arena, dest_ty, target) catch |err| switch (err) {
12986 error.FloatCannotFit => {
12987 return sema.fail(block, inst_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty });
12988 },
12989 else => |e| return e,
12990 };
12991 return try sema.addConstant(dest_ty, result_val);
12992 },
12993 .Int, .ComptimeInt => {
12994 if (!val.intFitsInType(dest_ty, target)) {
12995 return sema.fail(block, inst_src, "type {} cannot represent integer value {}", .{ dest_ty, val });
12996 }
12997 return try sema.addConstant(dest_ty, val);
12998 },
12999 else => {},
13000 },
13001 .ComptimeFloat, .Float => switch (src_zig_tag) {
13002 .ComptimeFloat => {
13003 const result_val = try val.floatCast(sema.arena, dest_ty);
13004 return try sema.addConstant(dest_ty, result_val);
13005 },
13006 .Float => {
13007 const result_val = try val.floatCast(sema.arena, dest_ty);
13008 if (!val.eql(result_val, dest_ty)) {
13009 return sema.fail(
13010 block,
13011 inst_src,
13012 "type {} cannot represent float value {}",
13013 .{ dest_ty, val },
13014 );
13015 }
13016 return try sema.addConstant(dest_ty, result_val);
13017 },
13018 .Int, .ComptimeInt => {
13019 const result_val = try val.intToFloat(sema.arena, dest_ty, target);
13020 // TODO implement this compile error
13021 //const int_again_val = try result_val.floatToInt(sema.arena, inst_ty);
13022 //if (!int_again_val.eql(val, inst_ty)) {
13023 // return sema.fail(
13024 // block,
13025 // inst_src,
13026 // "type {} cannot represent integer value {}",
13027 // .{ dest_ty, val },
13028 // );
13029 //}
13030 return try sema.addConstant(dest_ty, result_val);
13031 },
13032 else => {},
13033 },
13034 else => {},
13035 }
13036 return null;
13037}
13038
13039fn coerceVarArgParam(13012fn coerceVarArgParam(
13040 sema: *Sema,13013 sema: *Sema,
13041 block: *Block,13014 block: *Block,