diff --git a/include/asm/symbol.hpp b/include/asm/symbol.hpp index 2a1c5e19..15bdb477 100644 --- a/include/asm/symbol.hpp +++ b/include/asm/symbol.hpp @@ -95,6 +95,8 @@ Symbol *sym_Ref(std::string const &symName); Symbol *sym_AddString(std::string const &symName, std::shared_ptr value); Symbol *sym_RedefString(std::string const &symName, std::shared_ptr value); void sym_Purge(std::string const &symName); +bool sym_IsPurgedExact(std::string const &symName); +bool sym_IsPurgedScoped(std::string const &symName); void sym_Init(time_t now); // Functions to save and restore the current symbol scope. diff --git a/src/asm/fstack.cpp b/src/asm/fstack.cpp index 4365de82..452ff36a 100644 --- a/src/asm/fstack.cpp +++ b/src/asm/fstack.cpp @@ -333,7 +333,10 @@ void fstk_RunMacro(std::string const ¯oName, std::shared_ptr macr Symbol *macro = sym_FindExactSymbol(macroName); if (!macro) { - error("Macro \"%s\" not defined\n", macroName.c_str()); + if (sym_IsPurgedExact(macroName)) + error("Macro \"%s\" not defined; it was purged\n", macroName.c_str()); + else + error("Macro \"%s\" not defined\n", macroName.c_str()); return; } if (macro->type != SYM_MACRO) { diff --git a/src/asm/lexer.cpp b/src/asm/lexer.cpp index 37fa3ace..8768d973 100644 --- a/src/asm/lexer.cpp +++ b/src/asm/lexer.cpp @@ -604,7 +604,10 @@ static uint32_t readBracketedMacroArgNum() { Symbol const *sym = sym_FindScopedValidSymbol(symName); if (!sym) { - error("Bracketed symbol \"%s\" does not exist\n", symName.c_str()); + if (sym_IsPurgedScoped(symName)) + error("Bracketed symbol \"%s\" does not exist; it was purged\n", symName.c_str()); + else + error("Bracketed symbol \"%s\" does not exist\n", symName.c_str()); num = 0; symbolError = true; } else if (!sym->isNumeric()) { @@ -1209,7 +1212,10 @@ static std::shared_ptr readInterpolation(size_t depth) { Symbol const *sym = sym_FindScopedValidSymbol(fmtBuf); if (!sym || !sym->isDefined()) { - error("Interpolated symbol \"%s\" does not exist\n", fmtBuf.c_str()); + if (sym_IsPurgedScoped(fmtBuf)) + error("Interpolated symbol \"%s\" does not exist; it was purged\n", fmtBuf.c_str()); + else + error("Interpolated symbol \"%s\" does not exist\n", fmtBuf.c_str()); } else if (sym->type == SYM_EQUS) { auto buf = std::make_shared(); fmt.appendString(*buf, *sym->getEqus()); diff --git a/src/asm/parser.y b/src/asm/parser.y index 0dfc0727..4cb64dff 100644 --- a/src/asm/parser.y +++ b/src/asm/parser.y @@ -1527,8 +1527,12 @@ string: | POP_SECTION LPAREN scoped_anon_id RPAREN { Symbol *sym = sym_FindScopedValidSymbol($3); - if (!sym) - fatalerror("Unknown symbol \"%s\"\n", $3.c_str()); + if (!sym) { + if (sym_IsPurgedScoped($3)) + fatalerror("Unknown symbol \"%s\"; it was purged\n", $3.c_str()); + else + fatalerror("Unknown symbol \"%s\"\n", $3.c_str()); + } Section const *section = sym->getSection(); if (!section) diff --git a/src/asm/rpn.cpp b/src/asm/rpn.cpp index 4fbae875..54ce92e2 100644 --- a/src/asm/rpn.cpp +++ b/src/asm/rpn.cpp @@ -82,6 +82,8 @@ void Expression::makeSymbol(std::string const &symName) { isSymbol = true; data = sym_IsPC(sym) ? "PC is not constant at assembly time" + : sym_IsPurgedScoped(symName) + ? "'"s + symName + "' is not constant at assembly time; it was purged" : "'"s + symName + "' is not constant at assembly time"; sym = sym_Ref(symName); @@ -122,7 +124,9 @@ void Expression::makeBankSymbol(std::string const &symName) { // Symbol's section is known and bank is fixed data = (int32_t)sym->getSection()->bank; } else { - data = "\""s + symName + "\"'s bank is not known"; + data = sym_IsPurgedScoped(symName) + ? "\""s + symName + "\"'s bank is not known; it was purged" + : "\""s + symName + "\"'s bank is not known"; size_t nameLen = sym->name.length() + 1; // Room for NUL! diff --git a/src/asm/symbol.cpp b/src/asm/symbol.cpp index ab60279b..7dfb00a6 100644 --- a/src/asm/symbol.cpp +++ b/src/asm/symbol.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "error.hpp" #include "helpers.hpp" // assume @@ -19,6 +20,7 @@ using namespace std::literals; std::unordered_map symbols; +std::unordered_set purgedSymbols; static std::optional labelScope = std::nullopt; // Current section's label scope static Symbol *PCSymbol; @@ -158,12 +160,14 @@ Symbol const *sym_GetPC() { return PCSymbol; } -// Purge a symbol void sym_Purge(std::string const &symName) { Symbol *sym = sym_FindScopedValidSymbol(symName); if (!sym) { - error("'%s' not defined\n", symName.c_str()); + if (sym_IsPurgedScoped(symName)) + error("'%s' was already purged\n", symName.c_str()); + else + error("'%s' not defined\n", symName.c_str()); } else if (sym->isBuiltin) { error("Built-in symbol '%s' cannot be purged\n", symName.c_str()); } else if (sym->ID != (uint32_t)-1) { @@ -176,10 +180,27 @@ void sym_Purge(std::string const &symName) { // Do not keep a reference to the label's name after purging it if (sym->name == labelScope) labelScope = std::nullopt; + purgedSymbols.emplace(sym->name); symbols.erase(sym->name); } } +bool sym_IsPurgedExact(std::string const &symName) { + return purgedSymbols.find(symName) != purgedSymbols.end(); +} + +bool sym_IsPurgedScoped(std::string const &symName) { + if (size_t dotPos = symName.find('.'); dotPos != std::string::npos) { + // Check for a nonsensical reference to a nested scoped symbol + if (symName.find('.', dotPos + 1) != std::string::npos) + return false; + // If auto-scoped local label, expand the name + if (dotPos == 0 && labelScope) + return sym_IsPurgedExact(*labelScope + symName); + } + return sym_IsPurgedExact(symName); +} + uint32_t sym_GetPCValue() { Section const *sect = sect_GetSymbolSection(); @@ -218,7 +239,10 @@ uint32_t sym_GetConstantValue(std::string const &symName) { if (Symbol const *sym = sym_FindScopedSymbol(symName); sym) return sym->getConstantValue(); - error("'%s' not defined\n", symName.c_str()); + if (sym_IsPurgedScoped(symName)) + error("'%s' not defined; it was purged\n", symName.c_str()); + else + error("'%s' not defined\n", symName.c_str()); return 0; } @@ -242,6 +266,7 @@ static Symbol *createNonrelocSymbol(std::string const &symName, bool numeric) { if (!sym) { sym = &createSymbol(symName); + purgedSymbols.erase(sym->name); } else if (sym->isDefined()) { error("'%s' already defined at ", symName.c_str()); dumpFilename(*sym); diff --git a/test/asm/local-purge.err b/test/asm/local-purge.err index 55064fd3..5857c42f 100644 --- a/test/asm/local-purge.err +++ b/test/asm/local-purge.err @@ -1,5 +1,5 @@ warning: local-purge.asm(7): [-Wpurge] Purging a label ".loc" error: local-purge.asm(8): - Interpolated symbol ".loc" does not exist + Interpolated symbol ".loc" does not exist; it was purged error: Assembly aborted (1 error)! diff --git a/test/asm/sym-collision.err b/test/asm/sym-collision.err index 7054acfd..fd3d0461 100644 --- a/test/asm/sym-collision.err +++ b/test/asm/sym-collision.err @@ -1,5 +1,5 @@ warning: sym-collision.asm(20): [-Wpurge] Purging a label "dork" error: sym-collision.asm(25): - Interpolated symbol "dork" does not exist + Interpolated symbol "dork" does not exist; it was purged error: Assembly aborted (1 error)!