Refactor the error-backend and setup the test file for it
This commit is contained in:
parent
7d7293aa32
commit
2860e570da
4
dev.org
4
dev.org
|
@ -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);=
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)...);
|
||||
};
|
||||
|
||||
|
|
|
@ -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){};
|
||||
};
|
||||
|
||||
|
|
|
@ -17,6 +17,3 @@
|
|||
*/
|
||||
|
||||
#include "serene/errors.h"
|
||||
|
||||
#define GET_ERRS_ARRAY
|
||||
#include "serene/errors/errs.h.inc"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue