Refactor section creation errors, and simplify output for single errors (#1964)

This commit is contained in:
Rangi
2026-05-21 13:01:08 -04:00
committed by GitHub
parent 728d14879b
commit d56dbbb4bf
20 changed files with 132 additions and 87 deletions
+5 -1
View File
@@ -61,13 +61,17 @@ extern Diagnostics<WarningLevel, WarningID> warnings;
void warning(WarningID id, char const *fmt, ...);
// Used for errors that compromise the whole assembly process by affecting the
// following code, potencially making the assembler generate errors caused by
// following code, potentially making the assembler generate errors caused by
// the first one and unrelated to the code that the assembler complains about.
// It is also used when the assembler goes into an invalid state (for example,
// when it fails to allocate memory).
[[gnu::format(printf, 1, 2), noreturn]]
void fatal(char const *fmt, ...);
// Used for fatal errors that handle their own backtrace output.
[[noreturn]]
void fatalNoTrace(std::function<void()> callback);
// Used for errors that make it impossible to assemble correctly, but don't
// affect the following code. The code will fail to assemble but the user will
// get a list of all errors at the end, making it easier to fix all of them at
+93 -41
View File
@@ -9,6 +9,7 @@
#include <iterator>
#include <optional>
#include <stack>
#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -114,16 +115,33 @@ Section *sect_FindSectionByName(std::string const &name) {
return index ? &sections[*index] : nullptr;
}
#define sectError(...) \
do { \
error(__VA_ARGS__); \
++nbSectErrors; \
} while (0)
static std::vector<std::string> sectErrors;
static unsigned int
mergeSectUnion(Section &sect, uint32_t org, uint8_t alignment, uint16_t alignOffset) {
unsigned int nbSectErrors = 0;
// Ideally we'd use a variadic template function and `std::forward` the variadic arguments to
// `snprintf`; but passing `fmt` to `snprintf` triggers a `-Wformat-security` warning which we
// can't prevent because GCC only supports the `[[gnu::format(printf, 1, 2)]]` attribute on
// C-style variadic functions, not on variadic templates; so we have to use `vsnprintf`.
void sectError(char const *fmt, ...) {
std::string result;
va_list args1, args2;
va_start(args1, fmt);
va_copy(args2, args1);
int len = vsnprintf(nullptr, 0, fmt, args1);
va_end(args1);
if (len < 0) {
// LCOV_EXCL_START
va_end(args2);
fatal("Error describing the error that occurred when merging a section");
// LCOV_EXCL_STOP
} else if (len > 0) {
result.resize(len);
vsnprintf(result.data(), len + 1, fmt, args2);
}
va_end(args2);
sectErrors.push_back(result);
}
static void mergeSectUnion(Section &sect, uint32_t org, uint8_t alignment, uint16_t alignOffset) {
assume(alignment < 16); // Should be ensured by the caller
uint32_t alignSize = 1u << alignment;
uint32_t alignMask = alignSize - 1;
@@ -136,11 +154,15 @@ static unsigned int
// If both are fixed, they must be the same
if (sect.org != UINT32_MAX && sect.org != org) {
sectError(
"Section already declared as fixed at different address $%04" PRIx32, sect.org
"Section \"%s\" already declared as fixed at different address $%04" PRIx32,
sect.name.c_str(),
sect.org
);
} else if (sect.align != 0 && ((org - sect.alignOfs) & sectAlignMask)) {
sectError(
"Section already declared as aligned to %" PRIu32 " bytes (offset %" PRIu16 ")",
"Section \"%s\" already declared as aligned to %" PRIu32 " bytes (offset %" PRIu16
")",
sect.name.c_str(),
sectAlignSize,
sect.alignOfs
);
@@ -154,15 +176,17 @@ static unsigned int
if (sect.org != UINT32_MAX) {
if ((sect.org - alignOffset) & alignMask) {
sectError(
"Section already declared as fixed at incompatible address $%04" PRIx32,
"Section \"%s\" already declared as fixed at incompatible address $%04" PRIx32,
sect.name.c_str(),
sect.org
);
}
// Check if alignment offsets are compatible
} else if ((alignOffset & sectAlignMask) != (sect.alignOfs & alignMask)) {
sectError(
"Section already declared with incompatible %" PRIu32
"Section \"%s\" already declared with incompatible %" PRIu32
"-byte alignment (offset %" PRIu16 ")",
sect.name.c_str(),
sectAlignSize,
sect.alignOfs
);
@@ -172,14 +196,9 @@ static unsigned int
sect.alignOfs = alignOffset;
}
}
return nbSectErrors;
}
static unsigned int
mergeFragments(Section &sect, uint32_t org, uint8_t alignment, uint16_t alignOffset) {
unsigned int nbSectErrors = 0;
static void mergeFragments(Section &sect, uint32_t org, uint8_t alignment, uint16_t alignOffset) {
assume(alignment < 16); // Should be ensured by the caller
uint32_t alignSize = 1u << alignment;
uint32_t alignMask = alignSize - 1;
@@ -197,11 +216,15 @@ static unsigned int
// If both are fixed, they must be the same
if (sect.org != UINT32_MAX && sect.org != curOrg) {
sectError(
"Section already declared as fixed at incompatible address $%04" PRIx32, sect.org
"Section \"%s\" already declared as fixed at incompatible address $%04" PRIx32,
sect.name.c_str(),
sect.org
);
} else if (sect.align != 0 && ((curOrg - sect.alignOfs) & sectAlignMask)) {
sectError(
"Section already declared as aligned to %" PRIu32 " bytes (offset %" PRIu16 ")",
"Section \"%s\" already declared as aligned to %" PRIu32 " bytes (offset %" PRIu16
")",
sect.name.c_str(),
sectAlignSize,
sect.alignOfs
);
@@ -215,15 +238,17 @@ static unsigned int
if (uint32_t curOfs = (alignOffset - sect.size) & alignMask; sect.org != UINT32_MAX) {
if ((sect.org - curOfs) & alignMask) {
sectError(
"Section already declared as fixed at incompatible address $%04" PRIx32,
"Section \"%s\" already declared as fixed at incompatible address $%04" PRIx32,
sect.name.c_str(),
sect.org
);
}
// Check if alignment offsets are compatible
} else if ((curOfs & sectAlignMask) != (sect.alignOfs & alignMask)) {
sectError(
"Section already declared with incompatible %" PRIu32
"Section \"%s\" already declared with incompatible %" PRIu32
"-byte alignment (offset %" PRIu16 ")",
sect.name.c_str(),
sectAlignSize,
sect.alignOfs
);
@@ -233,8 +258,6 @@ static unsigned int
sect.alignOfs = curOfs;
}
}
return nbSectErrors;
}
static void mergeSections(
@@ -246,23 +269,41 @@ static void mergeSections(
uint16_t alignOffset,
SectionModifier mod
) {
unsigned int nbSectErrors = 0;
sectErrors.clear();
auto sectAlreadyDefinedCallback = [&sect]() {
fprintf(stderr, "Section \"%s\" already defined\n", sect.name.c_str());
fstk_TraceCurrent();
fputs(" and also:\n", stderr);
sect.src->printBacktrace(sect.fileLine);
};
auto sectAlreadyDefinedError = []() {
// The empty string is a sentinel for the `sectAlreadyDefinedCallback` error,
// since it cannot be preformatted as a string
sectErrors.push_back("");
};
if (type != sect.type) {
sectError(
"Section already exists but with type `%s`", sectionTypeInfo[sect.type].name.c_str()
"Section \"%s\" already exists but with type `%s`",
sect.name.c_str(),
sectionTypeInfo[sect.type].name.c_str()
);
}
if (sect.modifier != mod) {
sectError("Section already declared as `SECTION %s`", sectionModNames[sect.modifier]);
sectError(
"Section \"%s\" already declared as `SECTION %s`",
sect.name.c_str(),
sectionModNames[sect.modifier]
);
} else {
switch (mod) {
case SECTION_UNION:
case SECTION_FRAGMENT: {
unsigned int (*merge)(Section &, uint32_t, uint8_t, uint16_t) =
void (*merge)(Section &, uint32_t, uint8_t, uint16_t) =
mod == SECTION_UNION ? mergeSectUnion : mergeFragments;
nbSectErrors += merge(sect, org, alignment, alignOffset);
merge(sect, org, alignment, alignOffset);
// If the section's bank is unspecified, override it
if (sect.bank == UINT32_MAX) {
@@ -270,26 +311,39 @@ static void mergeSections(
}
// If both specify a bank, it must be the same one
else if (bank != UINT32_MAX && sect.bank != bank) {
sectError("Section already declared with different bank %" PRIu32, sect.bank);
sectError(
"Section \"%s\" already declared with different bank %" PRIu32,
sect.name.c_str(),
sect.bank
);
}
break;
}
case SECTION_NORMAL:
errorNoTrace([&]() {
fputs("Section already defined\n", stderr);
fstk_TraceCurrent();
fputs(" and also:\n", stderr);
sect.src->printBacktrace(sect.fileLine);
++nbSectErrors;
});
sectAlreadyDefinedError();
break;
}
}
if (nbSectErrors) {
if (size_t nbSectErrors = sectErrors.size(); nbSectErrors == 1) {
// If there was only one error, print it as a fatal error
if (std::string const &message = sectErrors[0]; message.empty()) {
fatalNoTrace(sectAlreadyDefinedCallback);
} else {
fatal("%s", message.c_str());
}
} else if (nbSectErrors > 1) {
// If there were multiple errors, print each of them, followed by a fatal summary error
for (std::string const &message : sectErrors) {
if (message.empty()) {
errorNoTrace(sectAlreadyDefinedCallback);
} else {
error("%s", message.c_str());
}
}
fatal(
"Cannot create section \"%s\" (%u error%s)",
"Cannot create section \"%s\" (%zu error%s)",
sect.name.c_str(),
nbSectErrors,
nbSectErrors == 1 ? "" : "s"
@@ -297,8 +351,6 @@ static void mergeSections(
}
}
#undef sectError
static Section *createSection(
std::string const &name,
SectionType type,
+10
View File
@@ -131,6 +131,16 @@ void fatal(char const *fmt, ...) {
exit(1);
}
[[noreturn]]
void fatalNoTrace(std::function<void()> callback) {
style_Set(stderr, STYLE_RED, true);
fputs("FATAL: ", stderr);
style_Reset(stderr);
callback();
exit(1);
}
void requireZeroErrors() {
if (warnings.nbErrors != 0) {
style_Set(stderr, STYLE_RED, true);
+1 -3
View File
@@ -1,6 +1,4 @@
error: Section already defined
FATAL: Section "dup" already defined
at duplicate-section-after-literal.asm(7)
and also:
at duplicate-section-after-literal.asm(5)
FATAL: Cannot create section "dup" (1 error)
at duplicate-section-after-literal.asm(7)
+1 -3
View File
@@ -1,6 +1,4 @@
error: Section already defined
FATAL: Section "sec" already defined
at duplicate-section.asm(4)
and also:
at duplicate-section.asm(2)
FATAL: Cannot create section "sec" (1 error)
at duplicate-section.asm(4)
+1 -3
View File
@@ -1,4 +1,2 @@
error: Section already declared as fixed at incompatible address $c002
at fragment-align-mismatch.asm(2)
FATAL: Cannot create section "aligned" (1 error)
FATAL: Section "aligned" already declared as fixed at incompatible address $c002
at fragment-align-mismatch.asm(2)
+1 -3
View File
@@ -1,4 +1,2 @@
error: Section already declared as fixed at incompatible address $0000
at fragment-mismatch.asm(2)
FATAL: Cannot create section "test" (1 error)
FATAL: Section "test" already declared as fixed at incompatible address $0000
at fragment-mismatch.asm(2)
+1 -3
View File
@@ -1,4 +1,2 @@
error: Section already declared with incompatible 256-byte alignment (offset 0)
at incompatible-alignment.asm(8)
FATAL: Cannot create section "Test" (1 error)
FATAL: Section "Test" already declared with incompatible 256-byte alignment (offset 0)
at incompatible-alignment.asm(8)
+3
View File
@@ -0,0 +1,3 @@
SECTION FRAGMENT "test", ROM0[$0000]
SECTION FRAGMENT "test", ROMX[$4000]
+6
View File
@@ -0,0 +1,6 @@
error: Section "test" already exists but with type `ROM0`
at multiple-section-errors.asm(3)
error: Section "test" already declared as fixed at incompatible address $0000
at multiple-section-errors.asm(3)
FATAL: Cannot create section "test" (2 errors)
at multiple-section-errors.asm(3)
+1 -3
View File
@@ -1,4 +1,2 @@
error: Section already declared as fixed at different address $c000
at section-union-mismatch.asm(2)
FATAL: Cannot create section "test" (1 error)
FATAL: Section "test" already declared as fixed at different address $c000
at section-union-mismatch.asm(2)
+1 -3
View File
@@ -1,4 +1,2 @@
error: Section already declared as `SECTION UNION`
at section-union.asm(37)
FATAL: Cannot create section "test" (1 error)
FATAL: Section "test" already declared as `SECTION UNION`
at section-union.asm(37)
+1 -3
View File
@@ -1,4 +1,2 @@
error: Section already declared as fixed at incompatible address $c001
at union-mismatch.asm(2)
FATAL: Cannot create section "fixed" (1 error)
FATAL: Section "fixed" already declared as fixed at incompatible address $c001
at union-mismatch.asm(2)
@@ -4,7 +4,5 @@ FATAL: Section "conflicting alignment" is defined with 4-byte alignment (offset
at section-fragment/align-conflict.asm(7)
Linking aborted with 1 error
---
error: Section already declared as aligned to 4 bytes (offset 0)
at <stdin>(18)
FATAL: Cannot create section "conflicting alignment" (1 error)
FATAL: Section "conflicting alignment" already declared as aligned to 4 bytes (offset 0)
at <stdin>(18)
+1 -3
View File
@@ -4,7 +4,5 @@ FATAL: Section "conflicting alignment" is defined with 4-byte alignment (offset
at section-union/align-conflict.asm(7)
Linking aborted with 1 error
---
error: Section already declared as aligned to 4 bytes (offset 0)
at <stdin>(18)
FATAL: Cannot create section "conflicting alignment" (1 error)
FATAL: Section "conflicting alignment" already declared as aligned to 4 bytes (offset 0)
at <stdin>(18)
@@ -4,7 +4,5 @@ FATAL: Section "conflicting alignment" is defined with 8-byte alignment (offset
at section-union/align-ofs-conflict.asm(7)
Linking aborted with 1 error
---
error: Section already declared with incompatible 8-byte alignment (offset 7)
at <stdin>(18)
FATAL: Cannot create section "conflicting alignment" (1 error)
FATAL: Section "conflicting alignment" already declared with incompatible 8-byte alignment (offset 7)
at <stdin>(18)
+1 -3
View File
@@ -4,7 +4,5 @@ FATAL: Section "conflicting types" is defined with type `HRAM`, but also with ty
at section-union/bad-types.asm(7)
Linking aborted with 1 error
---
error: Section already exists but with type `HRAM`
at <stdin>(18)
FATAL: Cannot create section "conflicting types" (1 error)
FATAL: Section "conflicting types" already exists but with type `HRAM`
at <stdin>(18)
+1 -3
View File
@@ -4,7 +4,5 @@ FATAL: Section "conflicting banks" is defined with bank 4, but also with bank 1
at section-union/bank-conflict.asm(5)
Linking aborted with 1 error
---
error: Section already declared with different bank 4
at <stdin>(14)
FATAL: Cannot create section "conflicting banks" (1 error)
FATAL: Section "conflicting banks" already declared with different bank 4
at <stdin>(14)
+1 -3
View File
@@ -4,7 +4,5 @@ FATAL: Section "conflicting alignment" is defined with 8-byte alignment (offset
at section-union/different-ofs.asm(7)
Linking aborted with 1 error
---
error: Section already declared with incompatible 8-byte alignment (offset 7)
at <stdin>(18)
FATAL: Cannot create section "conflicting alignment" (1 error)
FATAL: Section "conflicting alignment" already declared with incompatible 8-byte alignment (offset 7)
at <stdin>(18)
+1 -3
View File
@@ -4,7 +4,5 @@ FATAL: Section "conflicting address" is defined with address $beef, but also wit
at section-union/org-conflict.asm(7)
Linking aborted with 1 error
---
error: Section already declared as fixed at different address $beef
at <stdin>(16)
FATAL: Cannot create section "conflicting address" (1 error)
FATAL: Section "conflicting address" already declared as fixed at different address $beef
at <stdin>(16)