commit 269edfdb7d1fa78663cc7d09591fad90bd15d54a
parent b271d374207b7d13aa27b9cb079613f37a2954e0
Author: Bor Grošelj Simić <bgs@turminal.net>
Date: Wed, 8 Feb 2023 00:49:27 +0100
implement align() builtin
Signed-off-by: Bor Grošelj Simić <bgs@turminal.net>
Diffstat:
21 files changed, 171 insertions(+), 54 deletions(-)
diff --git a/include/expr.h b/include/expr.h
@@ -265,6 +265,7 @@ struct expression_match {
};
enum measure_operator {
+ M_ALIGN,
M_LEN,
M_SIZE,
M_OFFSET,
diff --git a/include/lex.h b/include/lex.h
@@ -19,6 +19,7 @@ enum lexical_token {
T_ATTR_THREADLOCAL,
T_UNDERSCORE,
T_ABORT,
+ T_ALIGN,
T_ALLOC,
T_APPEND,
T_AS,
diff --git a/src/check.c b/src/check.c
@@ -2236,6 +2236,20 @@ check_expr_measure(struct context *ctx,
return;
}
break;
+ case M_ALIGN:
+ expr->measure.dimensions = type_store_lookup_dimensions(
+ ctx->store, aexpr->measure.type);
+ if (expr->measure.dimensions.align == ALIGN_UNDEFINED) {
+ error(ctx, aexpr->measure.value->loc, expr,
+ "Cannot take alignment of a type with undefined alignment");
+ return;
+ }
+ if (expr->measure.dimensions.size == 0) {
+ error(ctx, aexpr->measure.value->loc, expr,
+ "Cannot take alignment of a type of size 0");
+ return;
+ }
+ break;
case M_SIZE:
expr->measure.dimensions = type_store_lookup_dimensions(
ctx->store, aexpr->measure.type);
diff --git a/src/eval.c b/src/eval.c
@@ -712,6 +712,9 @@ eval_measurement(struct context *ctx, struct expression *in, struct expression *
}
out->constant.uval = len;
return EVAL_OK;
+ case M_ALIGN:
+ out->constant.uval = in->measure.dimensions.align;
+ return EVAL_OK;
case M_SIZE:
out->constant.uval = in->measure.dimensions.size;
return EVAL_OK;
diff --git a/src/gen.c b/src/gen.c
@@ -2779,6 +2779,12 @@ gen_expr_measure(struct gen_context *ctx, const struct expression *expr)
abort(); // Invariant
}
break;
+ case M_ALIGN:
+ return (struct gen_value){
+ .kind = GV_CONST,
+ .type = &builtin_type_size,
+ .lval = expr->measure.dimensions.align,
+ };
case M_SIZE:
return (struct gen_value){
.kind = GV_CONST,
diff --git a/src/lex.c b/src/lex.c
@@ -26,6 +26,7 @@ static const char *tokens[] = {
[T_ATTR_THREADLOCAL] = "@threadlocal",
[T_UNDERSCORE] = "_",
[T_ABORT] = "abort",
+ [T_ALIGN] = "align",
[T_ALLOC] = "alloc",
[T_APPEND] = "append",
[T_AS] = "as",
diff --git a/src/parse.c b/src/parse.c
@@ -1153,6 +1153,10 @@ parse_measurement_expression(struct lexer *lexer)
want(lexer, T_LPAREN, NULL);
switch (tok.token) {
+ case T_ALIGN:
+ exp->measure.op = M_ALIGN;
+ exp->measure.type = parse_type(lexer);
+ break;
case T_SIZE:
exp->measure.op = M_SIZE;
exp->measure.type = parse_type(lexer);
@@ -1561,6 +1565,7 @@ parse_builtin_expression(struct lexer *lexer)
case T_ASSERT:
unlex(lexer, &tok);
return parse_assertion_expression(lexer, false);
+ case T_ALIGN:
case T_SIZE:
case T_LEN:
case T_OFFSET:
diff --git a/tests/01-arrays.ha b/tests/01-arrays.ha
@@ -32,6 +32,19 @@ fn measurements() void = {
assert(size([3]int) == size(int) * 3);
static assert(len([1, 2, 3]) == 3);
+
+ assert(align([_]i8) == 1);
+ assert(align([_]i16) == 2);
+ assert(align([_]i32) == 4);
+ assert(align([_]i64) == 8);
+ assert(align([*]i8) == 1);
+ assert(align([*]i16) == 2);
+ assert(align([*]i32) == 4);
+ assert(align([*]i64) == 8);
+ assert(align([2]i8) == 1);
+ assert(align([2]i16) == 2);
+ assert(align([2]i32) == 4);
+ assert(align([2]i64) == 8);
};
fn storage() void = {
@@ -42,13 +55,6 @@ fn storage() void = {
assert(*((y + (size(int) * 2): uintptr): *int) == 3);
};
-fn alignment() void = {
- let x: [_]i32 = [1, 2, 3];
- assert(&x: uintptr: size % 4 == 0);
- let y: [_]i64 = [1, 2, 3];
- assert(&y: uintptr: size % 8 == 0);
-};
-
fn assignment() void = {
let x = [1, 2, 3];
let y = x;
@@ -152,7 +158,6 @@ export fn main() void = {
indexing();
measurements();
storage();
- alignment();
assignment();
param([1, 2, 3]);
nested();
diff --git a/tests/02-integers.ha b/tests/02-integers.ha
@@ -1,43 +1,23 @@
-fn sizes() void = {
+fn dimensions() void = {
// Fixed precision
- assert(size(i8) == 1);
- assert(size(i16) == 2);
- assert(size(i32) == 4);
- assert(size(i64) == 8);
- assert(size(u8) == 1);
- assert(size(u16) == 2);
- assert(size(u32) == 4);
- assert(size(u64) == 8);
+ assert(size(i8) == 1 && align(i8) == 1 && size(i8) % align(i8) == 0);
+ assert(size(i16) == 2 && align(i16) == 2 && size(i16) % align(i16) == 0);
+ assert(size(i32) == 4 && align(i32) == 4 && size(i32) % align(i32) == 0);
+ assert(size(i64) == 8 && align(i64) == 8 && size(i64) % align(i64) == 0);
+ assert(size(u8) == 1 && align(u8) == 1 && size(u8) % align(u8) == 0);
+ assert(size(u16) == 2 && align(u16) == 2 && size(u16) % align(u16) == 0);
+ assert(size(u32) == 4 && align(u32) == 4 && size(u32) % align(u32) == 0);
+ assert(size(u64) == 8 && align(u64) == 8 && size(u64) % align(u64) == 0);
// Implementation-defined (test meets spec limits)
- assert(size(int) >= 4);
- assert(size(uint) >= 4);
+ assert(size(int) >= 4 && align(int) >= 4 && size(int) % align(int) == 0);
+ assert(size(uint) >= 4 && align(uint) >= 4 && size(uint) % align(uint) == 0);
+ assert(size(size) % align(size) == 0);
+ assert(size(uintptr) % align(uintptr) == 0);
// etc
- assert(size(char) == 1);
-};
-
-fn alignment() void = {
- // Fixed alignment
- let _i8 = 0i8, _u8 = 0u8;
- assert(&_i8: uintptr: size % 1 == 0);
- assert(&_u8: uintptr: size % 1 == 0);
- let _i16 = 0i16, _u16 = 0u16;
- assert(&_i16: uintptr: size % 2 == 0);
- assert(&_u16: uintptr: size % 2 == 0);
- let _i32 = 0i32, _u32 = 0u32;
- assert(&_i32: uintptr: size % 4 == 0);
- assert(&_u32: uintptr: size % 4 == 0);
- let _i64 = 0i64, _u64 = 0u64;
- assert(&_i64: uintptr: size % 8 == 0);
- assert(&_u64: uintptr: size % 8 == 0);
- // Implementation-defined alignment
- let i = 0i, u = 0u, z = 0z, uptr = &i: uintptr;
- assert(&i: uintptr: size % size(int) == 0);
- assert(&u: uintptr: size % size(uint) == 0);
- assert(&z: uintptr: size % size(size) == 0);
- assert(&uptr: uintptr: size % size(uintptr) == 0);
+ assert(size(char) == 1 && align(char) == 1 && size(char) % align(char) == 0);
+ assert(size(rune) == 4 && align(rune) == 4 && size(rune) % align(rune) == 0);
};
export fn main() void = {
- sizes();
- alignment();
+ dimensions();
};
diff --git a/tests/03-pointers.ha b/tests/03-pointers.ha
@@ -8,6 +8,9 @@ fn basics() void = {
assert(*y == 42);
*y = 1337;
assert(x == 1337);
+
+ assert(size(*int) == size(uintptr));
+ assert(align(*int) == align(uintptr));
};
fn _nullable() void = {
diff --git a/tests/04-strings.ha b/tests/04-strings.ha
@@ -5,10 +5,10 @@ fn measurements() void = {
assert(len("Hello!\0") == 7);
assert(len("He\0llo!") == 7);
assert(size(str) == size(*u8) + size(size) * 2);
- const align: size =
+ const alignment: size =
if (size(*u8) > size(size)) size(*u8)
else size(size);
- assert(&x: uintptr: size % align == 0);
+ assert(align(str) % alignment == 0);
static assert(len("Hello!") == 6);
};
diff --git a/tests/06-structs.ha b/tests/06-structs.ha
@@ -4,6 +4,9 @@ fn padding() void = {
assert(size(struct { x: i32, y: i32 }) == 8);
assert(size(struct { x: i32, y: i64 }) == 16);
assert(size(union { x: i8, y: i16, z: i32 }) == 4);
+ assert(align(struct { x: i32, y: i32 }) == 4);
+ assert(align(struct { x: i32, y: i64 }) == 8);
+ assert(align(union { x: i8, y: i16, z: i32 }) == 4);
const s = struct {
x: i8 = 10,
diff --git a/tests/07-aliases.ha b/tests/07-aliases.ha
@@ -104,6 +104,24 @@ fn recursive() void = {
assert(x.self == x.self.self);
};
+fn measurement() void = {
+ assert(size(my_int) == size(int) && size(my_my_int) == size(int));
+ assert(size(my_array) == size([3]int));
+ assert(size(my_array_ptr) == size(*[3]int));
+ assert(size(my_slice) == size([]int));
+ assert(size(my_struct) == size(struct { x: int, y: int }));
+ assert(size(my_struct_ptr) == size(*struct { x: int, y: int }));
+ assert(size(my_tagged) == size((int | void)));
+
+ assert(align(my_int) == align(int) && align(my_my_int) == align(int));
+ assert(align(my_array) == align([3]int));
+ assert(align(my_array_ptr) == align(*[3]int));
+ assert(align(my_slice) == align([]int));
+ assert(align(my_struct) == align(struct { x: int, y: int }));
+ assert(align(my_struct_ptr) == align(*struct { x: int, y: int }));
+ assert(align(my_tagged) == align((int | void)));
+};
+
export fn main() void = {
alias_builtin();
alias_array();
@@ -112,4 +130,5 @@ export fn main() void = {
alias_tagged();
alias_alias();
recursive();
+ measurement();
};
diff --git a/tests/08-slices.ha b/tests/08-slices.ha
@@ -51,8 +51,10 @@ fn casting() void = {
fn measurements() void = {
let x: []int = [1, 2, 3, 4, 5];
assert(size([]int) == size(*[*]int) + size(size) * 2);
+ assert(align([]int) == (if (align(*[*]int) > align(size)) align(*[*]int)
+ else align(size)));
assert(len(x) == 5);
- assert(&x: uintptr: size % size(int) == 0);
+ assert(&x[0]: uintptr: size % size(int) == 0);
};
fn indexing() void = {
diff --git a/tests/09-funcs.ha b/tests/09-funcs.ha
@@ -77,6 +77,9 @@ fn reject() void = {
"fn f(x: [*]int) void = void;",
// return value of undefined size
"fn f(x: int) [*]int = void;",
+
+ "let x: size = size(fn(x: int) int);",
+ "let x: size = align(fn(x: int) int);",
];
for (let i = 0z; i < len(failures); i += 1) {
diff --git a/tests/13-tagged.ha b/tests/13-tagged.ha
@@ -2,16 +2,17 @@ use rt::{compile, exited, EXIT_SUCCESS};
fn measurements() void = {
const x: (u8 | u16 | u32 | u64) = 1337u16; // With padding
- const align: size =
+ const alignment: size =
if (size(u64) < size(uint)) size(uint)
else size(u64);
- assert(size((u8 | u16 | u32 | u64)) == align * 2);
+ assert(align((u8 | u16 | u32 | u64)) == alignment);
+ assert(size((u8 | u16 | u32 | u64)) == alignment * 2);
assert(&x: uintptr: size % size(uint) == 0);
assert(&x: uintptr: size % size(u64) == 0);
const y: (u8 | u16) = 1337u16; // No padding
- assert(&y: uintptr: size % size(uint) == 0);
- assert(&y: uintptr: size % size(u16) == 0);
+ assert(align((u8 | u16)) == align(uint));
+ assert(align(((u8 | u16) | (i8 | i16))) == align(uint));
};
fn storage() void = {
diff --git a/tests/15-enums.ha b/tests/15-enums.ha
@@ -55,7 +55,12 @@ let global_uintptr: uintptr_storage = uintptr_storage::FOO;
fn storage() void = {
assert(size(explicit_values) == size(int));
- assert(size(with_storage) == 2);
+ assert(size(with_storage) == size(u16));
+ assert(size(uintptr_storage) == size(uintptr));
+ assert(align(explicit_values) == align(int));
+ assert(align(with_storage) == align(u16));
+ assert(align(uintptr_storage) == align(uintptr));
+
const val = 0xBEEFu16;
const is_little = (&val: *[2]u8)[0] == 0xEF;
assert(with_storage::CAFE: u8 == (if (is_little) 0xFEu8 else 0xCAu8));
diff --git a/tests/21-tuples.ha b/tests/21-tuples.ha
@@ -5,6 +5,9 @@ def CONST: (int, str) = (15, "foo");
fn storage() void = {
let x: (int, size) = (42, 1337);
assert(size((int, size)) == size(size) * 2);
+ assert(size((int, (u8, size))) == size(size) * 3);
+ assert(align((int, size)) == align(size));
+ assert(align((int, (u8, size))) == align(size));
let ptr = &x: *struct { i: int, z: size };
assert(ptr.i == 42 && ptr.z == 1337);
};
diff --git a/tests/23-errors.ha b/tests/23-errors.ha
@@ -68,9 +68,33 @@ fn void_assignability() void = {
`) as exited != EXIT_SUCCESS); // non-void types cannot be assigned to void
};
+fn measurements() void = {
+ assert(size(!int) == size(int));
+ assert(size(!f64) == size(f64));
+ assert(size(!(int | void)) == size((int | void)));
+ assert(size(!(i8, rune)) == size((i8, rune)));
+ assert(size(!struct { x: int, y: str }) == size(struct { x: int, y: str }));
+ assert(size(!union { x: int, y: str }) == size(union { x: int, y: str }));
+ assert(size(![2]int) == size([2]int));
+ assert(size(![]int) == size([]int));
+ assert(size(!*size) == size(*size));
+
+ assert(align(!int) == align(int));
+ assert(align(!f64) == align(f64));
+ assert(align(!(int | void)) == align((int | void)));
+ assert(align(!(i8, rune)) == align((i8, rune)));
+ assert(align(!struct { x: int, y: str }) == align(struct { x: int, y: str }));
+ assert(align(!union { x: int, y: str }) == align(union { x: int, y: str }));
+ assert(align(![2]int) == align([2]int));
+ assert(align(![*]int) == align([*]int));
+ assert(align(![]int) == align([]int));
+ assert(align(!*size) == align(*size));
+};
+
export fn main() void = {
assignability();
propagate();
cannotignore();
void_assignability();
+ measurements();
};
diff --git a/tests/34-declarations.ha b/tests/34-declarations.ha
@@ -162,13 +162,19 @@ type mut_ptuple_A1 = *(mut_ptuple_A2, u8), mut_ptuple_A2 = (mut_ptuple_A1, u8);
type mut_ptuple_B2 = (mut_ptuple_B1, u8), mut_ptuple_B1 = *(mut_ptuple_B2, u8); // reverse
// type with a type dimension dependency
-type arri8_A = [size(arrintptr)]u8, arrintptr = [8]*int;
-type arrintptr_B = [8]*int, arri8_B = [size(arrintptr)]u8; // reverse
+type arri8_A = [size(arrintptr_A)]u8, arrintptr_A = [8]*int;
+type arrintptr_B = [8]*int, arri8_B = [size(arrintptr_B)]u8; // reverse
+
+type arri8_al_A = [align(arrintptr_al_A)]u8, arrintptr_al_A = [8]*int;
+type arrintptr_al_B = [8]*int, arri8_al_B = [align(arrintptr_al_B)]u8; // reverse
// mutually recursive types with a dimension dependency
type arru8_A = [size(arru8ptr_A)]u8, arru8ptr_A = [8]*arru8_A;
type arru8ptr_B = [8]*arru8_B, arru8_B = [size(arru8ptr_B)]u8; // reverse
+type arru8_al_A = [align(arru8ptr_al_A)]u8, arru8ptr_al_A = [8]*arru8_al_A;
+type arru8ptr_al_B = [8]*arru8_al_B, arru8_al_B = [align(arru8ptr_al_B)]u8; // reverse
+
// unwrapped aliases to tagged unions
type unwrap_A1 = ([32]u8 | void | str),
unwrap_A2 = (i64 | ...unwrap_A1),
@@ -182,6 +188,7 @@ type unwrap_alias_B3 = ...unwrap_alias_B2, // reverse
unwrap_B1 = ([32]u8 | void | str);
fn sz() void = {
+ // size
static assert(size(mut_struct_A3) == 2 * size(*void));
static assert(size(mut_struct_B3) == 2 * size(*void));
@@ -205,6 +212,31 @@ fn sz() void = {
static assert(size(unwrap_A1) == size(unwrap_alias_A3));
static assert(size(unwrap_B1) == size(unwrap_alias_B3));
+
+ //align
+ static assert(align(mut_struct_A3) == align(*void));
+ static assert(align(mut_struct_B3) == align(*void));
+
+ static assert(align(mut_tagged_A1) == align(*void));
+ static assert(align(mut_tagged_B1) == align(*void));
+
+ static assert(align(mut_tagged_A3) == align(*void));
+ static assert(align(mut_tagged_B3) == align(*void));
+
+ static assert(align(mut_tagged_A4) == align(*void));
+ static assert(align(mut_tagged_B4) == align(*void));
+
+ static assert(align(mut_tuple_A1) == align(*void));
+ static assert(align(mut_tuple_B1) == align(*void));
+
+ static assert(align(arru8_A) == align(u8));
+ static assert(align(arru8_B) == align(u8));
+
+ static assert(align(arru8ptr_A) == align(*void));
+ static assert(align(arru8ptr_B) == align(*void));
+
+ static assert(align(unwrap_A1) == align(unwrap_alias_A3));
+ static assert(align(unwrap_B1) == align(unwrap_alias_B3));
};
fn reject() void = {
diff --git a/tests/35-floats.ha b/tests/35-floats.ha
@@ -1,3 +1,8 @@
+fn measurements() void = {
+ assert(size(f32) == align(f32));
+ assert(size(f32) == align(f32));
+};
+
fn signed_casts() void = {
let a = 20f64;
let f = a: f32;
@@ -109,6 +114,7 @@ fn unsigned_casts() void = {
export fn main() void = {
// TODO: test parsing, compile-time evaluation
+ measurements();
signed_casts();
unsigned_casts();
};