commit 38de3d690073d5d4bac8695b747179097beaa1b7
parent 20db8aa13ac26b27e7a60286dc283b65da24e14e
Author: Eyal Sawady <ecs@d2evs.net>
Date: Fri, 25 Feb 2022 16:45:23 +0000
Add tests for flexible constants
Signed-off-by: Eyal Sawady <ecs@d2evs.net>
Diffstat:
2 files changed, 195 insertions(+), 0 deletions(-)
diff --git a/tests/00-constants.ha b/tests/00-constants.ha
@@ -1,3 +1,137 @@
+use rt;
+
+type my_enum = enum u8 {
+ FOO,
+};
+
+fn assignment() void = {
+ let i = 0i8;
+ let u = 0u64;
+ let f = 0.0f64;
+ let r = 'a';
+ let e = my_enum::FOO;
+
+ // There are five cases that need to be tested for tagged unions:
+ // - The default type for the constant is a member of the union
+ // - A single non-default type the constant could assume is a member of
+ // the union
+ // - The default type for the constant along with at least one other
+ // type the constant could assume are both members of the union
+ // - At least two types the constant could assume are members of the
+ // union, and the default type isn't a member of the union
+ // - None of the types the constant could assume are members of the
+ // union
+ // All but the fourth and fifth case are valid, and the invalid cases
+ // should error out gracefully.
+ let itu1: (int | void) = void;
+ let itu2: (u64 | void) = void;
+ let itu3: (int | u64 | void) = void;
+ let ftu1: (f64 | void) = void;
+ let ftu2: (f32 | void) = void;
+ let ftu3: (f32 | f64 | void) = void;
+ let rtu1: (rune | void) = void;
+ let rtu2: (u64 | void) = void;
+ let rtu3: (rune | u64 | void) = void;
+
+ i = 127;
+ assert(rt::compile("export fn main() void = { let i = 0i8; i = 128; };") != 0);
+ u = 18446744073709551615;
+ assert(rt::compile("export fn main() void = { let u = 0u32; u = 4294967296; };") != 0);
+ assert(rt::compile("export fn main() void = { let f = 0.0f64; f = 0; };") != 0);
+ assert(rt::compile("export fn main() void = { let r = 'a'; r = 0; };") != 0);
+ e = 0;
+ assert(rt::compile("type my_enum = enum u8 { FOO }; export fn main() void = { let e: my_enum = my_enum::FOO; e = 256; };") != 0);
+ assert(rt::compile("export fn main() void = { let p: nullable *void = null; p = 0; };") != 0);
+ assert(rt::compile("export fn main() void = { let b = false; b = 0; };") != 0);
+ assert(rt::compile("export fn main() void = { let n = null; n = 0; };") != 0);
+ assert(rt::compile("export fn main() void = { let s: struct { i: int } = struct { i: int = 0 }; s = 0; };") != 0);
+ assert(rt::compile("export fn main() void = { let t = (0, 1); t = 0; };") != 0);
+ assert(rt::compile("export fn main() void = { let a = [0, 1]; a = 0; };") != 0);
+ assert(rt::compile("export fn main() void = { let s = \"\"; s = 0; };") != 0);
+ itu1 = 0;
+ itu2 = 0;
+ itu3 = 0;
+ assert(rt::compile("export fn main() void = { let itu4: (u32 | u64 | void) = void; itu4 = 0; };") != 0);
+ assert(rt::compile("export fn main() void = { let itu5: (str | void) = void; itu5 = 0; };") != 0);
+
+ assert(rt::compile("export fn main() void = { let i = 0i8; i = 0.0; };") != 0);
+ assert(rt::compile("export fn main() void = { let u = 0u8; u = 0.0; };") != 0);
+ f = 0.0;
+ assert(rt::compile("export fn main() void = { let r = 'a'; r = 0.0; };") != 0);
+ assert(rt::compile("type my_enum = enum u8 { FOO }; export fn main() void = { let e: my_enum = my_enum::FOO; e = 0.0; };") != 0);
+ assert(rt::compile("export fn main() void = { let p: nullable *void = null; p = 0.0; };") != 0);
+ assert(rt::compile("export fn main() void = { let b = false; b = 0.0; };") != 0);
+ assert(rt::compile("export fn main() void = { let n = null; n = 0.0; };") != 0);
+ assert(rt::compile("export fn main() void = { let s: struct { i: int } = struct { i: int = 0 }; s = 0.0; };") != 0);
+ assert(rt::compile("export fn main() void = { let t = (0, 1); t = 0.0; };") != 0);
+ assert(rt::compile("export fn main() void = { let a = [0, 1]; a = 0.0; };") != 0);
+ assert(rt::compile("export fn main() void = { let s = ""; s = 0.0; };") != 0);
+ ftu1 = 0.0;
+ ftu2 = 0.0;
+ ftu3 = 0.0;
+ assert(rt::compile("type my_f32 = f32; export fn main() void = { let ftu4: (f32 | my_f32 | void) = void; ftu4 = 0.0; };") != 0);
+ assert(rt::compile("export fn main() void = { let ftu5: (str | void) = void; ftu5 = 0.0; };") != 0);
+
+ i = 'a';
+ u = 'a';
+ assert(rt::compile("export fn main() void = { let f = 0.0f64; f = 'a'; };") != 0);
+ r = 'a';
+ e = 'a';
+ assert(rt::compile("export fn main() void = { let p: nullable *void = null; p = 'a'; };") != 0);
+ assert(rt::compile("export fn main() void = { let b = false; b = 'a'; };") != 0);
+ assert(rt::compile("export fn main() void = { let n = null; n = 'a'; };") != 0);
+ assert(rt::compile("export fn main() void = { let s: struct { i: int } = struct { i: int = 0 }; s = 'a'; };") != 0);
+ assert(rt::compile("export fn main() void = { let t = (0, 1); t = 'a'; };") != 0);
+ assert(rt::compile("export fn main() void = { let a = [0, 1]; a = 'a'; };") != 0);
+ assert(rt::compile("export fn main() void = { let s = ""; s = 'a'; };") != 0);
+ rtu1 = 'a';
+ rtu2 = 'a';
+ rtu3 = 'a';
+ assert(rt::compile("export fn main() void = { let rtu4: (u32 | u64 | void) = void; rtu4 = 'a'; };") != 0);
+ assert(rt::compile("export fn main() void = { let rtu5: (str | void) = void; rtu5 = 'a'; };") != 0);
+};
+
+fn aggregates() void = {
+ // Pointers
+
+ // Kinda hacky way to verify that something has the expected type
+ // The variables are necessary in order to avoid type hints, which would
+ // avoid verifying that constants are lowered when entering aggregate
+ // types
+ let maxiptr = if (true) alloc(2147483647) else void;
+ free(maxiptr as *int);
+ let miniptr = if (true) alloc(-2147483648) else void;
+ free(miniptr as *int);
+ let smalli64ptr = if (true) alloc(2147483648) else void;
+ free(smalli64ptr as *i64);
+ let negi64ptr = if (true) alloc(-2147483649) else void;
+ free(negi64ptr as *i64);
+ let maxi64ptr = if (true) alloc(9223372036854775807) else void;
+ free(maxi64ptr as *i64);
+ // -9223372036854775808 can't be made to work without lots of hacks
+ let mini64ptr = if (true) alloc(-9223372036854775807) else void;
+ free(mini64ptr as *i64);
+ let fptr = if (true) alloc(0.0) else void;
+ free(fptr as *f64);
+ let rptr = if (true) alloc('a') else void;
+ free(rptr as *rune);
+
+ // Tuples
+
+ // The edge cases of the iconst lowering algorithm were already tested
+ // above, and tuple items can't affect each other, so this suffices
+ let tuple = if (true) (2147483647, 0.0, 'a') else void;
+ tuple as (int, f64, rune);
+
+ // Arrays
+ let iarr = if (true) [0, 1, 2] else void;
+ iarr as [3]int;
+ let uarr = if (true) [0u8, 1, 2] else void;
+ uarr as [3]u8;
+ let u2arr = if (true) [0, 1u8, 2] else void;
+ u2arr as [3]u8;
+};
+
export fn main() void = {
let i1 = 13, i2 = 13i, i3 = 13i8, i4 = 13i16, i5 = 13i32, i6 = 13i64;
let u1 = 13u, u2 = 13z, u3 = 13u8, u4 = 13u16, u5 = 13u32, u6 = 13u64;
@@ -10,4 +144,9 @@ export fn main() void = {
r15 = '\U12345678';
let f1 = 1.0, f2 = 1f32, f3 = 1.0e2, f4 = 1.0f64;
let f5 = 1.23e+45, f6 = 9.87e-65, f7 = 1e-7, f8 = 5.0e-324;
+
+ // The interaction between constants and result type reduction is tested
+ // in 30-reduction.c
+ assignment();
+ aggregates();
};
diff --git a/tests/30-reduction.c b/tests/30-reduction.c
@@ -129,4 +129,60 @@ int main(void) {
test(&ctx, "",
"if (true) null "
"else void");
+
+ // However, constants behave differently in if vs switch/match
+
+ test(&ctx, "int", "if (true) 0 else if (true) 1 else 2");
+ test(&ctx, "(int | i64)", "if (true) 0 else 9223372036854775807");
+ test(&ctx, "(int | size)", "if (true) 0 else 0z");
+ test(&ctx, "(int | void)", "if (true) 0 else void");
+
+ test(&ctx, "int",
+ "switch (0) { "
+ "case 0 => "
+ " yield 0; "
+ "case 1 => "
+ " yield 1; "
+ "case => "
+ " yield 2; "
+ "};");
+ test(&ctx, "(int | i64)",
+ "switch (0) { "
+ "case 0 => "
+ " yield 0; "
+ "case => "
+ " yield 9223372036854775807; "
+ "};");
+ test(&ctx, "(int | size)",
+ "switch (0) { "
+ "case 0 => "
+ " yield 0; "
+ "case => "
+ " yield 0z; "
+ "};");
+ test(&ctx, "(int | void)",
+ "switch (0) { "
+ "case 0 => "
+ " yield 0; "
+ "case => "
+ " yield; "
+ "};");
+ test(&ctx, "(int | size | u32)",
+ "switch (0) { "
+ "case 0 => "
+ " yield 0; "
+ "case 1 => "
+ " yield 1z; "
+ "case => "
+ " yield 2u32; "
+ "};");
+ test(&ctx, "(int | i64)",
+ "switch (0) { "
+ "case 0 => "
+ " yield 0; "
+ "case 1 => "
+ " yield 1i; "
+ "case => "
+ " yield 9223372036854775807; "
+ "};");
}