diff options
| -rw-r--r-- | compile.c | 75 | ||||
| -rw-r--r-- | environment.c | 32 | ||||
| -rw-r--r-- | examples/vectors/vectors.tm | 4 | ||||
| -rw-r--r-- | stdlib/integers.c | 4 | ||||
| -rw-r--r-- | stdlib/integers.h | 2 | ||||
| -rw-r--r-- | stdlib/nums.c | 8 | ||||
| -rw-r--r-- | stdlib/optionals.c | 2 | ||||
| -rw-r--r-- | stdlib/stdlib.c | 18 | ||||
| -rw-r--r-- | test/minmax.tm | 2 | ||||
| -rw-r--r-- | test/nums.tm | 42 | ||||
| -rw-r--r-- | tomo.c | 2 | ||||
| -rw-r--r-- | typecheck.c | 32 | ||||
| -rw-r--r-- | types.c | 9 |
13 files changed, 173 insertions, 59 deletions
@@ -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 @@ -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); } @@ -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; |
