diff options
| -rw-r--r-- | doc/language/keyword/cpsc-keyword-function.7.md | 2 | ||||
| -rw-r--r-- | include/cps/FunctionGen.h | 7 | ||||
| -rw-r--r-- | lib/CodeGen/CodeGen.cc | 8 | ||||
| -rw-r--r-- | lib/CodeGen/FunctionGen.cc | 4 |
4 files changed, 20 insertions, 1 deletions
diff --git a/doc/language/keyword/cpsc-keyword-function.7.md b/doc/language/keyword/cpsc-keyword-function.7.md index a7de9a4..d49a07d 100644 --- a/doc/language/keyword/cpsc-keyword-function.7.md +++ b/doc/language/keyword/cpsc-keyword-function.7.md @@ -15,7 +15,7 @@ ENDFUNCTION # DESCRIPTION -Call a function in an expression as `name(arguments)`. A call may precede its top-level definition. RETURNS specifies the result type; omitting it defaults to INTEGER. RETURN supplies a value of that type. Whole-array results are unsupported. +Call a function in an expression as `name(arguments)`. A call may precede its top-level definition. RETURNS specifies the result type; omitting it defaults to INTEGER. RETURN supplies a value of that type. If execution reaches ENDFUNCTION without a RETURN, a run-time error is reported and an exception is thrown (the top-level MAIN is exempt). Whole-array results are unsupported. As a compiler extension, `name(arguments)` may also be a complete statement that discards the result. This differs from guide section 8.2, which requires function calls to occur within expressions. Built-in functions support the same form. diff --git a/include/cps/FunctionGen.h b/include/cps/FunctionGen.h index 6c5c7c0..f5e5ba2 100644 --- a/include/cps/FunctionGen.h +++ b/include/cps/FunctionGen.h @@ -86,6 +86,7 @@ private: std::function<void(const FuncSig &)> BeforeCall; ArrayParamBinder BindArrayParam; std::function<void(llvm::Value *, const std::string &, int)> RegisterCopy; + std::function<void(int, const std::string &)> MissingReturn; ClangABI ABI{Context, Types}; // clang context created on the first EXTERN void createArgumentAllocas(llvm::Function *F, const std::vector<ParamDecl> &Params, bool IsMethod); @@ -105,6 +106,12 @@ public: void setArrayParamBinder(ArrayParamBinder Binder) { BindArrayParam = std::move(Binder); } + // Called (with the FUNCTION header line and name) where a non-VOID FUNCTION + // body can fall off its end; emits the runtime error and throw. + void setMissingReturnHandler(std::function<void(int, const std::string &)> Handler) { + MissingReturn = std::move(Handler); + } + void setCopyRegistrar(std::function<void(llvm::Value *, const std::string &, int)> Registrar) { RegisterCopy = std::move(Registrar); } diff --git a/lib/CodeGen/CodeGen.cc b/lib/CodeGen/CodeGen.cc index e45a3db..772dc82 100644 --- a/lib/CodeGen/CodeGen.cc +++ b/lib/CodeGen/CodeGen.cc @@ -77,6 +77,14 @@ CodeGen::CodeGen() { activateStackObject(registerStackObject(Addr, TypeName, Line)); }); + FuncGen->setMissingReturnHandler([this](int Line, const std::string &Name) { + Value *Msg = Builder->CreateGlobalString("[Fatal] line %d: FUNCTION %s ended without RETURN\n", + "err_missing_return", 0, TheModule.get()); + Value *NameStr = Builder->CreateGlobalString(Name, "function_name", 0, TheModule.get()); + RuntimeChecker->emitStderr(Msg, {RuntimeChecker->lineValue(Line), NameStr}); + Exceptions->emitThrow(); + }); + SetupExternalFunctions(); } diff --git a/lib/CodeGen/FunctionGen.cc b/lib/CodeGen/FunctionGen.cc index 48c9978..c233205 100644 --- a/lib/CodeGen/FunctionGen.cc +++ b/lib/CodeGen/FunctionGen.cc @@ -444,6 +444,10 @@ Function *FunctionGen::emitFunctionDef(FunctionDefAST *FuncAST, if (!Builder.GetInsertBlock()->hasTerminator()) { if (sameIdentifier(Proto->getReturnType(), "VOID")) { Exceptions.recordExit(Builder.CreateRetVoid()); + } else if (MissingReturn && !(!IsMethod && sameIdentifier(Proto->getName(), "MAIN"))) { + // Falling off the end of a FUNCTION is a runtime error, not a zero value. + // The handler ends the block with a throw (unreachable). + MissingReturn(FuncAST->getLine(), Proto->getName()); } else { Exceptions.recordExit(Builder.CreateRet(Constant::getNullValue(TheFunction->getReturnType()))); } |
