diff options
| author | _ov4 <ov4@ov4.org> | 2026-10-22 15:11:38 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-10-22 15:21:14 +0000 |
| commit | 64622b737f070b377fdef3fb31a67c084457bd7b (patch) | |
| tree | 8111bfcf325ed9162dac645a0c68536bad954820 /lib | |
| parent | f14b50d776ec743bd62fda22a9c154bba9588840 (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.h | 1 | ||||
| -rw-r--r-- | lib/CodeGen/ExpressionGen.cc | 25 | ||||
| -rw-r--r-- | lib/CodeGen/TypeCheckGen.cc | 6 |
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; } } |
