Refactor the error-backend and setup the test file for it

This commit is contained in:
Sameer Rahmani 2022-03-08 13:19:34 +00:00
parent 7d7293aa32
commit 2860e570da
9 changed files with 100 additions and 80 deletions

View File

@ -127,6 +127,10 @@ and recompile those functions with more optimization passes
* TODOs * TODOs
** TODO Investigate possible implementanion for Internal Errors
- An option is to use llvm registry functionality like the one used in =clang-doc= instead of
=errorVariants= var.
** TODO In =SereneContext::getLatestJITDylib= function, make sure that the JITDylib is still valid ** TODO In =SereneContext::getLatestJITDylib= function, make sure that the JITDylib is still valid
Make sure that the returning Dylib still exists in the JIT Make sure that the returning Dylib still exists in the JIT
by calling =jit->engine->getJITDylibByName(dylib_name);= by calling =jit->engine->getJITDylibByName(dylib_name);=

View File

@ -16,10 +16,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#ifndef SERENE_ENVIRONMENT_H #ifndef SERENE_TEST_ENVIRONMENT_H
#define SERENE_ENVIRONMENT_H #define SERENE_TEST_ENVIRONMENT_H
#include "serene/llvm/patches.h"
#include "serene/utils.h" #include "serene/utils.h"
#include <llvm/ADT/StringMap.h> #include <llvm/ADT/StringMap.h>

View File

