mirror of
https://github.com/ziglang/zig.git
synced 2024-11-14 16:13:24 +00:00
Fixes parsing integers as floats
This commit is contained in:
parent
0f2fde90af
commit
54950c9c4b
215
src/zon.zig
215
src/zon.zig
@ -252,7 +252,8 @@ fn parseExpr(self: LowerZon, node: Ast.Node.Index, res_ty: Type) CompileError!In
|
||||
switch (Type.zigTypeTag(res_ty, self.sema.pt.zcu)) {
|
||||
.void => return self.parseVoid(node),
|
||||
.bool => return self.parseBool(node),
|
||||
.int, .comptime_int, .float, .comptime_float => return self.parseNumber(node, res_ty),
|
||||
.int, .comptime_int => return self.parseInt(node, res_ty),
|
||||
.float, .comptime_float => return self.parseFloat(node, res_ty),
|
||||
.optional => return self.parseOptional(node, res_ty),
|
||||
.null => return self.parseNull(node),
|
||||
.@"enum" => return self.parseEnum(node, res_ty),
|
||||
@ -544,7 +545,7 @@ fn parseBool(self: LowerZon, node: Ast.Node.Index) !InternPool.Index {
|
||||
return self.fail(.{ .node_abs = node }, "expected bool", .{});
|
||||
}
|
||||
|
||||
fn parseNumber(
|
||||
fn parseInt(
|
||||
self: LowerZon,
|
||||
node: Ast.Node.Index,
|
||||
res_ty: Type,
|
||||
@ -693,64 +694,126 @@ fn parseNumber(
|
||||
unreachable;
|
||||
};
|
||||
const float = if (is_negative == null) unsigned_float else -unsigned_float;
|
||||
switch (Type.zigTypeTag(res_ty, self.sema.pt.zcu)) {
|
||||
.float, .comptime_float => return self.sema.pt.intern(.{ .float = .{
|
||||
.ty = res_ty.toIntern(),
|
||||
.storage = switch (res_ty.floatBits(self.sema.pt.zcu.getTarget())) {
|
||||
16 => .{ .f16 = @floatCast(float) },
|
||||
32 => .{ .f32 = @floatCast(float) },
|
||||
64 => .{ .f64 = @floatCast(float) },
|
||||
80 => .{ .f80 = @floatCast(float) },
|
||||
128 => .{ .f128 = float },
|
||||
else => unreachable,
|
||||
},
|
||||
} }),
|
||||
.int, .comptime_int => {
|
||||
// Check for fractional components
|
||||
if (@rem(float, 1) != 0) {
|
||||
return self.fail(
|
||||
.{ .node_abs = num_lit_node },
|
||||
"fractional component prevents float value '{}' from coercion to type '{}'",
|
||||
.{ float, res_ty.fmt(self.sema.pt) },
|
||||
);
|
||||
}
|
||||
|
||||
// Create a rational representation of the float
|
||||
var rational = try std.math.big.Rational.init(gpa);
|
||||
defer rational.deinit();
|
||||
rational.setFloat(f128, float) catch |err| switch (err) {
|
||||
error.NonFiniteFloat => unreachable,
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
};
|
||||
|
||||
// The float is reduced in rational.setFloat, so we assert that denominator is equal to one
|
||||
const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true };
|
||||
assert(rational.q.toConst().eqlAbs(big_one));
|
||||
if (is_negative != null) rational.negate();
|
||||
|
||||
// Check that the result is in range of the result type
|
||||
const int_info = res_ty.intInfo(self.sema.pt.zcu);
|
||||
if (!rational.p.fitsInTwosComp(int_info.signedness, int_info.bits)) {
|
||||
return self.fail(
|
||||
.{ .node_abs = num_lit_node },
|
||||
"float value '{}' cannot be stored in integer type '{}'",
|
||||
.{ float, res_ty.fmt(self.sema.pt) },
|
||||
);
|
||||
}
|
||||
|
||||
return self.sema.pt.zcu.intern_pool.get(gpa, self.sema.pt.tid, .{
|
||||
.int = .{
|
||||
.ty = res_ty.toIntern(),
|
||||
.storage = .{ .big_int = rational.p.toConst() },
|
||||
},
|
||||
});
|
||||
},
|
||||
else => unreachable,
|
||||
// Check for fractional components
|
||||
if (@rem(float, 1) != 0) {
|
||||
return self.fail(
|
||||
.{ .node_abs = num_lit_node },
|
||||
"fractional component prevents float value '{}' from coercion to type '{}'",
|
||||
.{ float, res_ty.fmt(self.sema.pt) },
|
||||
);
|
||||
}
|
||||
|
||||
// Create a rational representation of the float
|
||||
var rational = try std.math.big.Rational.init(gpa);
|
||||
defer rational.deinit();
|
||||
rational.setFloat(f128, float) catch |err| switch (err) {
|
||||
error.NonFiniteFloat => unreachable,
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
};
|
||||
|
||||
// The float is reduced in rational.setFloat, so we assert that denominator is equal to one
|
||||
const big_one = std.math.big.int.Const{ .limbs = &.{1}, .positive = true };
|
||||
assert(rational.q.toConst().eqlAbs(big_one));
|
||||
if (is_negative != null) rational.negate();
|
||||
|
||||
// Check that the result is in range of the result type
|
||||
const int_info = res_ty.intInfo(self.sema.pt.zcu);
|
||||
if (!rational.p.fitsInTwosComp(int_info.signedness, int_info.bits)) {
|
||||
return self.fail(
|
||||
.{ .node_abs = num_lit_node },
|
||||
"float value '{}' cannot be stored in integer type '{}'",
|
||||
.{ float, res_ty.fmt(self.sema.pt) },
|
||||
);
|
||||
}
|
||||
|
||||
return self.sema.pt.zcu.intern_pool.get(gpa, self.sema.pt.tid, .{
|
||||
.int = .{
|
||||
.ty = res_ty.toIntern(),
|
||||
.storage = .{ .big_int = rational.p.toConst() },
|
||||
},
|
||||
});
|
||||
},
|
||||
.failure => |err| return self.failWithNumberError(token, err),
|
||||
}
|
||||
},
|
||||
.identifier => {
|
||||
unreachable; // Decide what error to give here
|
||||
},
|
||||
else => return self.fail(.{ .node_abs = num_lit_node }, "invalid ZON value", .{}),
|
||||
}
|
||||
}
|
||||
|
||||
fn parseFloat(
|
||||
self: LowerZon,
|
||||
node: Ast.Node.Index,
|
||||
res_ty: Type,
|
||||
) !InternPool.Index {
|
||||
@setFloatMode(.strict);
|
||||
|
||||
const tags = self.file.tree.nodes.items(.tag);
|
||||
const main_tokens = self.file.tree.nodes.items(.main_token);
|
||||
const num_lit_node, const is_negative = if (tags[node] == .negation) b: {
|
||||
const data = self.file.tree.nodes.items(.data);
|
||||
break :b .{
|
||||
data[node].lhs,
|
||||
node,
|
||||
};
|
||||
} else .{
|
||||
node,
|
||||
null,
|
||||
};
|
||||
switch (tags[num_lit_node]) {
|
||||
.char_literal => {
|
||||
const token = main_tokens[num_lit_node];
|
||||
const token_bytes = self.file.tree.tokenSlice(token);
|
||||
var char: i64 = switch (std.zig.string_literal.parseCharLiteral(token_bytes)) {
|
||||
.success => |char| char,
|
||||
.failure => |err| {
|
||||
const offset = self.file.tree.tokens.items(.start)[token];
|
||||
return self.fail(
|
||||
.{ .byte_abs = offset + @as(u32, @intCast(err.offset())) },
|
||||
"{}",
|
||||
.{err.fmtWithSource(token_bytes)},
|
||||
);
|
||||
},
|
||||
};
|
||||
if (is_negative != null) char = -char;
|
||||
return self.sema.pt.intern(.{ .float = .{
|
||||
.ty = res_ty.toIntern(),
|
||||
.storage = switch (res_ty.toIntern()) {
|
||||
.f16_type => .{ .f16 = @floatFromInt(char) },
|
||||
.f32_type => .{ .f32 = @floatFromInt(char) },
|
||||
.f64_type => .{ .f64 = @floatFromInt(char) },
|
||||
.f80_type => .{ .f80 = @floatFromInt(char) },
|
||||
.f128_type, .comptime_float_type => .{ .f128 = @floatFromInt(char) },
|
||||
else => unreachable,
|
||||
},
|
||||
} });
|
||||
},
|
||||
.number_literal => {
|
||||
const token = main_tokens[num_lit_node];
|
||||
const token_bytes = self.file.tree.tokenSlice(token);
|
||||
|
||||
var float = std.fmt.parseFloat(f128, token_bytes) catch |err| switch (err) {
|
||||
error.InvalidCharacter => return self.fail(.{ .node_abs = num_lit_node }, "invalid character", .{}),
|
||||
};
|
||||
if (is_negative != null) float = -float;
|
||||
|
||||
return self.sema.pt.intern(.{
|
||||
.float = .{
|
||||
.ty = res_ty.toIntern(),
|
||||
.storage = switch (res_ty.toIntern()) {
|
||||
.f16_type => .{ .f16 = @floatCast(float) },
|
||||
.f32_type => .{ .f32 = @floatCast(float) },
|
||||
.f64_type => .{ .f64 = @floatCast(float) },
|
||||
.f80_type => .{ .f80 = @floatCast(float) },
|
||||
.f128_type, .comptime_float_type => .{ .f128 = float },
|
||||
else => unreachable,
|
||||
},
|
||||
},
|
||||
});
|
||||
},
|
||||
.identifier => {
|
||||
switch (Type.zigTypeTag(res_ty, self.sema.pt.zcu)) {
|
||||
.float, .comptime_float => {},
|
||||
@ -765,28 +828,32 @@ fn parseNumber(
|
||||
});
|
||||
if (values.get(bytes)) |value| {
|
||||
return switch (value) {
|
||||
.nan => self.sema.pt.intern(.{ .float = .{
|
||||
.ty = res_ty.toIntern(),
|
||||
.storage = switch (res_ty.floatBits(self.sema.pt.zcu.getTarget())) {
|
||||
16 => .{ .f16 = std.math.nan(f16) },
|
||||
32 => .{ .f32 = std.math.nan(f32) },
|
||||
64 => .{ .f64 = std.math.nan(f64) },
|
||||
80 => .{ .f80 = std.math.nan(f80) },
|
||||
128 => .{ .f128 = std.math.nan(f128) },
|
||||
else => unreachable,
|
||||
.nan => self.sema.pt.intern(.{
|
||||
.float = .{
|
||||
.ty = res_ty.toIntern(),
|
||||
.storage = switch (res_ty.toIntern()) {
|
||||
.f16_type => .{ .f16 = std.math.nan(f16) },
|
||||
.f32_type => .{ .f32 = std.math.nan(f32) },
|
||||
.f64_type => .{ .f64 = std.math.nan(f64) },
|
||||
.f80_type => .{ .f80 = std.math.nan(f80) },
|
||||
.f128_type, .comptime_float_type => .{ .f128 = std.math.nan(f128) },
|
||||
else => unreachable,
|
||||
},
|
||||
},
|
||||
} }),
|
||||
.inf => self.sema.pt.intern(.{ .float = .{
|
||||
.ty = res_ty.toIntern(),
|
||||
.storage = switch (res_ty.floatBits(self.sema.pt.zcu.getTarget())) {
|
||||
16 => .{ .f16 = if (is_negative == null) std.math.inf(f16) else -std.math.inf(f16) },
|
||||
32 => .{ .f32 = if (is_negative == null) std.math.inf(f32) else -std.math.inf(f32) },
|
||||
64 => .{ .f64 = if (is_negative == null) std.math.inf(f64) else -std.math.inf(f64) },
|
||||
80 => .{ .f80 = if (is_negative == null) std.math.inf(f80) else -std.math.inf(f80) },
|
||||
128 => .{ .f128 = if (is_negative == null) std.math.inf(f128) else -std.math.inf(f128) },
|
||||
else => unreachable,
|
||||
}),
|
||||
.inf => self.sema.pt.intern(.{
|
||||
.float = .{
|
||||
.ty = res_ty.toIntern(),
|
||||
.storage = switch (res_ty.toIntern()) {
|
||||
.f16_type => .{ .f16 = if (is_negative == null) std.math.inf(f16) else -std.math.inf(f16) },
|
||||
.f32_type => .{ .f32 = if (is_negative == null) std.math.inf(f32) else -std.math.inf(f32) },
|
||||
.f64_type => .{ .f64 = if (is_negative == null) std.math.inf(f64) else -std.math.inf(f64) },
|
||||
.f80_type => .{ .f80 = if (is_negative == null) std.math.inf(f80) else -std.math.inf(f80) },
|
||||
.f128_type, .comptime_float_type => .{ .f128 = if (is_negative == null) std.math.inf(f128) else -std.math.inf(f128) },
|
||||
else => unreachable,
|
||||
},
|
||||
},
|
||||
} }),
|
||||
}),
|
||||
};
|
||||
}
|
||||
return self.fail(.{ .node_abs = num_lit_node }, "use of unknown identifier '{s}'", .{bytes});
|
||||
|
@ -91,30 +91,23 @@ test "union" {
|
||||
test "struct" {
|
||||
const Vec0 = struct {};
|
||||
const Vec1 = struct { x: f32 };
|
||||
// const Vec2 = struct { x: f32, y: f32 };
|
||||
// const Escaped = struct { @"0": f32, foo: f32 };
|
||||
const Vec2 = struct { x: f32, y: f32 };
|
||||
const Escaped = struct { @"0": f32, foo: f32 };
|
||||
try expectEqual(Vec0{}, @as(Vec0, @import("zon/vec0.zon")));
|
||||
try expectEqual(Vec1{ .x = 1.5 }, @as(Vec1, @import("zon/vec1.zon")));
|
||||
// try expectEqual(Vec2{ .x = 1.5, .y = 2 }, @as(Vec2, @import("zon/vec2.zon")));
|
||||
// try expectEqual(Escaped{ .@"0" = 1.5, .foo = 2 }, @as(Escaped, @import("zon/escaped_struct.zon")));
|
||||
|
||||
// The skipped parts are failing because we need to resolve an issue where we intern whole number
|
||||
// floats incorrectly (they get parsed as integers and then we try to store them that way, they
|
||||
// should just be parsed as floats)
|
||||
return error.SkipZigTest;
|
||||
try expectEqual(Vec2{ .x = 1.5, .y = 2 }, @as(Vec2, @import("zon/vec2.zon")));
|
||||
try expectEqual(Escaped{ .@"0" = 1.5, .foo = 2 }, @as(Escaped, @import("zon/escaped_struct.zon")));
|
||||
}
|
||||
|
||||
test "struct default fields" {
|
||||
// We're skipping this for the same reason we skip some of the other struct tests
|
||||
return error.SkipZigTest;
|
||||
// const Vec3 = struct {
|
||||
// x: f32,
|
||||
// y: f32,
|
||||
// z: f32 = 123.4,
|
||||
// };
|
||||
// try expectEqual(Vec3{ .x = 1.5, .y = 2.0, .z = 123.4 }, @as(Vec3, @import("zon/vec2.zon")));
|
||||
// const ascribed: Vec3 = @import("zon/vec2.zon");
|
||||
// try expectEqual(Vec3{ .x = 1.5, .y = 2.0, .z = 123.4 }, ascribed);
|
||||
const Vec3 = struct {
|
||||
x: f32,
|
||||
y: f32,
|
||||
z: f32 = 123.4,
|
||||
};
|
||||
try expectEqual(Vec3{ .x = 1.5, .y = 2.0, .z = 123.4 }, @as(Vec3, @import("zon/vec2.zon")));
|
||||
const ascribed: Vec3 = @import("zon/vec2.zon");
|
||||
try expectEqual(Vec3{ .x = 1.5, .y = 2.0, .z = 123.4 }, ascribed);
|
||||
}
|
||||
|
||||
test "struct enum field" {
|
||||
@ -272,42 +265,40 @@ test "int" {
|
||||
}
|
||||
|
||||
test "floats" {
|
||||
// See issue on disabled struct tests
|
||||
return error.SkipZigTest;
|
||||
// const expected = .{
|
||||
// // Test decimals
|
||||
// @as(f16, 0.5),
|
||||
// @as(f32, 123.456),
|
||||
// @as(f64, -123.456),
|
||||
// @as(f128, 42.5),
|
||||
const expected = .{
|
||||
// Test decimals
|
||||
@as(f16, 0.5),
|
||||
@as(f32, 123.456),
|
||||
@as(f64, -123.456),
|
||||
@as(f128, 42.5),
|
||||
|
||||
// // Test whole numbers with and without decimals
|
||||
// @as(f16, 5.0),
|
||||
// @as(f16, 5.0),
|
||||
// @as(f32, -102),
|
||||
// @as(f32, -102),
|
||||
// Test whole numbers with and without decimals
|
||||
@as(f16, 5.0),
|
||||
@as(f16, 5.0),
|
||||
@as(f32, -102),
|
||||
@as(f32, -102),
|
||||
|
||||
// // Test characters and negated characters
|
||||
// @as(f32, 'a'),
|
||||
// @as(f32, 'z'),
|
||||
// @as(f32, -'z'),
|
||||
// Test characters and negated characters
|
||||
@as(f32, 'a'),
|
||||
@as(f32, 'z'),
|
||||
@as(f32, -'z'),
|
||||
|
||||
// // Test big integers
|
||||
// @as(f32, 36893488147419103231),
|
||||
// @as(f32, -36893488147419103231),
|
||||
// @as(f128, 0x1ffffffffffffffff),
|
||||
// @as(f32, 0x1ffffffffffffffff),
|
||||
// Test big integers
|
||||
@as(f32, 36893488147419103231),
|
||||
@as(f32, -36893488147419103231),
|
||||
@as(f128, 0x1ffffffffffffffff),
|
||||
@as(f32, 0x1ffffffffffffffff),
|
||||
|
||||
// // Exponents, underscores
|
||||
// @as(f32, 123.0E+77),
|
||||
// Exponents, underscores
|
||||
@as(f32, 123.0E+77),
|
||||
|
||||
// // Hexadecimal
|
||||
// @as(f32, 0x103.70p-5),
|
||||
// @as(f32, -0x103.70),
|
||||
// @as(f32, 0x1234_5678.9ABC_CDEFp-10),
|
||||
// };
|
||||
// const actual: @TypeOf(expected) = @import("zon/floats.zon");
|
||||
// try expectEqual(actual, expected);
|
||||
// Hexadecimal
|
||||
@as(f32, 0x103.70p-5),
|
||||
@as(f32, -0x103.70),
|
||||
@as(f32, 0x1234_5678.9ABC_CDEFp-10),
|
||||
};
|
||||
const actual: @TypeOf(expected) = @import("zon/floats.zon");
|
||||
try expectEqual(actual, expected);
|
||||
}
|
||||
|
||||
test "inf and nan" {
|
||||
|
Loading…
Reference in New Issue
Block a user