aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-10-22 15:11:38 +0000
committer_ov4 <ov4@ov4.org>2026-10-22 15:21:14 +0000
commit64622b737f070b377fdef3fb31a67c084457bd7b (patch)
tree8111bfcf325ed9162dac645a0c68536bad954820 /lib
parentf14b50d776ec743bd62fda22a9c154bba9588840 (diff)
feat(codegen)!: require explicit REAL to INTEGER conversion and add INT
Implicit REAL to INTEGER is now a compile error with an INT(...) hint. INT is a compiler builtin that truncates toward zero and raises a runtime error for NaN or values outside the INTEGER range. No existing test program relied on the implicit conversion. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/CodeGen/Builtins.h1
-rw-r--r--lib/CodeGen/ExpressionGen.cc25
-rw-r--r--lib/CodeGen/TypeCheckGen.cc6
3 files changed, 30 insertions, 2 deletions
diff --git a/lib/CodeGen/Builtins.h b/lib/CodeGen/Builtins.h
index 2163b3e..84ba765 100644
--- a/lib/CodeGen/Builtins.h
+++ b/lib/CodeGen/Builtins.h
@@ -18,6 +18,7 @@ inline constexpr BuiltinInfo Builtins[] = {
{"MALLOC", 1, "C_TYPE(\"void*\")"},
{"REALLOC", 2, "C_TYPE(\"void*\")"},
{"FREE", 1, "C_TYPE(\"void*\")"},
+ {"INT", 1, "INTEGER"},
};
inline const BuiltinInfo *findBuiltin(const std::string &Name) {
diff --git a/lib/CodeGen/ExpressionGen.cc b/lib/CodeGen/ExpressionGen.cc
index 0434348..51d7087 100644
--- a/lib/CodeGen/ExpressionGen.cc
+++ b/lib/CodeGen/ExpressionGen.cc
@@ -251,6 +251,7 @@ Value *CodeGen::emitCall(const std::string &Callee,
if (!Address) return nullptr;
return Memory->emitRelease(Address, Line);
}
+ if (Name == "INT") return emitIntBuiltin(ArgExprs[0].get(), Line);
if (Name == "EOF") {
Value *FileName = emitFileNameOperand(ArgExprs[0].get(), "EOF");
if (!FileName) return nullptr;
@@ -264,6 +265,30 @@ Value *CodeGen::emitCall(const std::string &Callee,
return FuncGen->emitCall(*Sig, Args, Variadic);
}
+// INT(x): the integer part of a REAL, truncated toward zero. NaN and values
+// outside the INTEGER range are a runtime error.
+Value *CodeGen::emitIntBuiltin(ExprAST *Arg, int Line) {
+ SaveAndRestore<int> Location(DiagnosticLine, Line ? Line : DiagnosticLine);
+ const TypeInfo *Info = getExprTypeInfo(Arg);
+ if (isWholeArrayVar(Arg) || !Info ||
+ (Info->Kind != TypeKind::Integer && Info->Kind != TypeKind::Real)) {
+ reportError("INT expects an INTEGER or REAL argument");
+ return nullptr;
+ }
+ Value *V = emitExpr(Arg);
+ if (!V || Info->Kind == TypeKind::Integer) return V;
+
+ Type *DoubleTy = Type::getDoubleTy(*TheContext);
+ Value *AtLeastMin = Builder->CreateFCmpOGE(V, ConstantFP::get(DoubleTy, -9223372036854775808.0), "int_ge_min");
+ Value *BelowMax = Builder->CreateFCmpOLT(V, ConstantFP::get(DoubleTy, 9223372036854775808.0), "int_lt_max");
+ Value *InRange = Builder->CreateAnd(AtLeastMin, BelowMax, "int_in_range");
+ Value *OutOfRange = Builder->CreateNot(InRange, "int_out_of_range");
+ Value *Msg = Builder->CreateGlobalString("[Fatal] line %d: INT argument out of INTEGER range\n",
+ "err_int_range", 0, TheModule.get());
+ RuntimeChecker->emitCheck(OutOfRange, Msg, {RuntimeChecker->lineValue(Line)});
+ return Builder->CreateFPToSI(V, Type::getInt64Ty(*TheContext), "int_value");
+}
+
Value *CodeGen::emitAllocationSize(ExprAST *Expr) {
SaveAndRestore<int> Location(DiagnosticLine, Expr->getLine() ? Expr->getLine() : DiagnosticLine);
const TypeInfo *Info = getExprTypeInfo(Expr);
diff --git a/lib/CodeGen/TypeCheckGen.cc b/lib/CodeGen/TypeCheckGen.cc
index 8d54221..7cdf6ea 100644
--- a/lib/CodeGen/TypeCheckGen.cc
+++ b/lib/CodeGen/TypeCheckGen.cc
@@ -109,8 +109,10 @@ Value *CodeGen::emitCoercedExpr(ExprAST *E, const TypeInfo *Target) {
bool Allowed = Src->Kind == Target->Kind ||
(Src->Kind == TypeKind::Integer && Target->Kind == TypeKind::Real);
if (!Allowed) {
- reportError("Cannot use a value of type %s where %s is expected",
- Src->Name.c_str(), Target->Name.c_str());
+ reportError("Cannot use a value of type %s where %s is expected%s",
+ Src->Name.c_str(), Target->Name.c_str(),
+ Src->Kind == TypeKind::Real && Target->Kind == TypeKind::Integer
+ ? " (use INT(...) to convert)" : "");
return nullptr;
}
}