diff --git a/contrib/bash_compl/_rgblink.bash b/contrib/bash_compl/_rgblink.bash
index 9b94fccd..49d28c51 100755
--- a/contrib/bash_compl/_rgblink.bash
+++ b/contrib/bash_compl/_rgblink.bash
@@ -142,6 +142,7 @@ _rgblink_completions() {
mapfile -t COMPREPLY < <(compgen -W "
assert
div
+ large-constant
obsolete
shift
shift-amount
diff --git a/contrib/zsh_compl/_rgblink b/contrib/zsh_compl/_rgblink
index fa4f5831..8818dda5 100644
--- a/contrib/zsh_compl/_rgblink
+++ b/contrib/zsh_compl/_rgblink
@@ -9,6 +9,7 @@ _rgblink_warnings() {
'assert:Warn when WARN-type asserts fail'
'div:Warn when dividing the smallest int by -1'
+ 'large-constant:Warn on constants too large for a signed 32-bit int'
'obsolete:Warn when using deprecated features'
'shift:Warn when shifting negative values'
'shift-amount:Warn when a shift'\''s operand is negative or \> 32'
diff --git a/include/link/warning.hpp b/include/link/warning.hpp
index a19f7a16..4156c164 100644
--- a/include/link/warning.hpp
+++ b/include/link/warning.hpp
@@ -22,11 +22,12 @@ enum WarningLevel {
};
enum WarningID {
- WARNING_ASSERT, // Assertions
- WARNING_DIV, // Undefined division behavior
- WARNING_OBSOLETE, // Obsolete/deprecated things
- WARNING_SHIFT, // Undefined `SHIFT` behavior
- WARNING_SHIFT_AMOUNT, // Strange `SHIFT` amount
+ WARNING_ASSERT, // Assertions
+ WARNING_DIV, // Undefined division behavior
+ WARNING_LARGE_CONSTANT, // Constants too large
+ WARNING_OBSOLETE, // Obsolete/deprecated things
+ WARNING_SHIFT, // Undefined `SHIFT` behavior
+ WARNING_SHIFT_AMOUNT, // Strange `SHIFT` amount
NB_PLAIN_WARNINGS,
@@ -47,6 +48,8 @@ void warning(FileStackNode const *src, uint32_t lineNo, WarningID id, char const
void warning(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...);
[[gnu::format(printf, 1, 2)]]
void warning(char const *fmt, ...);
+[[gnu::format(printf, 2, 3)]]
+void scriptWarning(WarningID id, char const *fmt, ...);
[[gnu::format(printf, 3, 4)]]
void error(FileStackNode const *src, uint32_t lineNo, char const *fmt, ...);
diff --git a/man/rgblink.1 b/man/rgblink.1
index d7826478..a3df1fd3 100644
--- a/man/rgblink.1
+++ b/man/rgblink.1
@@ -369,6 +369,8 @@ for
Warn when dividing the smallest negative integer (-2**31) by -1, which yields itself due to integer overflow.
This warning is enabled by
.Fl Wall .
+.It Fl Wno-large-constant
+Warn when a constant too large to fit in a signed 32-bit integer is encountered.
.It Fl Wno-obsolete
Warn when obsolete features are encountered, which have been deprecated and may later be removed.
.It Fl Wshift
diff --git a/src/link/lexer.cpp b/src/link/lexer.cpp
index c2602dfa..61b19211 100644
--- a/src/link/lexer.cpp
+++ b/src/link/lexer.cpp
@@ -113,9 +113,18 @@ static yy::parser::symbol_type readNumber(int initial, char const *prefix, char
context.file.sbumpc();
}
for (int c = context.file.sgetc(); isDigit(c) || c == '_'; c = context.file.snextc()) {
- if (c != '_') {
- number = number * Base + parseDigit(c);
+ if (c == '_') {
+ continue;
}
+ uint32_t digit = parseDigit(c);
+ if (number > (UINT32_MAX - digit) / Base) {
+ scriptWarning(WARNING_LARGE_CONSTANT, "Integer constant is too large");
+ // Discard any additional digits
+ for (c = context.file.snextc(); isDigit(c) || c == '_';
+ c = context.file.snextc()) {}
+ return yy::parser::make_number(0);
+ }
+ number = number * Base + digit;
}
return yy::parser::make_number(number);
}
diff --git a/src/link/warning.cpp b/src/link/warning.cpp
index 48cdb795..a9b85b7e 100644
--- a/src/link/warning.cpp
+++ b/src/link/warning.cpp
@@ -17,18 +17,19 @@
// clang-format off: nested initializers
Diagnostics warnings = {
.metaWarnings = {
- {"all", LEVEL_ALL },
- {"everything", LEVEL_EVERYTHING},
+ {"all", LEVEL_ALL },
+ {"everything", LEVEL_EVERYTHING},
},
.warningFlags = {
- {"assert", LEVEL_DEFAULT },
- {"div", LEVEL_ALL },
- {"obsolete", LEVEL_DEFAULT },
- {"shift", LEVEL_ALL },
- {"shift-amount", LEVEL_ALL },
+ {"assert", LEVEL_DEFAULT },
+ {"div", LEVEL_ALL },
+ {"large-constant", LEVEL_DEFAULT },
+ {"obsolete", LEVEL_DEFAULT },
+ {"shift", LEVEL_ALL },
+ {"shift-amount", LEVEL_ALL },
// Parametric warnings
- {"truncation", LEVEL_DEFAULT },
- {"truncation", LEVEL_EVERYTHING},
+ {"truncation", LEVEL_DEFAULT },
+ {"truncation", LEVEL_EVERYTHING},
},
.paramWarnings = {
{WARNING_TRUNCATION_1, WARNING_TRUNCATION_2, 1},
@@ -198,3 +199,29 @@ void warning(FileStackNode const *src, uint32_t lineNo, WarningID id, char const
va_end(args);
}
+
+void scriptWarning(WarningID id, char const *fmt, ...) {
+ char const *flag = warnings.warningFlags[id].name;
+ va_list args;
+
+ va_start(args, fmt);
+
+ switch (warnings.getWarningBehavior(id)) {
+ case WarningBehavior::DISABLED:
+ break;
+
+ case WarningBehavior::ENABLED:
+ printDiag(nullptr, 0, fmt, args, "warning", STYLE_YELLOW, "[-W%s]", flag);
+ break;
+
+ case WarningBehavior::ERROR:
+ printDiag(nullptr, 0, fmt, args, "error", STYLE_RED, "[-Werror=%s]", flag);
+
+ warnings.incrementErrors();
+ break;
+ }
+
+ va_end(args);
+
+ lexer_TraceCurrent();
+}
diff --git a/test/link/large-constant.asm b/test/link/large-constant.asm
new file mode 100644
index 00000000..143c8b68
--- /dev/null
+++ b/test/link/large-constant.asm
@@ -0,0 +1,2 @@
+section "test", rom0
+label:: dw label
diff --git a/test/link/large-constant.inc b/test/link/large-constant.inc
new file mode 100644
index 00000000..4fc75d34
--- /dev/null
+++ b/test/link/large-constant.inc
@@ -0,0 +1 @@
+ org $1_ffff_ffff
diff --git a/test/link/large-constant.link b/test/link/large-constant.link
new file mode 100644
index 00000000..63e95e27
--- /dev/null
+++ b/test/link/large-constant.link
@@ -0,0 +1,3 @@
+ROM0
+ INCLUDE "large-constant.inc"
+ "test"
diff --git a/test/link/large-constant.out b/test/link/large-constant.out
new file mode 100644
index 00000000..7c9ebf3b
--- /dev/null
+++ b/test/link/large-constant.out
@@ -0,0 +1,2 @@
+warning: Integer constant is too large [-Wlarge-constant]
+ at large-constant.inc(1) <- large-constant.link(3)