aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compile.c75
-rw-r--r--environment.c32
-rw-r--r--examples/vectors/vectors.tm4
-rw-r--r--stdlib/integers.c4
-rw-r--r--stdlib/integers.h2
-rw-r--r--stdlib/nums.c8
-rw-r--r--stdlib/optionals.c2
-rw-r--r--stdlib/stdlib.c18
-rw-r--r--test/minmax.tm2
-rw-r--r--test/nums.tm42
-rw-r--r--tomo.c2
-rw-r--r--typecheck.c32
-rw-r--r--types.c9
13 files changed, 173 insertions, 59 deletions
diff --git a/compile.c b/compile.c
index 2d07c117..0b19e48f 100644
--- a/compile.c
+++ b/compile.c
@@ -32,6 +32,8 @@ static CORD compile_unsigned_type(type_t *t);
static CORD promote_to_optional(type_t *t, CORD code);
static CORD compile_null(type_t *t);
static CORD compile_to_type(env_t *env, ast_t *ast, type_t *t);
+static CORD check_null(type_t *t, CORD value);
+static CORD optional_into_nonnull(type_t *t, CORD value);
CORD promote_to_optional(type_t *t, CORD code)
{
@@ -54,7 +56,7 @@ CORD promote_to_optional(type_t *t, CORD code)
}
}
-static bool promote(env_t *env, CORD *code, type_t *actual, type_t *needed)
+static bool promote(env_t *env, ast_t *ast, CORD *code, type_t *actual, type_t *needed)
{
if (type_eq(actual, needed))
return true;
@@ -68,6 +70,18 @@ static bool promote(env_t *env, CORD *code, type_t *actual, type_t *needed)
return true;
}
+ // Automatic optional checking for nums:
+ if (needed->tag == NumType && actual->tag == OptionalType && Match(actual, OptionalType)->type->tag == NumType) {
+ *code = CORD_all("({ ", compile_declaration(actual, "opt"), " = ", *code, "; ",
+ "if (", check_null(actual, "opt"), ")\n",
+ CORD_asprintf("fail_source(%r, %ld, %ld, \"This value was expected to be non-null, but it's null!\");\n",
+ CORD_quoted(ast->file->filename),
+ (long)(ast->start - ast->file->text),
+ (long)(ast->end - ast->file->text)),
+ optional_into_nonnull(actual, "opt"), "; })");
+ return true;
+ }
+
if (actual->tag == IntType && needed->tag == BigIntType) {
*code = CORD_all("I(", *code, ")");
return true;
@@ -89,7 +103,7 @@ static bool promote(env_t *env, CORD *code, type_t *actual, type_t *needed)
binding_t *b = get_binding(Match(needed, EnumType)->env, tag);
assert(b && b->type->tag == FunctionType);
// Single-value enum constructor:
- if (!promote(env, code, actual, Match(b->type, FunctionType)->args->type))
+ if (!promote(env, ast, code, actual, Match(b->type, FunctionType)->args->type))
return false;
*code = CORD_all(b->code, "(", *code, ")");
return true;
@@ -105,7 +119,7 @@ static bool promote(env_t *env, CORD *code, type_t *actual, type_t *needed)
if (actual->tag == PointerType
&& can_promote(Match(actual, PointerType)->pointed, needed)) {
*code = CORD_all("*(", *code, ")");
- return promote(env, code, Match(actual, PointerType)->pointed, needed);
+ return promote(env, ast, code, Match(actual, PointerType)->pointed, needed);
}
// Stack ref promotion:
@@ -376,7 +390,7 @@ static CORD compile_inline_block(env_t *env, ast_t *ast)
return code;
}
-static CORD optional_into_nonnull(type_t *t, CORD value)
+CORD optional_into_nonnull(type_t *t, CORD value)
{
if (t->tag == OptionalType) t = Match(t, OptionalType)->type;
switch (t->tag) {
@@ -391,7 +405,7 @@ static CORD optional_into_nonnull(type_t *t, CORD value)
}
}
-static CORD check_null(type_t *t, CORD value)
+CORD check_null(type_t *t, CORD value)
{
t = Match(t, OptionalType)->type;
if (t->tag == PointerType || t->tag == FunctionType || t->tag == CStringType
@@ -539,7 +553,7 @@ CORD compile_statement(env_t *env, ast_t *ast)
type_t *t = get_type(env, decl->value);
CORD val_code = compile_maybe_incref(env, decl->value);
if (t->tag == FunctionType) {
- assert(promote(env, &val_code, t, Type(ClosureType, t)));
+ assert(promote(env, decl->value, &val_code, t, Type(ClosureType, t)));
t = Type(ClosureType, t);
}
return CORD_asprintf(
@@ -627,7 +641,7 @@ CORD compile_statement(env_t *env, ast_t *ast)
CORD val_code = compile_maybe_incref(env, decl->value);
if (t->tag == FunctionType) {
- assert(promote(env, &val_code, t, Type(ClosureType, t)));
+ assert(promote(env, decl->value, &val_code, t, Type(ClosureType, t)));
t = Type(ClosureType, t);
}
@@ -670,10 +684,10 @@ CORD compile_statement(env_t *env, ast_t *ast)
type_t *lhs_t = get_type(env, update->lhs);
type_t *rhs_t = get_type(env, update->rhs);
- if (!promote(env, &rhs, rhs_t, lhs_t)) {
+ if (!promote(env, update->rhs, &rhs, rhs_t, lhs_t)) {
if (update->rhs->tag == Int && (lhs_t->tag == IntType || lhs_t->tag == ByteType))
rhs = compile_int_to_type(env, update->rhs, lhs_t);
- else if (!(lhs_t->tag == ArrayType && promote(env, &rhs, rhs_t, Match(lhs_t, ArrayType)->item_type)))
+ else if (!(lhs_t->tag == ArrayType && promote(env, update->rhs, &rhs, rhs_t, Match(lhs_t, ArrayType)->item_type)))
code_err(ast, "I can't do operations between %T and %T", lhs_t, rhs_t);
}
@@ -755,7 +769,7 @@ CORD compile_statement(env_t *env, ast_t *ast)
return CORD_all(lhs, " = Texts(", lhs, ", ", rhs, ");");
} else if (lhs_t->tag == ArrayType) {
CORD padded_item_size = CORD_asprintf("%ld", type_size(Match(lhs_t, ArrayType)->item_type));
- if (promote(env, &rhs, rhs_t, Match(lhs_t, ArrayType)->item_type)) {
+ if (promote(env, update->rhs, &rhs, rhs_t, Match(lhs_t, ArrayType)->item_type)) {
// arr ++= item
if (update->lhs->tag == Var)
return CORD_all("Array$insert(&", lhs, ", stack(", rhs, "), I(0), ", padded_item_size, ");");
@@ -1547,7 +1561,7 @@ CORD compile_to_type(env_t *env, ast_t *ast, type_t *t)
return compile_null(t);
CORD code = compile(env, ast);
type_t *actual = get_type(env, ast);
- if (!promote(env, &code, actual, t))
+ if (!promote(env, ast, &code, actual, t))
code_err(ast, "I expected a %T here, but this is a %T", t, actual);
return code;
}
@@ -1572,7 +1586,7 @@ CORD compile_int_to_type(env_t *env, ast_t *ast, type_t *target)
if (ast->tag != Int) {
CORD code = compile(env, ast);
type_t *actual_type = get_type(env, ast);
- if (!promote(env, &code, actual_type, target))
+ if (!promote(env, ast, &code, actual_type, target))
code = CORD_all(type_to_cord(actual_type), "_to_", type_to_cord(target), "(", code, ", no)");
return code;
}
@@ -1659,7 +1673,7 @@ CORD compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t
env_t *arg_env = with_enum_scope(env, spec_arg->type);
type_t *actual_t = get_type(arg_env, call_arg->value);
value = compile_maybe_incref(arg_env, call_arg->value);
- if (!promote(arg_env, &value, actual_t, spec_arg->type))
+ if (!promote(arg_env, call_arg->value, &value, actual_t, spec_arg->type))
code_err(call_arg->value, "This argument is supposed to be a %T, but this value is a %T", spec_arg->type, actual_t);
}
Table$str_set(&used_args, call_arg->name, call_arg);
@@ -1688,7 +1702,7 @@ CORD compile_arguments(env_t *env, ast_t *call_ast, arg_t *spec_args, arg_ast_t
env_t *arg_env = with_enum_scope(env, spec_arg->type);
type_t *actual_t = get_type(arg_env, call_arg->value);
value = compile_maybe_incref(arg_env, call_arg->value);
- if (!promote(arg_env, &value, actual_t, spec_arg->type))
+ if (!promote(arg_env, call_arg->value, &value, actual_t, spec_arg->type))
code_err(call_arg->value, "This argument is supposed to be a %T, but this value is a %T", spec_arg->type, actual_t);
}
@@ -2050,6 +2064,13 @@ CORD compile(env_t *env, ast_t *ast)
}
}
+ bool lhs_is_optional_num = (lhs_t->tag == OptionalType && Match(lhs_t, OptionalType)->type->tag == NumType);
+ if (lhs_is_optional_num)
+ lhs_t = Match(lhs_t, OptionalType)->type;
+ bool rhs_is_optional_num = (rhs_t->tag == OptionalType && Match(rhs_t, OptionalType)->type->tag == NumType);
+ if (rhs_is_optional_num)
+ rhs_t = Match(rhs_t, OptionalType)->type;
+
CORD lhs, rhs;
if (lhs_t->tag == BigIntType && rhs_t->tag != BigIntType && is_numeric_type(rhs_t) && binop->lhs->tag == Int) {
lhs = compile_int_to_type(env, binop->lhs, rhs_t);
@@ -2065,9 +2086,9 @@ CORD compile(env_t *env, ast_t *ast)
}
type_t *operand_t;
- if (promote(env, &rhs, rhs_t, lhs_t))
+ if (promote(env, binop->rhs, &rhs, rhs_t, lhs_t))
operand_t = lhs_t;
- else if (promote(env, &lhs, lhs_t, rhs_t))
+ else if (promote(env, binop->lhs, &lhs, lhs_t, rhs_t))
operand_t = rhs_t;
else
code_err(ast, "I can't do operations between %T and %T", lhs_t, rhs_t);
@@ -2136,6 +2157,8 @@ CORD compile(env_t *env, ast_t *ast)
case BigIntType:
return CORD_all("Int$equal_value(", lhs, ", ", rhs, ")");
case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType:
+ if (lhs_is_optional_num || rhs_is_optional_num)
+ return CORD_asprintf("generic_equal(stack(%r), stack(%r), %r)", lhs, rhs, compile_type_info(env, Type(OptionalType, operand_t)));
return CORD_all("(", lhs, " == ", rhs, ")");
default:
return CORD_asprintf("generic_equal(stack(%r), stack(%r), %r)", lhs, rhs, compile_type_info(env, operand_t));
@@ -2146,6 +2169,8 @@ CORD compile(env_t *env, ast_t *ast)
case BigIntType:
return CORD_all("!Int$equal_value(", lhs, ", ", rhs, ")");
case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType:
+ if (lhs_is_optional_num || rhs_is_optional_num)
+ return CORD_asprintf("!generic_equal(stack(%r), stack(%r), %r)", lhs, rhs, compile_type_info(env, Type(OptionalType, operand_t)));
return CORD_all("(", lhs, " != ", rhs, ")");
default:
return CORD_asprintf("!generic_equal(stack(%r), stack(%r), %r)", lhs, rhs, compile_type_info(env, operand_t));
@@ -2156,6 +2181,8 @@ CORD compile(env_t *env, ast_t *ast)
case BigIntType:
return CORD_all("(Int$compare_value(", lhs, ", ", rhs, ") < 0)");
case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType:
+ if (lhs_is_optional_num || rhs_is_optional_num)
+ return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) < 0)", lhs, rhs, compile_type_info(env, Type(OptionalType, operand_t)));
return CORD_all("(", lhs, " < ", rhs, ")");
default:
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) < 0)", lhs, rhs, compile_type_info(env, operand_t));
@@ -2166,6 +2193,8 @@ CORD compile(env_t *env, ast_t *ast)
case BigIntType:
return CORD_all("(Int$compare_value(", lhs, ", ", rhs, ") <= 0)");
case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType:
+ if (lhs_is_optional_num || rhs_is_optional_num)
+ return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) <= 0)", lhs, rhs, compile_type_info(env, Type(OptionalType, operand_t)));
return CORD_all("(", lhs, " <= ", rhs, ")");
default:
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) <= 0)", lhs, rhs, compile_type_info(env, operand_t));
@@ -2176,6 +2205,8 @@ CORD compile(env_t *env, ast_t *ast)
case BigIntType:
return CORD_all("(Int$compare_value(", lhs, ", ", rhs, ") > 0)");
case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType:
+ if (lhs_is_optional_num || rhs_is_optional_num)
+ return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) > 0)", lhs, rhs, compile_type_info(env, Type(OptionalType, operand_t)));
return CORD_all("(", lhs, " > ", rhs, ")");
default:
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) > 0)", lhs, rhs, compile_type_info(env, operand_t));
@@ -2186,6 +2217,8 @@ CORD compile(env_t *env, ast_t *ast)
case BigIntType:
return CORD_all("(Int$compare_value(", lhs, ", ", rhs, ") >= 0)");
case BoolType: case ByteType: case IntType: case NumType: case PointerType: case FunctionType:
+ if (lhs_is_optional_num || rhs_is_optional_num)
+ return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) >= 0)", lhs, rhs, compile_type_info(env, Type(OptionalType, operand_t)));
return CORD_all("(", lhs, " >= ", rhs, ")");
default:
return CORD_asprintf("(generic_compare(stack(%r), stack(%r), %r) >= 0)", lhs, rhs, compile_type_info(env, operand_t));
@@ -2200,6 +2233,8 @@ CORD compile(env_t *env, ast_t *ast)
code_err(ast, "The 'and' operator isn't supported for %T and %T", lhs_t, rhs_t);
}
case BINOP_CMP: {
+ if (lhs_is_optional_num || rhs_is_optional_num)
+ operand_t = Type(OptionalType, operand_t);
return CORD_all("generic_compare(stack(", lhs, "), stack(", rhs, "), ", compile_type_info(env, operand_t), ")");
}
case BINOP_OR: {
@@ -2417,7 +2452,7 @@ CORD compile(env_t *env, ast_t *ast)
code_err(ast, "This item type can't be sent over a channel because it contains reference to memory that may not be thread-safe.");
if (chan->max_size) {
CORD max_size = compile(env, chan->max_size);
- if (!promote(env, &max_size, get_type(env, chan->max_size), INT_TYPE))
+ if (!promote(env, chan->max_size, &max_size, get_type(env, chan->max_size), INT_TYPE))
code_err(chan->max_size, "This value must be an integer, not %T", get_type(env, chan->max_size));
return CORD_all("Channel$new(", max_size, ")");
} else {
@@ -3525,7 +3560,7 @@ void compile_namespace(env_t *env, const char *ns_name, ast_t *block)
} else {
CORD val_code = compile_maybe_incref(ns_env, decl->value);
if (t->tag == FunctionType) {
- assert(promote(env, &val_code, t, Type(ClosureType, t)));
+ assert(promote(env, decl->value, &val_code, t, Type(ClosureType, t)));
t = Type(ClosureType, t);
}
@@ -3740,7 +3775,7 @@ CORD compile_file(env_t *env, ast_t *ast)
if (!is_constant(env, decl->value)) {
CORD val_code = compile_maybe_incref(env, decl->value);
if (t->tag == FunctionType) {
- assert(promote(env, &val_code, t, Type(ClosureType, t)));
+ assert(promote(env, decl->value, &val_code, t, Type(ClosureType, t)));
t = Type(ClosureType, t);
}
@@ -3771,7 +3806,7 @@ CORD compile_file(env_t *env, ast_t *ast)
} else {
CORD val_code = compile_maybe_incref(env, decl->value);
if (t->tag == FunctionType) {
- assert(promote(env, &val_code, t, Type(ClosureType, t)));
+ assert(promote(env, decl->value, &val_code, t, Type(ClosureType, t)));
t = Type(ClosureType, t);
}
env->code->staticdefs = CORD_all(
diff --git a/environment.c b/environment.c
index c98d55e4..8be31dc7 100644
--- a/environment.c
+++ b/environment.c
@@ -137,7 +137,7 @@ env_t *new_compilation_unit(CORD libname)
{"power", "Int$power", "func(base:Int,exponent:Int -> Int)"},
{"prev_prime", "Int$prev_prime", "func(x:Int -> Int)"},
{"right_shifted", "Int$right_shifted", "func(x,y:Int -> Int)"},
- {"sqrt", "Int$sqrt", "func(x:Int -> Int)"},
+ {"sqrt", "Int$sqrt", "func(x:Int -> Int?)"},
{"times", "Int$times", "func(x,y:Int -> Int)"},
{"to", "Int$to", "func(from:Int,to:Int -> Range)"},
)},
@@ -219,61 +219,65 @@ env_t *new_compilation_unit(CORD libname)
)},
#define C(name) {#name, "M_"#name, "Num"}
#define F(name) {#name, #name, "func(n:Num -> Num)"}
+#define F_opt(name) {#name, #name, "func(n:Num -> Num?)"}
#define F2(name) {#name, #name, "func(x,y:Num -> Num)"}
{"Num", Type(NumType, .bits=TYPE_NBITS64), "Num_t", "Num$info", TypedArray(ns_entry_t,
{"near", "Num$near", "func(x,y:Num, ratio=1e-9, min_epsilon=1e-9 -> Bool)"},
{"clamped", "Num$clamped", "func(x,low,high:Num -> Num)"},
{"format", "Num$format", "func(n:Num, precision=0 -> Text)"},
{"scientific", "Num$scientific", "func(n:Num,precision=0 -> Text)"},
- {"nan", "Num$nan", "func(tag=\"\" -> Num)"},
{"isinf", "Num$isinf", "func(n:Num -> Bool)"},
{"isfinite", "Num$isfinite", "func(n:Num -> Bool)"},
- {"isnan", "Num$isnan", "func(n:Num -> Bool)"},
C(2_SQRTPI), C(E), C(PI_2), C(2_PI), C(1_PI), C(LN10), C(LN2), C(LOG2E),
C(PI), C(PI_4), C(SQRT2), C(SQRT1_2),
{"INF", "(Num_t)(INFINITY)", "Num"},
+ {"NAN", "((Num_t)NAN)", "Num?"},
{"TAU", "(Num_t)(2.*M_PI)", "Num"},
{"mix", "Num$mix", "func(amount,x,y:Num -> Num)"},
{"parse", "Num$parse", "func(text:Text -> Num?)"},
{"abs", "fabs", "func(n:Num -> Num)"},
- F(acos), F(acosh), F(asin), F(asinh), F(atan), F(atanh), F(cbrt), F(ceil), F(cos), F(cosh), F(erf), F(erfc),
- F(exp), F(exp2), F(expm1), F(floor), F(j0), F(j1), F(log), F(log10), F(log1p), F(log2), F(logb),
- F(rint), F(round), F(significand), F(sin), F(sinh), F(sqrt),
- F(tan), F(tanh), F(tgamma), F(trunc), F(y0), F(y1),
+ F_opt(acos), F_opt(acosh), F_opt(asin), F(asinh), F(atan), F_opt(atanh),
+ F(cbrt), F(ceil), F_opt(cos), F(cosh), F(erf), F(erfc),
+ F(exp), F(exp2), F(expm1), F(floor), F(j0), F(j1), F_opt(log), F_opt(log10), F_opt(log1p),
+ F_opt(log2), F(logb), F(rint), F(round), F(significand), F_opt(sin), F(sinh), F_opt(sqrt),
+ F_opt(tan), F(tanh), F_opt(tgamma), F(trunc), F_opt(y0), F_opt(y1),
F2(atan2), F2(copysign), F2(fdim), F2(hypot), F2(nextafter),
)},
#undef F2
+#undef F_opt
#undef F
#undef C
#define C(name) {#name, "(Num32_t)(M_"#name")", "Num32"}
#define F(name) {#name, #name"f", "func(n:Num32 -> Num32)"}
+#define F_opt(name) {#name, #name"f", "func(n:Num32 -> Num32?)"}
#define F2(name) {#name, #name"f", "func(x,y:Num32 -> Num32)"}
{"Num32", Type(NumType, .bits=TYPE_NBITS32), "Num32_t", "Num32$info", TypedArray(ns_entry_t,
{"near", "Num32$near", "func(x,y:Num32, ratio=Num32(1e-9), min_epsilon=Num32(1e-9) -> Bool)"},
{"clamped", "Num32$clamped", "func(x,low,high:Num32 -> Num32)"},
{"format", "Num32$format", "func(n:Num32, precision=0 -> Text)"},
{"scientific", "Num32$scientific", "func(n:Num32, precision=0 -> Text)"},
- {"nan", "Num32$nan", "func(tag=\"\" -> Num32)"},
{"isinf", "Num32$isinf", "func(n:Num32 -> Bool)"},
{"isfinite", "Num32$isfinite", "func(n:Num32 -> Bool)"},
- {"isnan", "Num32$isnan", "func(n:Num32 -> Bool)"},
C(2_SQRTPI), C(E), C(PI_2), C(2_PI), C(1_PI), C(LN10), C(LN2), C(LOG2E),
C(PI), C(PI_4), C(SQRT2), C(SQRT1_2),
{"INF", "(Num32_t)(INFINITY)", "Num32"},
+ {"NAN", "((Num32_t)NAN)", "Num32?"},
{"TAU", "(Num32_t)(2.f*M_PI)", "Num32"},
{"mix", "Num32$mix", "func(amount,x,y:Num32 -> Num32)"},
{"parse", "Num32$parse", "func(text:Text -> Num32?)"},
{"abs", "fabsf", "func(n:Num32 -> Num32)"},
- F(acos), F(acosh), F(asin), F(asinh), F(atan), F(atanh), F(cbrt), F(ceil), F(cos), F(cosh), F(erf), F(erfc),
- F(exp), F(exp2), F(expm1), F(floor), F(j0), F(j1), F(log), F(log10), F(log1p), F(log2), F(logb),
- F(rint), F(round), F(significand), F(sin), F(sinh), F(sqrt),
- F(tan), F(tanh), F(tgamma), F(trunc), F(y0), F(y1),
- F2(atan2), F2(copysign), F2(fdim), F2(hypot), F2(nextafter), F2(pow), F2(remainder),
+ F_opt(acos), F_opt(acosh), F_opt(asin), F(asinh), F(atan), F_opt(atanh),
+ F(cbrt), F(ceil), F_opt(cos), F(cosh), F(erf), F(erfc),
+ F(exp), F(exp2), F(expm1), F(floor), F(j0), F(j1), F_opt(log), F_opt(log10), F_opt(log1p),
+ F_opt(log2), F(logb), F(rint), F(round), F(significand), F_opt(sin), F(sinh), F_opt(sqrt),
+ F_opt(tan), F(tanh), F_opt(tgamma), F(trunc), F_opt(y0), F_opt(y1),
+ F2(atan2), F2(copysign), F2(fdim), F2(hypot), F2(nextafter),
)},
{"CString", Type(CStringType), "char*", "CString$info", TypedArray(ns_entry_t,
{"as_text", "CString$as_text_simple", "func(str:CString -> Text)"},
)},
#undef F2
+#undef F_opt
#undef F
#undef C
{"Range", RANGE_TYPE, "Range_t", "Range", TypedArray(ns_entry_t,
diff --git a/examples/vectors/vectors.tm b/examples/vectors/vectors.tm
index 696bef1e..b2533be4 100644
--- a/examples/vectors/vectors.tm
+++ b/examples/vectors/vectors.tm
@@ -17,7 +17,7 @@ struct Vec2(x,y:Num):
func divided_by(v:Vec2, divisor:Num->Vec2; inline):
return Vec2(v.x/divisor, v.y/divisor)
func length(v:Vec2->Num; inline):
- return (v.x*v.x + v.y*v.y):sqrt()
+ return (v.x*v.x + v.y*v.y)!:sqrt()
func dist(a,b:Vec2->Num; inline):
return a:minus(b):length()
func angle(v:Vec2->Num; inline):
@@ -50,7 +50,7 @@ struct Vec3(x,y,z:Num):
func divided_by(v:Vec3, divisor:Num->Vec3; inline):
return Vec3(v.x/divisor, v.y/divisor, v.z/divisor)
func length(v:Vec3->Num; inline):
- return (v.x*v.x + v.y*v.y + v.z*v.z):sqrt()
+ return (v.x*v.x + v.y*v.y + v.z*v.z)!:sqrt()
func dist(a,b:Vec3->Num; inline):
return a:minus(b):length()
func norm(v:Vec3->Vec3; inline):
diff --git a/stdlib/integers.c b/stdlib/integers.c
index 48ab6019..3b95c994 100644
--- a/stdlib/integers.c
+++ b/stdlib/integers.c
@@ -300,8 +300,10 @@ public Int_t Int$power(Int_t base, Int_t exponent)
return Int$from_mpz(result);
}
-public Int_t Int$sqrt(Int_t i)
+public OptionalInt_t Int$sqrt(Int_t i)
{
+ if (Int$compare_value(i, I(0)) < 0)
+ return NULL_INT;
mpz_t result;
mpz_init_set_int(result, i);
mpz_sqrt(result, result);
diff --git a/stdlib/integers.h b/stdlib/integers.h
index 430a3eac..7bda6398 100644
--- a/stdlib/integers.h
+++ b/stdlib/integers.h
@@ -106,7 +106,7 @@ OptionalInt_t Int$from_str(const char *str);
OptionalInt_t Int$parse(Text_t text);
Int_t Int$abs(Int_t x);
Int_t Int$power(Int_t base, Int_t exponent);
-Int_t Int$sqrt(Int_t i);
+OptionalInt_t Int$sqrt(Int_t i);
#define BIGGEST_SMALL_INT ((1<<29)-1)
diff --git a/stdlib/nums.c b/stdlib/nums.c
index 9f69bb1c..4c9be226 100644
--- a/stdlib/nums.c
+++ b/stdlib/nums.c
@@ -78,10 +78,6 @@ public OptionalNum_t Num$parse(Text_t text) {
return nan("null");
}
-public double Num$nan(Text_t tag) {
- return nan(Text$as_c_string(tag));
-}
-
public CONSTFUNC bool Num$isinf(double n) { return (fpclassify(n) == FP_INFINITE); }
public CONSTFUNC bool Num$finite(double n) { return (fpclassify(n) != FP_INFINITE); }
public CONSTFUNC bool Num$isnan(double n) { return (fpclassify(n) == FP_NAN); }
@@ -155,10 +151,6 @@ public OptionalNum32_t Num32$parse(Text_t text) {
return nan("null");
}
-public float Num32$nan(Text_t tag) {
- return nanf(Text$as_c_string(tag));
-}
-
public CONSTFUNC bool Num32$isinf(float n) { return (fpclassify(n) == FP_INFINITE); }
public CONSTFUNC bool Num32$finite(float n) { return (fpclassify(n) != FP_INFINITE); }
public CONSTFUNC bool Num32$isnan(float n) { return (fpclassify(n) == FP_NAN); }
diff --git a/stdlib/optionals.c b/stdlib/optionals.c
index b6a5989e..8c295228 100644
--- a/stdlib/optionals.c
+++ b/stdlib/optionals.c
@@ -21,6 +21,8 @@ public PUREFUNC bool is_null(const void *obj, const TypeInfo_t *non_optional_typ
return *((OptionalBool_t*)obj) == NULL_BOOL;
else if (non_optional_type == &Num$info)
return isnan(*((Num_t*)obj));
+ else if (non_optional_type == &Num32$info)
+ return isnan(*((Num32_t*)obj));
else if (non_optional_type == &Int64$info)
return ((OptionalInt64_t*)obj)->is_null;
else if (non_optional_type == &Int32$info)
diff --git a/stdlib/stdlib.c b/stdlib/stdlib.c
index 5dac3d3a..0e72dfc4 100644
--- a/stdlib/stdlib.c
+++ b/stdlib/stdlib.c
@@ -27,6 +27,18 @@
public bool USE_COLOR;
+static void signal_handler(int sig, siginfo_t *, void *)
+{
+ assert(sig == SIGILL);
+ fflush(stdout);
+ if (USE_COLOR) fputs("\x1b[31;7m ===== ILLEGAL INSTRUCTION ===== \n\n\x1b[m", stderr);
+ else fputs("===== ILLEGAL INSTRUCTION =====\n\n", stderr);
+ print_stack_trace(stderr, 3, 4);
+ fflush(stderr);
+ raise(SIGABRT);
+ _exit(1);
+}
+
public void tomo_init(void)
{
GC_INIT();
@@ -44,6 +56,12 @@ public void tomo_init(void)
if (register_printf_specifier('k', printf_text, printf_text_size))
errx(1, "Couldn't set printf specifier");
+
+ struct sigaction sigact;
+ sigact.sa_sigaction = signal_handler;
+ sigemptyset(&sigact.sa_mask);
+ sigact.sa_flags = 0;
+ sigaction(SIGILL, &sigact, (struct sigaction *)NULL);
}
static bool parse_single_arg(const TypeInfo_t *info, char *arg, void *dest)
diff --git a/test/minmax.tm b/test/minmax.tm
index a5dd984e..3d11f8e5 100644
--- a/test/minmax.tm
+++ b/test/minmax.tm
@@ -1,7 +1,7 @@
struct Foo(x:Int, y:Int):
func len(f:Foo->Num):
- return Num.sqrt(f.x*f.x + f.y*f.y)
+ return Num.sqrt(f.x*f.x + f.y*f.y)!
func main():
>> 3 _min_ 5
diff --git a/test/nums.tm b/test/nums.tm
index 1391a129..4e6d34a9 100644
--- a/test/nums.tm
+++ b/test/nums.tm
@@ -22,27 +22,47 @@ func main():
>> Num.INF:isinf()
= yes
- >> Num.nan()
- = nan
- >> nan := Num.nan()
- >> nan:isnan()
- = yes
+ >> Num.NAN
+ = NULL : Num?
+ >> nan := Num.NAN
+ = NULL : Num?
>> nan == nan
+ = yes
+ >> nan < nan
= no
-
- >> Num.PI:cos():near(-1)
+ >> nan > nan
+ = no
+ >> nan != nan
+ = no
+ >> nan <> nan
+ = 0
+ >> nan == 0.0
+ = no
+ >> nan < 0.0
= yes
- >> Num.PI:sin():near(0)
+ >> nan > 0.0
+ = no
+ >> nan != 0.0
= yes
+ >> nan <> 0.0
+ = -1
- >> Num.nan():near(Num.nan())
- = no
+ >> nan + 1
+ = NULL : Num?
+
+ >> 0./0.
+ = NULL : Num?
+
+ >> Num.PI:cos()!:near(-1)
+ = yes
+ >> Num.PI:sin()!:near(0)
+ = yes
>> Num.INF:near(-Num.INF)
= no
>> Num32.sqrt(16)
- = 4 : Num32
+ = 4 : Num32?
>> 0.25:mix(10, 20)
= 12.5
diff --git a/tomo.c b/tomo.c
index 0a6694fe..3b46c835 100644
--- a/tomo.c
+++ b/tomo.c
@@ -43,7 +43,7 @@ static OptionalBool_t verbose = false,
static OptionalText_t autofmt = Text("sed '/^\\s*$/d' | indent -kr -l100 -nbbo -nut -sob"),
cflags = Text("-Werror -fdollars-in-identifiers -std=gnu11 -Wno-trigraphs -fsanitize=signed-integer-overflow -fno-sanitize-recover"
- " -fno-signed-zeros -fno-finite-math-only -fno-signaling-nans -fno-trapping-math -fno-finite-math-only"
+ " -fno-signed-zeros -fno-finite-math-only -fno-signaling-nans -fno-trapping-math"
" -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -D_DEFAULT_SOURCE -fPIC -ggdb"
" -I$HOME/.local/share/tomo/installed"),
ldlibs = Text("-lgc -lgmp -lm -ltomo"),
diff --git a/typecheck.c b/typecheck.c
index 2d84c622..da810258 100644
--- a/typecheck.c
+++ b/typecheck.c
@@ -126,6 +126,28 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast)
errx(1, "Unreachable");
}
+static PUREFUNC bool risks_zero_or_inf(ast_t *ast)
+{
+ switch (ast->tag) {
+ case Int: {
+ const char *str = Match(ast, Int)->str;
+ OptionalInt_t int_val = Int$from_str(str);
+ return (int_val.small == 0x1); // zero
+ }
+ case Num: {
+ return Match(ast, Num)->n == 0.0;
+ }
+ case BinaryOp: {
+ auto binop = Match(ast, BinaryOp);
+ if (binop->op == BINOP_MULT || binop->op == BINOP_DIVIDE || binop->op == BINOP_MIN || binop->op == BINOP_MAX)
+ return risks_zero_or_inf(binop->lhs) || risks_zero_or_inf(binop->rhs);
+ else
+ return true;
+ }
+ default: return true;
+ }
+}
+
PUREFUNC type_t *get_math_type(env_t *env, ast_t *ast, type_t *lhs_t, type_t *rhs_t)
{
(void)env;
@@ -1011,6 +1033,16 @@ type_t *get_type(env_t *env, ast_t *ast)
return result;
return Type(NumType, .bits=TYPE_NBITS64);
}
+ case BINOP_MULT: case BINOP_DIVIDE: {
+ type_t *math_type = get_math_type(env, ast, value_type(lhs_t), value_type(rhs_t));
+ if (value_type(lhs_t)->tag == NumType || value_type(rhs_t)->tag == NumType) {
+ if (risks_zero_or_inf(binop->lhs) && risks_zero_or_inf(binop->rhs))
+ return Type(OptionalType, math_type);
+ else
+ return math_type;
+ }
+ return math_type;
+ }
default: {
return get_math_type(env, ast, lhs_t, rhs_t);
}
diff --git a/types.c b/types.c
index 5c26d07a..b57ff7ba 100644
--- a/types.c
+++ b/types.c
@@ -207,6 +207,11 @@ static PUREFUNC INLINE double type_max_magnitude(type_t *t)
PUREFUNC precision_cmp_e compare_precision(type_t *a, type_t *b)
{
+ if (a->tag == OptionalType && Match(a, OptionalType)->type->tag == NumType)
+ a = Match(a, OptionalType)->type;
+ if (b->tag == OptionalType && Match(b, OptionalType)->type->tag == NumType)
+ b = Match(b, OptionalType)->type;
+
if (is_int_type(a) && b->tag == NumType)
return NUM_PRECISION_LESS;
else if (a->tag == NumType && is_int_type(b))
@@ -338,6 +343,10 @@ PUREFUNC bool can_promote(type_t *actual, type_t *needed)
if (actual->tag == PointerType && can_promote(Match(actual, PointerType)->pointed, needed))
return true;
+ // Optional num -> num
+ if (needed->tag == NumType && actual->tag == OptionalType && Match(actual, OptionalType)->type->tag == NumType)
+ return can_promote(Match(actual, OptionalType)->type, needed);
+
// Optional promotion:
if (needed->tag == OptionalType && can_promote(actual, Match(needed, OptionalType)->type))
return true;