diff options
| author | _ov4 <ov4@ov4.org> | 2026-07-26 12:51:55 +0000 |
|---|---|---|
| committer | _ov4 <ov4@ov4.org> | 2026-07-26 12:51:55 +0000 |
| commit | d1c47a328785ece9a8ffd9bbca3138818b8203b3 (patch) | |
| tree | 5a057e73674f6d7ac021572007266706fecc31fd /lib/CodeGen/FunctionGen.cc | |
| parent | 9164871fe4982bdd12866eb28ebded68ca3dacd1 (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.cc | 46 |
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()) { |
