/* context.c -- Stores state of the compilation process. Copyright © 2011-2016 Samuel Lidén Borell Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include "context.h" #include "context_private.h" #include "interop.h" #include "misc.h" #include "verify.h" /* Error descriptions. Based on the method developed by Bruno Haible in Ulrich Drepper's "How To Write Shared Libraries" manual. */ static const union { struct DescriptionEntries { #define LRL_D(n, s) char n[sizeof(s)]; #include "context_errors.h" #undef LRL_D char dummy; } entries; char data[1]; } error_descs = { { #define LRL_D(n, s) s, #include "context_errors.h" #undef LRL_D 0 } }; static const unsigned int desc_indices[] = { #define LRL_D(n, s) offsetof(struct DescriptionEntries, n), #include "context_errors.h" #undef LRL_D 0 }; LRLCtx *lrl_ctx_new(LRLConfig *config) { LRLCtx *ctx = malloc(sizeof(LRLCtx)); ctx->has_errors = 0; ctx->files = NULL; ctx->deferred_list = NULL; ctx->uses_list = NULL; ctx->interop_list = NULL; ctx->has_completed_constexpr = 0; ctx->allow_local_type_params = 0; ctx->builtins_scope = lrl_ident_create_root(); ctx->internals_scope = lrl_ident_create_priv_scope(ctx->builtins_scope); ctx->external_scope = calloc(1, sizeof(LRLIdent)); ctx->error_handler = lrl_err_default_handler; ctx->bool_tr.type = lrl_builtin_get_type(LRL_BT_bool); ctx->bool_tr.quals = 0; ctx->bool_tr.prm = NULL; ctx->config = config; ctx->no_filesystem = 0; ctx->errctx = NULL; memset(&ctx->error, 0, sizeof(ctx->error)); return ctx; } LRLFileInfo *lrl_ctx_add_file(LRLCtx *ctx, char *filename, char *source) { LRLFileInfo *fileinfo; /* Add source file */ fileinfo = malloc(sizeof(LRLFileInfo)); fileinfo->next = ctx->files; fileinfo->completed = 0; fileinfo->filename = filename; fileinfo->source = source; fileinfo->root = NULL; ctx->files = fileinfo; return fileinfo; } void lrl_ctx_set_file_ast(LRLFileInfo *fileinfo, LRLASTNamespace *root) { fileinfo->root = root; } void lrl_ctx_free(LRLCtx *ctx) { LRLFileInfo *fileinfo; for (fileinfo = ctx->files; fileinfo; ) { LRLFileInfo *next = fileinfo->next; free(fileinfo); fileinfo = next; } free(ctx); } void lrl_ctx_find_source(const LRLCtx *ctx, const char *s, const char **filename, int *line, int *column) { LRLFileInfo *file = ctx->files; const char *source = NULL; int l = 1, c = 1; char ch; /* Find file */ for (; file; file = file->next) { if ((uintptr_t)s >= (uintptr_t)file->source && (uintptr_t)file->source > (uintptr_t)source) { /* Better match */ *filename = file->filename; source = file->source; } } if (source) { /* Find location in source */ for (;;) { ch = *source; if (source == s) { *line = l; *column = c; return; } else if (ch == '\0') break; else if (ch == '\n') { l++; c = 1; } else { c++; } source++; } } *filename = NULL; *line = -1; *column = -1; } void lrl_ctx_current_error_source(const LRLCtx *ctx, const char **filename, int *line, int *column) { const LRLCodeLocation *loc; if (ctx->errctx) { const LRLErrorCtx *errctx = ctx->errctx; while (errctx->parent) { errctx = errctx->parent; } loc = &errctx->loc; } else { loc = &ctx->error.locs[0]; } lrl_ctx_find_source(ctx, loc->start, filename, line, column); } void lrl_err_set_char(LRLCtx *ctx, const char *source, size_t index) { ctx->error.locs[index].start = source; ctx->error.locs[index].length = (*source ? 1 : 0); } void lrl_err_set_char_range(LRLCtx *ctx, const char *source, size_t length, size_t index) { ctx->error.locs[index].start = source; ctx->error.locs[index].length = length; } void lrl_err_set_loc(LRLCtx *ctx, const LRLCodeLocation *loc, size_t index) { ctx->error.locs[index] = *loc; } void lrl_err_set_token(LRLCtx *ctx, const LRLToken *token, size_t index) { ctx->error.locs[index] = token->loc; } static void set_loc_range(LRLCodeLocation *loc, const LRLToken *from, const LRLToken *to) { if (from && to && to >= from) { loc->start = from->loc.start; loc->length = to->loc.start + to->loc.length - from->loc.start; } else { loc->start = NULL; loc->length = 0; } } void lrl_err_set_token_range(LRLCtx *ctx, const LRLToken *from, const LRLToken *to, size_t index) { set_loc_range(&ctx->error.locs[index], from, to); } void lrl_err_set_ident(LRLCtx *ctx, const LRLToken *first_token, size_t index) { const LRLToken *end = first_token; for (;;) { if (end->type != LRL_TT_Ident && end->type != LRL_KW_Here) break; end++; if (end->type != LRL_Sym_NamespaceSep) break; end++; } lrl_err_set_token_range(ctx, first_token, end-1, index); } void lrl_err_set_type(LRLCtx *ctx, const LRLASTType *type, size_t index) { lrl_err_set_token_range(ctx, type->from, type->to, index); } void lrl_err_set_typeref(LRLCtx *ctx, const LRLTypeRef *typeref, size_t index) { /* TODO should include quals also! */ lrl_err_set_token_range(ctx, typeref->type->from, typeref->type->to, index); } void lrl_err_set_expr(LRLCtx *ctx, const LRLASTExpr *expr, size_t index) { lrl_err_set_token_range(ctx, expr->from, expr->to, index); } void lrl_err_set_interop(LRLCtx *ctx, const char *filename, int line, int column, const LRLASTInterop *interop) { ctx->error.interop.interop = interop; ctx->error.interop.filename = filename; ctx->error.interop.line = line; ctx->error.interop.column = column; } static void print_loc_linenum(const LRLCtx *ctx, const LRLCodeLocation *loc, int error_if_unknown) { const char *file; int line, column; lrl_ctx_find_source(ctx, loc->start, &file, &line, &column); if (file) { fprintf(stderr, "%s:%d,%d: ", file, line, column); } else if (error_if_unknown) { fprintf(stderr, "unknown location: "); } } static void print_loc_text(const LRLCodeLocation *loc) { fprintf(stderr, "%.*s\n", size2int(loc->length), loc->start); } static void error_full(LRLCtx *ctx, const LRLError *err) { if (ctx->error_handler) { ctx->error_handler(ctx, err); } lrl_ctx_set_has_errors(ctx, 1); } static void print_errorsource(LRLCtx *ctx, const LRLError *err, const LRLCodeLocation *loc, int *rootmsg_printed) { if (!*rootmsg_printed) { const char *description = &error_descs.data[desc_indices[err->type]]; *rootmsg_printed = 1; if (!loc->start) { fprintf(stderr, "Error at unkonwn location: %s\n", description); return; } if (ctx->error.interop.column) { fprintf(stderr, "In interop included at "); print_loc_linenum(ctx, loc, 1); fprintf(stderr, ": "); } else { print_loc_linenum(ctx, loc, 1); fprintf(stderr, "%s: ", description); } } else { fprintf(stderr, " evaluated to "); print_loc_linenum(ctx, loc, 0); } print_loc_text(&ctx->error.locs[0]); } /** * Displays the error to stderr. */ void lrl_err_default_handler(LRLCtx *ctx, const LRLError *err) { size_t i; const LRLErrorCtx *errctx; int rootmsg_printed = 0; /* Show evaluation "stack trace" */ for (errctx = ctx->errctx; errctx; errctx = errctx->parent) { print_errorsource(ctx, err, &errctx->loc, &rootmsg_printed); } if (!ctx->error.interop.filename) { /* TODO improve the message (replace "related") */ /* Show main location */ print_errorsource(ctx, err, &ctx->error.locs[0], &rootmsg_printed); /* Show "related" locations */ for (i = 1; i < LRL_MAX_ERR_LOCATIONS; i++) { if (ctx->error.locs[i].start) { fprintf(stderr, " related: "); print_loc_linenum(ctx, &ctx->error.locs[i], 0); print_loc_text(&ctx->error.locs[i]); } } } else { fprintf(stderr, "%s:%d", ctx->error.interop.filename, ctx->error.interop.line); if (ctx->error.interop.column) { fprintf(stderr, ",%d", ctx->error.interop.column); } fprintf(stderr, ": %s\n", &error_descs.data[desc_indices[err->type]]); /*fprintf(stderr, " included from interop at "); print_loc_linenum(ctx, &ctx->error.interop.interop->name->loc); fputc('\n', stderr);*/ } } void lrl_err_finish(LRLCtx *ctx, LRLErrorType err) { ctx->error.type = err; error_full(ctx, &ctx->error); /* Clear info for next error */ memset(&ctx->error, 0, sizeof(ctx->error)); } /* Error shorthands */ void lrl_err_char(LRLCtx *ctx, LRLErrorType err, const char *source) { lrl_err_set_char(ctx, source, 0); lrl_err_finish(ctx, err); } void lrl_err_token(LRLCtx *ctx, LRLErrorType err, const LRLToken *tok) { lrl_err_set_token(ctx, tok, 0); lrl_err_finish(ctx, err); } void lrl_err_expr(LRLCtx *ctx, LRLErrorType err, const LRLASTExpr *expr) { lrl_err_set_expr(ctx, expr, 0); lrl_err_finish(ctx, err); } void lrl_err_type(LRLCtx *ctx, LRLErrorType err, const LRLASTType *type) { lrl_err_set_type(ctx, type, 0); lrl_err_finish(ctx, err); } /** * Starts an "error context". Error contexts are used for referenced and * evaluated expressions. * * \param errctx Allocated but unitialized error context. Must stay allocated * until lrl_errctx_end is called. * \param from Token range, from (inclusive) * \param from Token range, to (inclusive) */ void lrl_errctx_start_loc(LRLCtx *ctx, LRLErrorCtx *errctx, const LRLToken *from, const LRLToken *to) { set_loc_range(&errctx->loc, from, to); errctx->parent = ctx->errctx; ctx->errctx = errctx; } void lrl_errctx_end(LRLCtx *ctx, const LRLErrorCtx *errctx) { if (ctx->errctx != errctx) { fail("ctx_errctx_level"); } ctx->errctx = errctx->parent; } int lrl_ctx_has_errors(const LRLCtx *ctx) { return ctx->has_errors; } void lrl_ctx_set_has_errors(LRLCtx *ctx, int value) { ctx->has_errors = value; } void lrl_ctx_set_has_deferred(LRLCtx *ctx) { ctx->has_deferred = 1; } void lrl_ctx_set_has_completed_constexpr(LRLCtx *ctx) { ctx->has_deferred = 1; ctx->has_completed_constexpr = 1; } /** * Prints diagnostic when a cycle is detected (e.g. identifier referencing * itself, but can also be a sign of a compiler/interop bug). * * Only cycles that cause lrl_ctx_bind_verify to get stuck are handled by * this function. */ static void cycle_detected(LRLCtx *ctx) { LRLFileInfo *file; LRLIdentRef *identref; int has_incomplete_files = 0; int found_guilty_ident = 0; for (identref = ctx->deferred_list; identref; identref = identref->next) { const LRLIdent *ident = identref->ident; if (ident) { fprintf(stderr, "found left-over bound identifier\n"); } else if (ident == LRL_IDENT_MISSING) { fprintf(stderr, "found left-over unbound identifier\n"); } else { lrl_err_set_ident(ctx, identref->first_token, 0); lrl_err_finish(ctx, LRL_Err_CycleDetected); found_guilty_ident = 1; } } if (found_guilty_ident) return; fprintf(stderr, "Unhandled cycle detected. This is a compiler BUG!\n" "These files were still being analyzed:\n"); for (file = ctx->files; file; file = file->next) { if (!file->completed) { fprintf(stderr, "%s\n", file->filename); has_incomplete_files = 1; } } if (!has_incomplete_files) { fprintf(stderr, "(no files)\n"); } /* Prevent backend from blowing up */ lrl_ctx_set_has_errors(ctx, 1); } /** * Binds identifiers, loads dependencies, verifies the AST, until finished. */ void lrl_ctx_bind_verify(LRLCtx *ctx) { do { LRLFileInfo *file; /* Get types for interop options exprs */ int has_broken_interop = 0; LRLASTInterop *interop; for (interop = ctx->interop_list; interop; interop = interop->next) { const LRLInteropImpl *impl; if (!interop->name || !interop->ident || !interop->options_expr) { /* Previous parse error */ continue; } impl = lrl_interop_lookup(interop->name->loc.start+1, interop->name->loc.length-2); if (!impl) { lrl_err_token(ctx, LRL_Err_InteropNotFound, interop->name); has_broken_interop = 1; interop->ident->flags |= LRL_IdFl_Broken; interop->impl = NULL; interop->options_type = NULL; continue; } interop->impl = impl; interop->options_type = impl->get_options_type(ctx); if (!interop->options_type) { fail("ctxbindvfy_nointeropoptionstype"); } ctx->has_parsed = 1; } ctx->interop_list = NULL; /* Bind identifiers */ do { /* Repeat "uses" lookups for "uses ... as ..." */ while (lrl_ident_lookup_uses_entries(ctx)) { } } while (lrl_ident_lookup_deferred(ctx)); if (lrl_ident_has_deferred(ctx) && !ctx->has_parsed) { if (!has_broken_interop) { cycle_detected(ctx); } return; } ctx->has_parsed = 0; ctx->has_completed_constexpr = 0; /* Validate types, etc. */ for (file = ctx->files; file; file = file->next) { if (file->completed) continue; ctx->has_deferred = 0; lrl_vfy_namespace(ctx, file->root); if (ctx->errctx) fail("ctxbindvfy_errctx_level"); if (!ctx->has_deferred) file->completed = 1; } } while (lrl_ident_has_deferred(ctx) || ctx->has_completed_constexpr); } LRLErrorHandler *lrl_err_get_handler(LRLCtx *ctx) { return ctx->error_handler; } void lrl_err_set_handler(LRLCtx *ctx, LRLErrorHandler *handler) { ctx->error_handler = handler; }