@ -22,6 +22,8 @@
#include "serene/errors/base.h" #include "serene/errors/base.h"
#include "serene/errors/errc.h" #include "serene/errors/errc.h"
#include <serene/export.h>
#define GET_CLASS_DEFS #define GET_CLASS_DEFS
#include "serene/errors/errs.h.inc" #include "serene/errors/errs.h.inc"
@ -29,8 +31,12 @@
namespace serene::errors { namespace serene::errors {
/// Create and return a Serene flavored `llvm::Error` by passing the parameters
/// directly to the constructor of type `E`.
///
/// This is the official way of creating error objects in Serene.
template <typename E, typename... Args> template <typename E, typename... Args>
llvm::Error makeError(Args &&...args) { SERENE_EXPORT llvm::Error makeError(Args &&...args) {
return llvm::make_error<E>(std::forward<Args>(args)...); return llvm::make_error<E>(std::forward<Args>(args)...);
}; };

View File

@ -28,19 +28,20 @@
namespace serene::errors { namespace serene::errors {
// This class is used in the generated code
struct ErrorVariant { struct ErrorVariant {
int id; const int id;
const std::string title; const std::string title;
const std::string desc; const std::string desc;
const std::string help; const std::string help;
static ErrorVariant make(int id, const char *t, const char *d, static ErrorVariant make(const int id, const char *t, const char *d,
const char *h) { const char *h) {
return ErrorVariant(id, t, d, h); return ErrorVariant(id, t, d, h);
}; };
private: private:
ErrorVariant(int id, const char *t, const char *d, const char *h) ErrorVariant(const int id, const char *t, const char *d, const char *h)
: id(id), title(t), desc(d), help(h){}; : id(id), title(t), desc(d), help(h){};
}; };

View File

@ -17,6 +17,3 @@
*/ */
#include "serene/errors.h" #include "serene/errors.h"
#define GET_ERRS_ARRAY
#include "serene/errors/errs.h.inc"

View File

@ -21,6 +21,8 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE. * SOFTWARE.
*/ */
#ifndef SERENE_TEST_CONTEXT_H
#define SERENE_TEST_CONTEXT_H
#include "serene/context.h" #include "serene/context.h"
#include "serene/namespace.h" #include "serene/namespace.h"
@ -162,3 +164,4 @@ TEST_CASE("context and jit", "[context]") {
}; };
} // namespace serene } // namespace serene
#endif

View File

@ -22,25 +22,37 @@
* SOFTWARE. * SOFTWARE.
*/ */
#ifndef SERENE_TEST_ERRORS_H
#define SERENE_TEST_ERRORS_H
#include "serene/errors.h" #include "serene/errors.h"
#include "serene/exprs/symbol.h"
#include "../test_helpers.cpp.inc" #include <catch2/catch_test_macros.hpp>
#include <catch2/catch_all.hpp>
#include <llvm/Support/Casting.h> #include <llvm/ADT/StringMap.h>
#include <llvm/Support/Error.h>
#include <llvm/Support/ErrorHandling.h>
namespace serene { namespace serene {
namespace errors { namespace errors {
TEST_CASE("Error Expression", "[expression]") { TEST_CASE("Serene Error construction", "[errors]") {
std::unique_ptr<reader::LocationRange> range(dummyLocation()); {
auto err = exprs::makeAndCast<Error>(*range.get(), &DefExpectSymbol, std::unique_ptr<reader::LocationRange> range(dummyLocation());
"Something Failed"); llvm::Error err = llvm::make_error<PassFailureError>(*range, "test error");
CHECK(err->getVariant()->id == E0001); auto unhandled =
CHECK(err->toString() == "<Error E1: Something Failed>"); llvm::handleErrors(std::move(err), [&](const PassFailureError &e) {
}; REQUIRE(e.message() == "test error");
CHECK(errorVariants[e.ID].title == "PassFailureError");
CHECK(errorVariants[e.ID].desc == "Pass Failure.");
CHECK(errorVariants[e.ID].help.empty());
});
CHECK(!unhandled);
};
} // namespace errors } // namespace errors
}; // namespace errors
} // namespace serene } // namespace serene
#endif

View File

@ -19,8 +19,8 @@
#define CATCH_CONFIG_MAIN #define CATCH_CONFIG_MAIN
#include "./context_tests.cpp.inc" #include "./context_tests.cpp.inc"
#include "./environment_tests.cpp.inc" #include "./environment_tests.cpp.inc"
#include "./errors/error_tests.cpp.inc"
#include "./setup.cpp.inc" #include "./setup.cpp.inc"
// #include "./errors/error_tests.cpp.inc"
// #include "./exprs/expression_tests.cpp.inc" // #include "./exprs/expression_tests.cpp.inc"
// #include "./exprs/list_tests.cpp.inc" // #include "./exprs/list_tests.cpp.inc"
// #include "./exprs/number_tests.cpp.inc" // #include "./exprs/number_tests.cpp.inc"

View File

@ -16,6 +16,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
/**
* Commentary:
* This is a `tablegen` backend to read from generate error definitions
* from the given tablegen records defined in a `.td` file. It relies on
* Two main classes to be available in the target source code. `SereneError`
* and `ErrorVariant`. Checkout `libserene/include/serene/errors/base.h`.
*/
// The "serene/" part is due to a convention that we use in the project // The "serene/" part is due to a convention that we use in the project
#include "serene/errors-backend.h" #include "serene/errors-backend.h"
@ -65,48 +73,7 @@ void ErrorsBackend::createErrorClass(const int id, llvm::Record &defRec,
<< "public:\n" << "public:\n"
<< " using llvm::ErrorInfo<" << recName << ", " << " using llvm::ErrorInfo<" << recName << ", "
<< "SereneError>::ErrorInfo;\n" << "SereneError>::ErrorInfo;\n"
<< " constexpr static const int ID = " << id << ";\n};\n\n" << " constexpr static const int ID = " << id << ";\n};\n\n";
<< "static const ErrorVariant " << recName << INSTANCE_SUFFIX
<< " = ErrorVariant::make(\n"
<< " " << id << ",\n"
<< " \"" << recName << "\",\n";
auto desc = defRec.getValueAsString("desc");
if (desc.empty()) {
llvm::PrintError("'desc' field is empty for " + recName);
}
os << " \"" << desc << "\",\n";
auto help = defRec.getValueAsString("help");
if (!help.empty()) {
const llvm::MemoryBufferRef value(help, "help");
llvm::line_iterator lines(value, false);
while (!lines.is_at_end()) {
if (lines.line_number() != 1) {
os << '\t';
}
auto prevLine = *lines;
lines++;
os << '"' << prevLine << '"';
if (lines.is_at_end()) {
os << ";\n";
} else {
os << '\n';
}
}
} else {
os << " \"\"";
}
os << ");\n";
// os << " " << help << ");\n";
// auto *stringVal = llvm::dyn_cast<llvm::StringInit>(val.getValue());
}; };
void ErrorsBackend::createNSBody(llvm::raw_ostream &os) { void ErrorsBackend::createNSBody(llvm::raw_ostream &os) {
@ -135,41 +102,72 @@ void ErrorsBackend::createNSBody(llvm::raw_ostream &os) {
createErrorClass(i, *defRec, os); createErrorClass(i, *defRec, os);
} }
});
os << "#undef GET_CLASS_DEFS\n#endif\n\n"; os << "static const ErrorVariant errorVariants[" << indexList->size()
<< "] = {\n";
os << "#ifdef GET_ERRS_ARRAY\n\n";
inNamespace("serene::errors", os, [&](llvm::raw_ostream &os) {
os << "SereneError::ID = -1;\n";
for (size_t i = 0; i < indexList->size(); i++) {
llvm::Record *defRec = indexList->getElementAsRecord(i);
os << defRec->getName() << "::ID = " << i << ";\n";
}
os << "static const std::array<int, ErrorVariant *> "
"variants{\n";
for (size_t i = 0; i < indexList->size(); i++) { for (size_t i = 0; i < indexList->size(); i++) {
llvm::Record *defRec = indexList->getElementAsRecord(i); llvm::Record *defRec = indexList->getElementAsRecord(i);
auto recName = defRec->getName();
if (!defRec->isSubClassOf("Error")) { if (!defRec->isSubClassOf("Error")) {
continue; continue;
} }
os << " &" << defRec->getName() << INSTANCE_SUFFIX << ",\n";
os << " ErrorVariant::make(" << i << ", \n";
os << " \"" << recName << "\",\n";
auto desc = defRec->getValueAsString("desc");
if (desc.empty()) {
llvm::PrintError("'desc' field is empty for " + recName);
}
os << " \"" << desc << "\",\n";
auto help = defRec->getValueAsString("help");
if (!help.empty()) {
const llvm::MemoryBufferRef value(help, "help");
llvm::line_iterator lines(value, false);
while (!lines.is_at_end()) {
if (lines.line_number() != 1) {
os << '\t';
}
auto prevLine = *lines;
lines++;
os << '"' << prevLine << '"';
if (lines.is_at_end()) {
os << ";\n";
} else {
os << '\n';
}
}
} else {
os << " \"\"";
}
os << "),\n";
} }
os << "};\n";
}); });
os << "\n};\n#undef GET_ERRS_ARRAY\n#endif\n";
os << "#undef GET_CLASS_DEFS\n#endif\n\n";
} }
void ErrorsBackend::run(llvm::raw_ostream &os) { void ErrorsBackend::run(llvm::raw_ostream &os) {
(void)records; (void)records;
llvm::emitSourceFileHeader("Serene's Errors collection", os); llvm::emitSourceFileHeader("Serene's Errors collection", os);
// DO NOT GUARD THE HEADER WITH #ifndef ...
os << "#include \"serene/errors/base.h\"\n\n#include " os << "#include \"serene/errors/base.h\"\n\n#include "
"<llvm/Support/Error.h>\n\n"; "<llvm/Support/Error.h>\n\n";
os << "#ifndef SERENE_ERRORS_ERRORS_H\n#define SERENE_ERRORS_ERRORS_H\n\n";
createNSBody(os); createNSBody(os);
os << "#endif\n";
} }
void emitErrors(llvm::RecordKeeper &rk, llvm::raw_ostream &os) { void emitErrors(llvm::RecordKeeper &rk, llvm::raw_ostream &os) {