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
** 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
Make sure that the returning Dylib still exists in the JIT
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/>.
*/
#ifndef SERENE_ENVIRONMENT_H
#define SERENE_ENVIRONMENT_H
#ifndef SERENE_TEST_ENVIRONMENT_H
#define SERENE_TEST_ENVIRONMENT_H
#include "serene/llvm/patches.h"
#include "serene/utils.h"
#include <llvm/ADT/StringMap.h>

View File

@ -22,6 +22,8 @@
#include "serene/errors/base.h"
#include "serene/errors/errc.h"
#include <serene/export.h>
#define GET_CLASS_DEFS
#include "serene/errors/errs.h.inc"
@ -29,8 +31,12 @@
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>
llvm::Error makeError(Args &&...args) {
SERENE_EXPORT llvm::Error makeError(Args &&...args) {
return llvm::make_error<E>(std::forward<Args>(args)...);
};

View File

@ -28,19 +28,20 @@
namespace serene::errors {
// This class is used in the generated code
struct ErrorVariant {
int id;
const int id;
const std::string title;
const std::string desc;
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) {
return ErrorVariant(id, t, d, h);
};
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){};
};

View File

@ -17,6 +17,3 @@
*/
#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
* SOFTWARE.
*/
#ifndef SERENE_TEST_CONTEXT_H
#define SERENE_TEST_CONTEXT_H
#include "serene/context.h"
#include "serene/namespace.h"
@ -162,3 +164,4 @@ TEST_CASE("context and jit", "[context]") {
};
} // namespace serene
#endif

View File

@ -22,25 +22,37 @@
* SOFTWARE.
*/
#ifndef SERENE_TEST_ERRORS_H
#define SERENE_TEST_ERRORS_H
#include "serene/errors.h"
#include "serene/exprs/symbol.h"
#include "../test_helpers.cpp.inc"
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <llvm/Support/Casting.h>
#include <llvm/ADT/StringMap.h>
#include <llvm/Support/Error.h>
#include <llvm/Support/ErrorHandling.h>
namespace serene {
namespace errors {
TEST_CASE("Error Expression", "[expression]") {
std::unique_ptr<reader::LocationRange> range(dummyLocation());
auto err = exprs::makeAndCast<Error>(*range.get(), &DefExpectSymbol,
"Something Failed");
TEST_CASE("Serene Error construction", "[errors]") {
{
std::unique_ptr<reader::LocationRange> range(dummyLocation());
llvm::Error err = llvm::make_error<PassFailureError>(*range, "test error");
CHECK(err->getVariant()->id == E0001);
CHECK(err->toString() == "<Error E1: Something Failed>");
};
auto unhandled =
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 serene
#endif

View File

@ -19,8 +19,8 @@
#define CATCH_CONFIG_MAIN
#include "./context_tests.cpp.inc"
#include "./environment_tests.cpp.inc"
#include "./errors/error_tests.cpp.inc"
#include "./setup.cpp.inc"
// #include "./errors/error_tests.cpp.inc"
// #include "./exprs/expression_tests.cpp.inc"
// #include "./exprs/list_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/>.
*/
/**
* 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
#include "serene/errors-backend.h"
@ -65,48 +73,7 @@ void ErrorsBackend::createErrorClass(const int id, llvm::Record &defRec,
<< "public:\n"
<< " using llvm::ErrorInfo<" << recName << ", "
<< "SereneError>::ErrorInfo;\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());
<< " constexpr static const int ID = " << id << ";\n};\n\n";
};
void ErrorsBackend::createNSBody(llvm::raw_ostream &os) {
@ -135,41 +102,72 @@ void ErrorsBackend::createNSBody(llvm::raw_ostream &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++) {
llvm::Record *defRec = indexList->getElementAsRecord(i);
auto recName = defRec->getName();
if (!defRec->isSubClassOf("Error")) {
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)records;
llvm::emitSourceFileHeader("Serene's Errors collection", os);
// DO NOT GUARD THE HEADER WITH #ifndef ...
os << "#include \"serene/errors/base.h\"\n\n#include "
"<llvm/Support/Error.h>\n\n";
os << "#ifndef SERENE_ERRORS_ERRORS_H\n#define SERENE_ERRORS_ERRORS_H\n\n";
createNSBody(os);
os << "#endif\n";
}
void emitErrors(llvm::RecordKeeper &rk, llvm::raw_ostream &os) {