Skip to content

Commit

Permalink
refact(all): remove promotion support for numeric types (#53)
Browse files Browse the repository at this point in the history
From now on, explicit casts are required for operations with distinct number types.

Also, integer literals default to the value `int` if no integer type is expected, otherwise such type will be used.
  • Loading branch information
StunxFS authored Dec 14, 2023
1 parent 8e3b9cd commit df12863
Show file tree
Hide file tree
Showing 22 changed files with 47 additions and 154 deletions.
5 changes: 5 additions & 0 deletions lib/core/src/StaticBuffer.c.ri
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ struct StaticBuffer {
return unsafe { libc.strtoul(&self.buf[0], none, 10) };
}

pub func as_uint(&self) -> uint {
self.buf[self.len] = 0;
return unsafe { @as(uint, libc.strtoul(&self.buf[0], none, 10)) };
}

pub func as_int(&self) -> int {
self.buf[self.len] = 0;
return unsafe { @as(int, libc.strtol(&self.buf[0], none, 10))};
Expand Down
2 changes: 1 addition & 1 deletion lib/core/src/StringFormatter.ri
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ pub struct StringFormatter {
);
}
}
index := buf.as_uint64();
index := buf.as_uint();
if index >= args.len {
runtime_error(
"string.fmt: argument index out of range (index: {}, len: {})",
Expand Down
2 changes: 1 addition & 1 deletion lib/core/src/int.ri
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ extend int32 < Stringable {
while n > 0 {
n1 := @as(int32, n / 100);
d = @as(uint32, @as(int32, n) - (n1 * 100)) << 1;
n = n1;
n = @as(int64, n1);
buf[index] = digitPairs.ptr[d];
index -= 1;
d += 1;
Expand Down
2 changes: 1 addition & 1 deletion lib/core/src/string.c.ri
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ pub struct string < Stringable, Hashable, Throwable {

#[inline]
pub func hash(self) -> uint {
return sum64_string(self);
return @as(uint, sum64_string(self));
}

#[inline]
Expand Down
6 changes: 3 additions & 3 deletions lib/core/src/wyhash.c.ri
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import c;

#[unsafe; inline]
func wyhash(key: &uint8, len: uint64, seed: uint64) -> uint64 {
func wyhash(key: &uint8, len: uint, seed: uint64) -> uint64 {
return unsafe { c.wyhash(key, len, seed, &c._wyp[0]) };
}

Expand All @@ -16,10 +16,10 @@ func wyhash64(a: uint64, b: uint64) -> uint64 {

#[inline]
pub func sum64(key: []uint8, seed: uint64 := 0) -> uint64 {
return unsafe { wyhash(&key[0], @as(uint64, key.len), seed) };
return unsafe { wyhash(&key[0], key.len, seed) };
}

#[inline]
pub func sum64_string(key: string, seed: uint64 := 0) -> uint64 {
return unsafe { wyhash(key.ptr, @as(uint64, key.len), seed) };
return unsafe { wyhash(key.ptr, key.len, seed) };
}
7 changes: 1 addition & 6 deletions lib/core/tests/string_test.ri
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,7 @@ test "string.parse_int()" {
@assert("9223372036854775807".parse_int(10, 64)! == 9223372036854775807);
@assert("baobab".parse_int(36, 64)! == 123314438);

// invalid bit sizes
@assert(if _ := "123".parse_int(10, -1) {
false
} else {
true
});
// invalid bit size
@assert(if _ := "123".parse_int(10, 65) {
false
} else {
Expand Down
2 changes: 1 addition & 1 deletion lib/rivet/src/ast/Table.ri
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ pub struct Table {

pub func comptime_number_to_type(self, type: Type) -> Type {
return if type == self.comptime_int_t {
self.int32_t
self.int_t
} else if type == self.comptime_float_t {
self.float64_t
} else {
Expand Down
2 changes: 1 addition & 1 deletion lib/rivet/src/checker/exprs.ri
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@ extend Checker {
);
}
} else {
promoted_type = self.promote(left_type, right_type);
promoted_type = left_type;
if promoted_type is .Void {
report.error(
"mismatched types `{}` and `{}`".fmt(left_type, right_type),
Expand Down
60 changes: 0 additions & 60 deletions lib/rivet/src/checker/types.ri
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ extend Checker {
if self.table.is_comptime_number(expected) || self.table.is_comptime_number(got) {
return true;
}
return self.promote_number(expected, got) == expected;
}

match expected_sym.info {
Expand Down Expand Up @@ -192,65 +191,6 @@ extend Checker {

return expected == got;
}

func promote(self, left_type: ast.Type, right_type: ast.Type) -> ast.Type {
if left_type == right_type {
return left_type;
} else if self.table.is_number(left_type) && self.table.is_number(right_type) {
return self.promote_number(left_type, right_type);
}
return left_type;
}

func promote_number(self, expected: ast.Type, got: ast.Type) -> ast.Type {
mut type_hi := expected;
mut type_lo := got;
mut bits_hi := self.table.number_bits(type_hi);
mut bits_lo := self.table.number_bits(type_lo);
if bits_hi < bits_lo {
old_hi := type_hi;
type_hi = type_lo;
type_lo = old_hi;

old_bhi := bits_hi;
bits_hi = bits_lo;
bits_lo = old_bhi;
}

return if self.table.is_float(type_hi) {
if self.table.is_float(type_lo) {
// float -> float (good)
type_hi
} else {
// float -> int (bad)
.Void
}
} else {
is_signed_lo := self.table.is_signed_int(type_lo);
is_unsigned_lo := !is_signed_lo;

is_signed_hi := self.table.is_signed_int(type_hi);
is_unsigned_hi := !is_signed_hi;

if is_unsigned_lo && is_unsigned_hi {
// unsigned number -> unsigned number (good)
type_hi
} else if is_signed_lo && is_signed_hi {
// signed number -> signed number (good)
if bits_lo == 64 && bits_hi != 64 {
type_lo
} else {
type_hi
}
} else if is_unsigned_lo && is_signed_hi && bits_hi > bits_lo {
// unsigned number -> signed number (good, if signed type is larger)
type_lo
} else {
// signed number -> unsigned number (bad)
.Void
}
};
}
}

func check_pointer(expected: ast.Type, got: ast.Type) -> bool {
Expand Down
2 changes: 1 addition & 1 deletion lib/rivet/tests/tokenizer.ri
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import rivet/prefs;
import rivet/token;
import rivet/tokenizer;
import rivet/ast.{ Table };
import rivet/ast.Table;

var prefs_ := prefs.Prefs();

Expand Down
2 changes: 1 addition & 1 deletion lib/std/src/hash/fnv1a/mod.ri
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ pub func sum64_string(data: string) -> uint64 {
mut hash := FNV64_OFFSET_BASIS;
mut i: uint := 0;
while i < data.len : i += 1 {
hash = (hash ^ @as(uint32, data[i])) * FNV64_PRIME;
hash = (hash ^ @as(uint64, data[i])) * FNV64_PRIME;
}
return hash;
}
8 changes: 4 additions & 4 deletions lib/std/src/semver/parse.ri
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
// Use of this source code is governed by an MIT license that can
// be found in the LICENSE file.

const VER_MAJOR: uint64 := 0;
const VER_MINOR: uint64 := 1;
const VER_PATCH: uint64 := 2;
const VER_MAJOR: uint := 0;
const VER_MINOR: uint := 1;
const VER_PATCH: uint := 2;
var versions := [VER_MAJOR, VER_MINOR, VER_PATCH];

struct RawVersion {
Expand Down Expand Up @@ -40,7 +40,7 @@ struct RawVersion {
}

#[inline]
func is_missing(&self, typ: uint64) -> bool {
func is_missing(&self, typ: uint) -> bool {
return typ >= self.raw_ints.len - 1;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/std/src/strings/TextScanner.ri
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ pub struct TextScanner {
/// of the input text.
/// NOTE: after `c := ts.next()`, `ts.current()` will also return `c`.
#[inline]
pub func current(self) -> ?uint {
pub func current(self) -> ?uint8 {
if self.pos > 0 {
return self.input[self.pos - 1];
}
Expand Down
6 changes: 3 additions & 3 deletions lib/std/tests/semver_test.ri
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import std/semver;

struct TestVersion {
raw: string;
major: uint64;
minor: uint64;
patch: uint64;
major: uint;
minor: uint;
patch: uint;
prerelease: string;
metadata: string;
}
Expand Down
2 changes: 1 addition & 1 deletion rivetc/src/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ def is_comptime_number(self, typ):

def comptime_number_to_type(self, typ):
if typ == self.comptime_int_t:
return self.int32_t
return self.int_t
elif typ == self.comptime_float_t:
return self.float64_t
return typ
Expand Down
54 changes: 5 additions & 49 deletions rivetc/src/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,10 @@ def check_expr(self, expr):
expr.typ = self.comp.rune_t
return expr.typ
elif isinstance(expr, ast.IntegerLiteral):
expr.typ = self.comp.comptime_int_t
if self.comp.is_number(self.expected_type):
expr.typ = self.expected_type
else:
expr.typ = self.comp.comptime_int_t
return expr.typ
elif isinstance(expr, ast.FloatLiteral):
expr.typ = self.comp.comptime_float_t
Expand Down Expand Up @@ -687,7 +690,7 @@ def check_expr(self, expr):
expr.pos
)
else:
promoted_type = self.promote(ltyp, rtyp)
promoted_type = ltyp
if promoted_type == self.comp.void_t:
report.error(
f"mismatched types `{ltyp}` and `{rtyp}`", expr.pos
Expand Down Expand Up @@ -1838,7 +1841,6 @@ def check_compatible_types(self, got, expected):
expected
) or self.comp.is_comptime_number(got):
return True
return self.promote_number(expected, got) == expected
elif exp_sym.kind == TypeKind.Trait:
if self.comp.comptime_number_to_type(got).symbol(
) in exp_sym.info.implements:
Expand Down Expand Up @@ -1876,52 +1878,6 @@ def check_pointer(self, expected, got):
return False
return expected.typ == got.typ

def promote(self, left_typ, right_typ):
if left_typ == right_typ:
return left_typ
elif self.comp.is_number(left_typ) and self.comp.is_number(right_typ):
return self.promote_number(left_typ, right_typ)
return left_typ

def promote_number(self, expected, got):
type_hi = expected
type_lo = got
bits_hi = self.comp.num_bits(type_hi)
bits_lo = self.comp.num_bits(type_lo)
if bits_hi < bits_lo:
old_hi = type_hi
type_hi = type_lo
type_lo = old_hi

old_bhi = bits_hi
bits_hi = bits_lo
bits_lo = old_bhi

if self.comp.is_float(type_hi):
if self.comp.is_float(type_lo):
# float -> float (good)
return type_hi
# float -> int (bad)
return self.comp.void_t

is_signed_lo = self.comp.is_signed_int(type_lo)
is_unsigned_lo = not is_signed_lo
is_signed_hi = self.comp.is_signed_int(type_hi)
is_unsigned_hi = not is_signed_hi

if is_unsigned_lo and is_unsigned_hi:
# unsigned number -> unsigned number (good)
return type_hi
elif is_signed_lo and is_signed_hi:
# signed number -> signed number (good)
return type_lo if bits_lo == 64 and is_signed_lo else type_hi
elif is_unsigned_lo and is_signed_hi and bits_lo < bits_hi:
# unsigned number -> signed number (good, if signed type is larger)
return type_lo
else:
# signed number -> unsigned number (bad)
return self.comp.void_t

def check_expr_is_mut(self, expr, from_assign = False):
if isinstance(expr, ast.ParExpr):
self.check_expr_is_mut(expr.expr)
Expand Down
2 changes: 1 addition & 1 deletion rivetc/src/codegen/c_headers.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
typedef uint64_t uint64;
typedef ptrdiff_t ri_int;
typedef size_t ri_uint;
typedef ri_int comptime_int; // TODO: remove
typedef ri_int comptime_int;
typedef uint8 bool;
typedef uint32 rune;
Expand Down
2 changes: 1 addition & 1 deletion tests/b_invalid/checking_trait.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
tests/b_invalid/checking_trait.ri:24:14: error: type `int32` does not implement trait `ToStr`
tests/b_invalid/checking_trait.ri:24:14: error: type `int` does not implement trait `ToStr`
24 | my_print(1); // FAIL
| ^
= note: in argument `v` of function `my_print`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
tests/invalid/checking_trait.ri:2:18: error: cannot take the address of a boxed value as receiver
2 | func to_str1(&self) -> string;
| ^~~~~
tests/invalid/checking_trait.ri:10:5-12:5: error: type `checking_trait.MyStruct1` incorrectly implements method `to_str1` of trait `checking_trait.ToStr`
tests/invalid/invalid_trait_implementation.ri:10:5-12:5: error: type `invalid_trait_implementation.MyStruct1` incorrectly implements method `to_str1` of trait `invalid_trait_implementation.ToStr`
| /~~ from here
10 | func to_str1(self) -> string {
11 | return self;
Expand All @@ -11,35 +8,35 @@ tests/invalid/checking_trait.ri:10:5-12:5: error: type `checking_trait.MyStruct1
= note: receiver `self` should be a reference, or use the attribute
`boxed`.
= note: ====== method signature for `to_str1` ======
= note: trait `ToStr` has `func(&self) -> string`
= note: trait `ToStr` has `func(self) -> string`
= note: type `MyStruct1` has `func(self) -> string`
tests/invalid/checking_trait.ri:11:16: error: expected type `string`, found `MyStruct1`
tests/invalid/invalid_trait_implementation.ri:11:16: error: expected type `string`, found `MyStruct1`
11 | return self;
| ^~~~
= note: in return of method `to_str1`
tests/invalid/checking_trait.ri:16:5-18:5: error: type `checking_trait.MyStruct2` incorrectly implements method `to_str1` of trait `checking_trait.ToStr`
tests/invalid/invalid_trait_implementation.ri:16:5-18:5: error: type `invalid_trait_implementation.MyStruct2` incorrectly implements method `to_str1` of trait `invalid_trait_implementation.ToStr`
| /~~ from here
16 | func to_str1(&self) -> string {
17 | return if self.* { "true" } else { "false" };
18 | }
| ^~~ to here
= note: method `to_str1` should be public.
= note: ====== method signature for `to_str1` ======
= note: trait `ToStr` has `func(&self) -> string`
= note: trait `ToStr` has `func(self) -> string`
= note: type `MyStruct2` has `func(&self) -> string`
tests/invalid/checking_trait.ri:17:19: error: non-boolean expression used as `if` condition
tests/invalid/invalid_trait_implementation.ri:17:19: error: non-boolean expression used as `if` condition
17 | return if self.* { "true" } else { "false" };
| ^~~~~~
tests/invalid/checking_trait.ri:22:14: error: type `string` does not implement trait `ToStr`
tests/invalid/invalid_trait_implementation.ri:22:14: error: type `string` does not implement trait `ToStr`
22 | my_print(""); // OK
| ^~
= note: in argument `v` of function `my_print`
tests/invalid/checking_trait.ri:23:14: error: type `bool` does not implement trait `ToStr`
tests/invalid/invalid_trait_implementation.ri:23:14: error: type `bool` does not implement trait `ToStr`
23 | my_print(true); // OK
| ^~~~
= note: in argument `v` of function `my_print`
tests/invalid/checking_trait.ri:24:14: error: type `int32` does not implement trait `ToStr`
tests/invalid/invalid_trait_implementation.ri:24:14: error: type `int` does not implement trait `ToStr`
24 | my_print(1); // FAIL
| ^
= note: in argument `v` of function `my_print`
rivet: error: could not compile module `checking_trait`, aborting due to 8 previous errors
rivet: error: could not compile module `invalid_trait_implementation`, aborting due to 7 previous errors
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
trait ToStr {
func to_str1(&self) -> string;
func to_str1(self) -> string;
}

func my_print(v: ToStr) {
Expand Down
Loading

0 comments on commit df12863

Please sign in to comment.