aboutsummaryrefslogtreecommitdiff
path: root/lib/CodeGen/FunctionGen.cc
diff options
context:
space:
mode:
author_ov4 <ov4@ov4.org>2026-07-26 12:51:55 +0000
committer_ov4 <ov4@ov4.org>2026-07-26 12:51:55 +0000
commitd1c47a328785ece9a8ffd9bbca3138818b8203b3 (patch)
tree5a057e73674f6d7ac021572007266706fecc31fd /lib/CodeGen/FunctionGen.cc
parent9164871fe4982bdd12866eb28ebded68ca3dacd1 (diff)
fix: close correctness gaps found by post-refactor adversarial review
Confirmed by a 17-agent review pass over the refactor diff (12 findings verified against the code and reproduced end-to-end): - marshalCallArgs: BYREF arguments must match the declared parameter type exactly (was: silent bit-reinterpretation, e.g. REAL passed to BYREF INTEGER printed garbage with exit 0); whole-array arguments are rejected (was: callee assignment smashed the array data pointer -> runtime SIGSEGV) - Null guards for CHR/LCASE/UCASE/IS_NUM/NUM_TO_STR/STR_TO_NUM/ASC argument emission (was: compiler SIGSEGV on e.g. CHR(undeclared)), matching the guards MID/RIGHT/LEFT already had - ParseReturnStmt: propagate expression-parse failure (was: diagnostic printed but exit 0) and recognize bare RETURN before ENDWHILE/UNTIL/NEXT (was: bogus diagnostic on legal early return) - emitPrototype rejects FUNCTION/PROCEDURE names that collide with runtime/libc symbols (was: FUNCTION strlen silently hijacked the string runtime; arity mismatch crashed createArgumentAllocas) or with builtins (was: silently shadowed); conflicting redefinitions keep the first signature - FOR STEP is evaluated exactly once, before the loop (was: once per iteration; the runtime-direction refactor had moved it to N+1) - RETURN values are coerced to the declared return type via the signature table (RETURN n in a RETURNS REAL function now compiles) - '&' coerces its operands to STRING (CHAR/number operands work); OUTPUT of a PROCEDURE call reports a clean error AGENTS docs updated to match. All positive samples remain byte-identical to the pre-refactor baseline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Diffstat (limited to 'lib/CodeGen/FunctionGen.cc')
-rw-r--r--lib/CodeGen/FunctionGen.cc46
1 files changed, 39 insertions, 7 deletions
diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc
index 2e5a704..b778bbe 100644
--- a/lib/CodeGen/FunctionGen.cc
+++ b/lib/CodeGen/FunctionGen.cc
@@ -1,10 +1,24 @@
#include "cps/FunctionGen.h"
-#include "llvm/IR/Verifier.h"
+#include "cps/CodeGen.h"
#include <cstdio>
using namespace llvm;
using namespace cps;
+// Symbols the compiler itself declares (libc + runtime); a user
+// FUNCTION/PROCEDURE with one of these names would hijack the runtime.
+static bool isReservedRuntimeName(const std::string &Name) {
+ static const char *Reserved[] = {
+ "main", "printf", "scanf", "malloc", "free", "strlen", "memcpy",
+ "strcpy", "strcat", "toupper", "tolower", "sprintf", "strtol",
+ "strtod", "memset", "exit",
+ };
+ for (const char *R : Reserved) {
+ if (Name == R) return true;
+ }
+ return false;
+}
+
Type *FunctionGen::getLLVMType(const std::string &TypeName) {
Type *Resolved = Types.getLLVMType(TypeName);
if (!Resolved) {
@@ -45,6 +59,19 @@ const FuncSig *FunctionGen::getSignature(const std::string &Name) const {
}
Function *FunctionGen::emitPrototype(PrototypeAST *Proto) {
+ if (isReservedRuntimeName(Proto->getName())) {
+ fprintf(stderr, "Error: '%s' is a reserved runtime symbol and cannot be a FUNCTION/PROCEDURE name\n",
+ Proto->getName().c_str());
+ HadError = true;
+ return nullptr;
+ }
+ if (CodeGen::isBuiltinName(Proto->getName())) {
+ fprintf(stderr, "Error: '%s' is a built-in function and cannot be redefined\n",
+ Proto->getName().c_str());
+ HadError = true;
+ return nullptr;
+ }
+
FuncSig Sig;
Sig.ReturnTypeName = Proto->getReturnType();
@@ -57,15 +84,24 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) {
ArgTypes.push_back(T);
Sig.Params.emplace_back(std::get<1>(Arg), std::get<2>(Arg));
}
- Signatures[Proto->getName()] = std::move(Sig);
Type *RetType = getLLVMType(Proto->getReturnType());
FunctionType *FT = FunctionType::get(RetType, ArgTypes, false);
Function *F = Module.getFunction(Proto->getName());
+ if (F && F->getFunctionType() != FT) {
+ fprintf(stderr, "Error: Function %s conflicts with an existing declaration of a different type\n",
+ Proto->getName().c_str());
+ HadError = true;
+ return nullptr;
+ }
if (!F) {
F = Function::Create(FT, Function::ExternalLinkage, Proto->getName(), &Module);
}
+ // Keep the first registration: on a duplicate definition the first one
+ // wins and the second is rejected by emitFunctionDef.
+ Signatures.emplace(Proto->getName(), std::move(Sig));
+
unsigned Idx = 0;
for (auto &Arg : F->args()) {
if (Idx < Proto->getArgs().size()) {
@@ -78,11 +114,7 @@ Function *FunctionGen::emitPrototype(PrototypeAST *Proto) {
Function *FunctionGen::emitFunctionDef(FunctionDefAST *FuncAST,
const std::function<void(StmtAST*)> &StmtEmitter) {
PrototypeAST *Proto = FuncAST->getProto();
- Function *TheFunction = Module.getFunction(Proto->getName());
-
- if (!TheFunction) {
- TheFunction = emitPrototype(Proto);
- }
+ Function *TheFunction = emitPrototype(Proto);
if (!TheFunction) return nullptr;
if (!TheFunction->empty()) {