Skip to content

Commit a7fbb21

Browse files
Calculate comptime integer arithmetic
1 parent 0bb4338 commit a7fbb21

4 files changed

Lines changed: 316 additions & 47 deletions

File tree

src/analyser/InternPool.zig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3846,6 +3846,15 @@ pub fn toInt(ip: *InternPool, val: Index, comptime T: type) ?T {
38463846
};
38473847
}
38483848

3849+
pub fn toBigInt(ip: *InternPool, gpa: Allocator, val: Index) !?std.math.big.int.Managed {
3850+
return switch (ip.indexToKey(val)) {
3851+
.int_u64_value => |int_value| try .initSet(gpa, int_value.int),
3852+
.int_i64_value => |int_value| try .initSet(gpa, int_value.int),
3853+
.int_big_value => |int_value| try int_value.getConst(ip).toManaged(gpa),
3854+
else => try .initSet(gpa, ip.toInt(val, i64) orelse return null),
3855+
};
3856+
}
3857+
38493858
pub fn getBigInt(ip: *InternPool, ty: Index, int: std.math.big.int.Const) Allocator.Error!Index {
38503859
assert(ip.isType(ty));
38513860
return try ip.get(.{

src/analysis.zig

Lines changed: 181 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1464,6 +1464,148 @@ fn resolveIntegerLiteral(analyser: *Analyser, comptime T: type, options: Resolve
14641464
return analyser.ip.toInt(ip_index, T);
14651465
}
14661466

1467+
const IntInfo = union(enum) {
1468+
comptime_int,
1469+
fixed_int: std.builtin.Type.Int,
1470+
};
1471+
1472+
fn intInfo(analyser: *Analyser, ty: InternPool.Index) ?IntInfo {
1473+
const type_tag = analyser.ip.zigTypeTag(ty) orelse return null;
1474+
return switch (type_tag) {
1475+
.comptime_int => .comptime_int,
1476+
.int => .{ .fixed_int = analyser.ip.intInfo(ty, builtin.target) },
1477+
else => null,
1478+
};
1479+
}
1480+
1481+
fn resolveUnaryIntegerExpression(
1482+
analyser: *Analyser,
1483+
tag: std.zig.Ast.Node.Tag,
1484+
instance: Type,
1485+
) !?Type {
1486+
const index = instance.ipIndex() orelse return null;
1487+
const ty = analyser.ip.typeOf(index);
1488+
const int_info = analyser.intInfo(ty) orelse return null;
1489+
var result = try analyser.ip.toBigInt(analyser.gpa, index) orelse return null;
1490+
defer result.deinit();
1491+
switch (tag) {
1492+
.bit_not => switch (int_info) {
1493+
.comptime_int => return null,
1494+
.fixed_int => |info| _ = try result.bitNotWrap(&result, info.signedness, info.bits),
1495+
},
1496+
.negation => result.negate(),
1497+
.negation_wrap => switch (int_info) {
1498+
.comptime_int => result.negate(),
1499+
.fixed_int => |info| {
1500+
// TODO: std.math.big.int does not have a negateWrap method
1501+
_ = info;
1502+
return null;
1503+
},
1504+
},
1505+
else => unreachable,
1506+
}
1507+
return try analyser.resolveIntegerExpressionResult(ty, result.toConst());
1508+
}
1509+
1510+
fn resolveBinaryIntegerExpression(
1511+
analyser: *Analyser,
1512+
tag: std.zig.Ast.Node.Tag,
1513+
instance: Type,
1514+
lhs_instance: Type,
1515+
rhs_instance: Type,
1516+
) !?Type {
1517+
const lhs_index = lhs_instance.ipIndex() orelse return null;
1518+
const rhs_index = rhs_instance.ipIndex() orelse return null;
1519+
const type_of = try instance.typeOf(analyser);
1520+
const ty = type_of.ipIndex() orelse return null;
1521+
const int_info = analyser.intInfo(ty) orelse return null;
1522+
var lhs = try analyser.ip.toBigInt(analyser.gpa, lhs_index) orelse return null;
1523+
defer lhs.deinit();
1524+
var rhs = try analyser.ip.toBigInt(analyser.gpa, rhs_index) orelse return null;
1525+
defer rhs.deinit();
1526+
var result: std.math.big.int.Managed = try .init(analyser.gpa);
1527+
defer result.deinit();
1528+
switch (tag) {
1529+
.mul => try result.mul(&lhs, &rhs),
1530+
.div => {
1531+
var temp: std.math.big.int.Managed = try .init(analyser.gpa);
1532+
defer temp.deinit();
1533+
try result.divTrunc(&temp, &lhs, &rhs);
1534+
},
1535+
.mod => {
1536+
var temp: std.math.big.int.Managed = try .init(analyser.gpa);
1537+
defer temp.deinit();
1538+
try temp.divTrunc(&result, &lhs, &rhs);
1539+
},
1540+
.add => try result.add(&lhs, &rhs),
1541+
.sub => try result.sub(&lhs, &rhs),
1542+
.shl => try result.shiftLeft(&lhs, rhs.toInt(usize) catch return null),
1543+
.shr => try result.shiftRight(&lhs, rhs.toInt(usize) catch return null),
1544+
.bit_and => try result.bitAnd(&lhs, &rhs),
1545+
.bit_xor => try result.bitXor(&lhs, &rhs),
1546+
.bit_or => try result.bitOr(&lhs, &rhs),
1547+
.add_wrap,
1548+
.sub_wrap,
1549+
.mul_wrap,
1550+
.add_sat,
1551+
.sub_sat,
1552+
.mul_sat,
1553+
.shl_sat,
1554+
=> switch (int_info) {
1555+
.comptime_int => switch (tag) {
1556+
.add_wrap, .add_sat => try result.add(&lhs, &rhs),
1557+
.sub_wrap, .sub_sat => try result.sub(&lhs, &rhs),
1558+
.mul_wrap, .mul_sat => try result.mul(&lhs, &rhs),
1559+
.shl_sat => try result.shiftLeft(&lhs, rhs.toInt(usize) catch return null),
1560+
else => unreachable,
1561+
},
1562+
.fixed_int => |info| switch (tag) {
1563+
.add_wrap => _ = try result.addWrap(&lhs, &rhs, info.signedness, info.bits),
1564+
.sub_wrap => _ = try result.subWrap(&lhs, &rhs, info.signedness, info.bits),
1565+
.mul_wrap => _ = try result.mulWrap(&lhs, &rhs, info.signedness, info.bits),
1566+
.add_sat => try result.addSat(&lhs, &rhs, info.signedness, info.bits),
1567+
.sub_sat => try result.subSat(&lhs, &rhs, info.signedness, info.bits),
1568+
.mul_sat => {
1569+
// std.math.big.int does not have a mulSat method
1570+
try result.mul(&lhs, &rhs);
1571+
try result.saturate(&result, info.signedness, info.bits);
1572+
},
1573+
.shl_sat => try result.shiftLeftSat(&lhs, rhs.toInt(usize) catch return null, info.signedness, info.bits),
1574+
else => unreachable,
1575+
},
1576+
},
1577+
else => unreachable,
1578+
}
1579+
return try analyser.resolveIntegerExpressionResult(ty, result.toConst());
1580+
}
1581+
1582+
fn resolveIntegerExpressionResult(
1583+
analyser: *Analyser,
1584+
ty: InternPool.Index,
1585+
result: std.math.big.int.Const,
1586+
) !?Type {
1587+
const int_info = analyser.intInfo(ty) orelse return null;
1588+
switch (int_info) {
1589+
.comptime_int => {},
1590+
.fixed_int => |info| {
1591+
if (!result.fitsInTwosComp(info.signedness, info.bits)) {
1592+
return null;
1593+
}
1594+
},
1595+
}
1596+
const index = index: {
1597+
if (result.positive) blk: {
1598+
const int = result.toInt(u64) catch break :blk;
1599+
break :index try analyser.ip.get(.{ .int_u64_value = .{ .ty = ty, .int = int } });
1600+
} else blk: {
1601+
const int = result.toInt(i64) catch break :blk;
1602+
break :index try analyser.ip.get(.{ .int_i64_value = .{ .ty = ty, .int = int } });
1603+
}
1604+
break :index try analyser.ip.getBigInt(ty, result);
1605+
};
1606+
return Type.fromIP(analyser, ty, index);
1607+
}
1608+
14671609
const primitives: std.StaticStringMap(InternPool.Index) = .initComptime(.{
14681610
.{ "anyerror", .anyerror_type },
14691611
.{ "anyframe", .anyframe_type },
@@ -1982,8 +2124,9 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error
19822124
const node = node_handle.node;
19832125
const handle = node_handle.handle;
19842126
const tree = &handle.tree;
2127+
const node_tag = tree.nodeTag(node);
19852128

1986-
switch (tree.nodeTag(node)) {
2129+
switch (node_tag) {
19872130
.global_var_decl,
19882131
.local_var_decl,
19892132
.simple_var_decl,
@@ -2754,7 +2897,7 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error
27542897
=> {
27552898
const ty = try analyser.resolveTypeOfNodeInternal(.of(tree.nodeData(node).node, handle)) orelse return null;
27562899
if (ty.is_type_val) return null;
2757-
return ty.withoutIPIndex(analyser);
2900+
return try analyser.resolveUnaryIntegerExpression(node_tag, ty) orelse ty.withoutIPIndex(analyser);
27582901
},
27592902

27602903
.multiline_string_literal => {
@@ -2899,63 +3042,69 @@ fn resolveTypeOfNodeUncached(analyser: *Analyser, options: ResolveOptions) Error
28993042
.bit_or,
29003043
=> {
29013044
const lhs, const rhs = tree.nodeData(node).node_and_node;
2902-
var lhs_ty = try analyser.resolveTypeOfNodeInternal(.of(lhs, handle)) orelse return null;
2903-
if (lhs_ty.is_type_val) return null;
2904-
var rhs_ty = try analyser.resolveTypeOfNodeInternal(.of(rhs, handle)) orelse return null;
2905-
if (rhs_ty.is_type_val) return null;
2906-
lhs_ty = lhs_ty.withoutIPIndex(analyser);
2907-
rhs_ty = rhs_ty.withoutIPIndex(analyser);
2908-
return analyser.resolvePeerTypes(lhs_ty, rhs_ty);
3045+
const lhs_instance = try analyser.resolveTypeOfNodeInternal(.of(lhs, handle)) orelse return null;
3046+
if (lhs_instance.is_type_val) return null;
3047+
const rhs_instance = try analyser.resolveTypeOfNodeInternal(.of(rhs, handle)) orelse return null;
3048+
if (rhs_instance.is_type_val) return null;
3049+
const lhs_no_value = lhs_instance.withoutIPIndex(analyser);
3050+
const rhs_no_value = rhs_instance.withoutIPIndex(analyser);
3051+
const instance = try analyser.resolvePeerTypes(lhs_no_value, rhs_no_value) orelse return null;
3052+
return try analyser.resolveBinaryIntegerExpression(node_tag, instance, lhs_instance, rhs_instance) orelse return instance;
29093053
},
29103054

29113055
.add => {
29123056
const lhs, const rhs = tree.nodeData(node).node_and_node;
2913-
var lhs_ty = try analyser.resolveTypeOfNodeInternal(.of(lhs, handle)) orelse return null;
2914-
if (lhs_ty.is_type_val) return null;
2915-
var rhs_ty = try analyser.resolveTypeOfNodeInternal(.of(rhs, handle)) orelse return null;
2916-
if (rhs_ty.is_type_val) return null;
2917-
lhs_ty = lhs_ty.withoutIPIndex(analyser);
2918-
rhs_ty = rhs_ty.withoutIPIndex(analyser);
2919-
if (lhs_ty.pointerSize(analyser)) |lhs_size| {
3057+
const lhs_instance = try analyser.resolveTypeOfNodeInternal(.of(lhs, handle)) orelse return null;
3058+
if (lhs_instance.is_type_val) return null;
3059+
const rhs_instance = try analyser.resolveTypeOfNodeInternal(.of(rhs, handle)) orelse return null;
3060+
if (rhs_instance.is_type_val) return null;
3061+
const lhs_no_value = lhs_instance.withoutIPIndex(analyser);
3062+
const rhs_no_value = rhs_instance.withoutIPIndex(analyser);
3063+
if (lhs_no_value.pointerSize(analyser)) |lhs_size| {
29203064
return switch (lhs_size) {
2921-
.many, .c => lhs_ty,
3065+
.many, .c => lhs_no_value,
29223066
else => null,
29233067
};
29243068
}
2925-
return try analyser.resolvePeerTypes(lhs_ty, rhs_ty);
3069+
const instance = try analyser.resolvePeerTypes(lhs_no_value, rhs_no_value) orelse return null;
3070+
return try analyser.resolveBinaryIntegerExpression(node_tag, instance, lhs_instance, rhs_instance) orelse return instance;
29263071
},
29273072

29283073
.sub => {
29293074
const lhs, const rhs = tree.nodeData(node).node_and_node;
2930-
var lhs_ty = try analyser.resolveTypeOfNodeInternal(.of(lhs, handle)) orelse return null;
2931-
if (lhs_ty.is_type_val) return null;
2932-
var rhs_ty = try analyser.resolveTypeOfNodeInternal(.of(rhs, handle)) orelse return null;
2933-
if (rhs_ty.is_type_val) return null;
2934-
lhs_ty = lhs_ty.withoutIPIndex(analyser);
2935-
rhs_ty = rhs_ty.withoutIPIndex(analyser);
2936-
if (lhs_ty.pointerSize(analyser)) |lhs_size| {
2937-
if (rhs_ty.pointerSize(analyser)) |rhs_size| {
3075+
const lhs_instance = try analyser.resolveTypeOfNodeInternal(.of(lhs, handle)) orelse return null;
3076+
if (lhs_instance.is_type_val) return null;
3077+
const rhs_instance = try analyser.resolveTypeOfNodeInternal(.of(rhs, handle)) orelse return null;
3078+
if (rhs_instance.is_type_val) return null;
3079+
const lhs_no_value = lhs_instance.withoutIPIndex(analyser);
3080+
const rhs_no_value = rhs_instance.withoutIPIndex(analyser);
3081+
if (lhs_no_value.pointerSize(analyser)) |lhs_size| {
3082+
if (rhs_no_value.pointerSize(analyser)) |rhs_size| {
29383083
if (lhs_size == .slice) return null;
29393084
if (rhs_size == .slice) return null;
29403085
return Type.fromIP(analyser, .usize_type, null);
29413086
} else {
29423087
return switch (lhs_size) {
2943-
.many, .c => lhs_ty,
3088+
.many, .c => lhs_no_value,
29443089
else => null,
29453090
};
29463091
}
29473092
}
2948-
return try analyser.resolvePeerTypes(lhs_ty, rhs_ty);
3093+
const instance = try analyser.resolvePeerTypes(lhs_no_value, rhs_no_value) orelse return null;
3094+
return try analyser.resolveBinaryIntegerExpression(node_tag, instance, lhs_instance, rhs_instance) orelse return instance;
29493095
},
29503096

29513097
.shl,
29523098
.shl_sat,
29533099
.shr,
29543100
=> {
2955-
const lhs, _ = tree.nodeData(node).node_and_node;
2956-
const lhs_ty = try analyser.resolveTypeOfNodeInternal(.of(lhs, handle)) orelse return null;
2957-
if (lhs_ty.is_type_val) return null;
2958-
return lhs_ty.withoutIPIndex(analyser);
3101+
const lhs, const rhs = tree.nodeData(node).node_and_node;
3102+
const lhs_instance = try analyser.resolveTypeOfNodeInternal(.of(lhs, handle)) orelse return null;
3103+
if (lhs_instance.is_type_val) return null;
3104+
const lhs_no_value = lhs_instance.withoutIPIndex(analyser);
3105+
const rhs_instance = try analyser.resolveTypeOfNodeInternal(.of(rhs, handle)) orelse return lhs_no_value;
3106+
if (rhs_instance.is_type_val) return lhs_no_value;
3107+
return try analyser.resolveBinaryIntegerExpression(node_tag, lhs_no_value, lhs_instance, rhs_instance) orelse return lhs_no_value;
29593108
},
29603109

29613110
.array_mult => {

0 commit comments

Comments
 (0)