diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index c74d33024..c15526930 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -284,3 +284,9 @@
```
- `release 7 7152`
- `release 6 6461`
+- `note 6` Warning and error messages reported by DMLC now display the tag for
+ the warning/error kind by default. The old behavior of having the tag
+ be omitted now requires passing `--no-tags` to DMLC. With this change, the
+ `-T` flag has become a no-op and will be removed in a future version.
+- `note 6` Added the `WARNING` pragma, which allows for the granular
+ suppression of DMLC warnings for a target line.
diff --git a/doc/1.4/language.md b/doc/1.4/language.md
index d8f6b573f..44847f8b5 100644
--- a/doc/1.4/language.md
+++ b/doc/1.4/language.md
@@ -246,7 +246,8 @@ or, if _`classification`_ is omitted:
A DML line will be affected by every `COVERITY` pragma specified in preceding
-lines, up until the first line not containing any `COVERITY` pragma. For
+lines, up until the first line not containing any `COVERITY` pragma or other
+line-based pragma (like `WARNING`). For
example:
```
/*% COVERITY unreachable %*/
@@ -259,7 +260,50 @@ Any C line corresponding to the call to `some_function(...)` will receive
analysis annotations for `var_deref_model`, `check_return`, and
`copy_paste_error` (with `copy_paste_error` specifically being classified as a
false positive), but not any analysis annotation for `unreachable`, as the empty
-line breaks the consecutive specifications of COVERITY pragmas.
+line breaks the consecutive specifications of pragmas.
+
+### WARNING pragma
+The `WARNING` pragma prevents DMLC from reporting DML warnings of a specified
+kind for a particular DML line.
+
+Note that most warnings are designed to have an idiomatic way to silence them
+without needing to employ the `WARNING` pragma; check the documentation for any
+particular warning in Appendix [Messages](messages.html#warning-messages)
+before employing the pragma.
+
+The syntax for the `WARNING` pragma is as follows:
+
+/*% WARNING tag %*/
+
+where _`tag`_ is the tag of the warning kind to suppress; see Appendix
+[Messages](messages.html#warning-messages).
+
+A DML line will be affected by every `WARNING` pragma specified in preceding
+lines, up until the first line not containing any `WARNING` pragma or other
+line-based pragma (like `COVERITY`). For example:
+```
+param special default -1;
+param then_level default 1;
+
+method check(uint32 i) {
+ /*% WARNING WREDUNDANTLEVEL %*/
+ /*% WARNING WNEGCONSTCOMP %*/
+ log info, 1 then then_level: "%s", i == special ? "special" : "regular";
+}
+```
+In this case, the two `WARNING` pragmas apply *exclusively* to the `log info`
+line. If any line (even an empty line) without a pragma were to be inserted
+between the two `WARNING` pragmas, then it would break up the consecutive
+specifications of pragmas and only `/*% WARNING WNEGCONSTCOMP %*/` would apply
+to the `log info` line.
+
+This example also demonstrates the most compelling kind of use-case for the
+pragma. By itself, this snippet would cause DMLC to warn about the `then` being
+redundant (`WREDUNDANTLEVEL`) and the `i == special` check being impossible to
+satisfy (`WNEGCONSTCOMP`), and yet those pieces of the code can still be
+meaningful if used in a context where the definitions of `special` and
+`then_level` get overridden. So in this case, the warnings fail to identify
+true problems in the code, making it desirable to suppress them.
## The Object Model
diff --git a/doc/1.4/running-dmlc.md b/doc/1.4/running-dmlc.md
index 182cae7ce..59a51e5db 100644
--- a/doc/1.4/running-dmlc.md
+++ b/doc/1.4/running-dmlc.md
@@ -95,11 +95,14 @@ constant. The parameter will appear in the top-level scope.
Output makefile rules describing dependencies.
--T
+--no-tags
-Show tags on warning messages. The tags can be used with
-the `--nowarn` and `--warn` options.
+Don't display the tag associated with each warning or error message.
+The tag identifies the particular kind of warning/error of the message,
+which can be used to look up its documentation in Appendix
+[Messages](messages.html). The `--nowarn` and `--warn` options as well as the
+[`WARNING` pragma](language.html#warning-pragma) also operate on warning tags.
-g
@@ -127,15 +130,15 @@ only used when needed.
-\-warn=*tag*
-Enable selected warnings. The tags can be found using
-the `-T` option.
+Enable selected warnings. The tag of a warning will be displayed before the
+message body, unless `--no-tags` has been passed to DMLC.
-\-nowarn=*tag*
-Suppress selected warnings. The tags can be found using
-the `-T` option.
+Suppress selected warnings. The tag of a warning will be displayed before the
+message body, unless `--no-tags` has been passed to DMLC.
-\-werror
diff --git a/py/dml/dmlc.py b/py/dml/dmlc.py
index 86eab30b0..673dd5c80 100644
--- a/py/dml/dmlc.py
+++ b/py/dml/dmlc.py
@@ -364,12 +364,16 @@ def main(argv):
+ ' dependency generation. Specify multiple times to have multiple'
+ ' targets.')
- # -T
- # Show tags on warning messages. The tags can be used with
- # the -\-nowarn and -\-warn options.
+ # -\-no-tags
+ # Disables showing the tag associated with each error or warning
+ # message.
parser.add_argument(
- '-T', dest='include_tag', action='store_true',
- help='show tags on warning messages')
+ '--no-tags', dest='include_tag', action='store_false',
+ help='disable tags on error and warning messages')
+
+ # Purely for backwards compatibility
+ parser.add_argument('-T', action='store_true', dest=argparse.SUPPRESS,
+ help=argparse.SUPPRESS)
# Deprecated with SIMICS_API > 4.8
parser.add_argument(
@@ -385,8 +389,7 @@ def main(argv):
help='generate artifacts and C code that allow for easier debugging')
# -\-warn=tag
- # Enable selected warnings. The tags can be found using
- # the -T option.
+ # Enable selected warnings.
parser.add_argument(
'--warn', dest='enabled_warnings', action='append',
metavar='TAG',
@@ -394,8 +397,7 @@ def main(argv):
help='enable warning TAG')
# -\-nowarn=tag
- # Suppress selected warnings. The tags can be found using
- # the -T option.
+ # Suppress selected warnings.
parser.add_argument(
'--nowarn', dest='disabled_warnings', action='append',
metavar='TAG',
diff --git a/py/dml/dmlparse.py b/py/dml/dmlparse.py
index de461896d..e143b2fa6 100644
--- a/py/dml/dmlparse.py
+++ b/py/dml/dmlparse.py
@@ -1547,14 +1547,19 @@ def cdecl2_ptr(t):
def cdecl2_vect(t):
'cdecl2 : VECT cdecl2'
if provisional.simics_util_vect not in t.parser.file_info.provisional:
- if not breaking_changes.vect_needs_provisional.enabled:
- vsite = site(t)
- if vsite.dml_version() != (1, 2):
- # defensively suppress warning in 1.2, for
- # compatibility
- report(WEXPERIMENTAL(site(t), 'vect types'))
- else:
- report(EOLDVECT(site(t)))
+ # defensively suppress warning in 1.2, for
+ # compatibility
+ vsite = site(t)
+ if vsite.dml_version() != (1, 2):
+ if not breaking_changes.vect_needs_provisional.enabled:
+ report(WEXPERIMENTAL(vsite,
+ "vect types\nTo suppress this "
+ + "warning, specify "
+ + "'provisional simics_util_vect;' at "
+ + "the top of the file, after "
+ + "'dml 1.4;'"))
+ else:
+ report(EOLDVECT(vsite))
t[0] = ['vect'] + t[2]
@prod_dml12
diff --git a/py/dml/globals.py b/py/dml/globals.py
index 65c131bd5..4c61567fe 100644
--- a/py/dml/globals.py
+++ b/py/dml/globals.py
@@ -66,7 +66,9 @@ def compat_dml12_int(site):
coverity = False
-coverity_pragmas = {}
+# the specified pragmas in the model that operate on a subsequent line.
+# For the moment, all pragmas are of that sort
+line_pragmas = {}
# all warnings are disabled by the --dep flag
ignore_all_warnings = False
diff --git a/py/dml/logging.py b/py/dml/logging.py
index 98cc90979..c76cb0487 100644
--- a/py/dml/logging.py
+++ b/py/dml/logging.py
@@ -13,6 +13,7 @@
'warning_is_ignored',
'enable_warning',
'set_include_tag',
+ 'site_line_pragmas',
'ErrorContext',
'dollar',
@@ -66,6 +67,14 @@ def enable_warning(tag):
def warning_is_ignored(tag):
return dml.globals.ignore_all_warnings or ignored_warnings.get(tag, False)
+def warning_occurrence_is_ignored(warning):
+ tag = warning.tag()
+ if warning_is_ignored(tag):
+ return True
+
+ return any((p == "WARNING" and t == tag
+ for (p, t) in site_line_pragmas(warning.site)))
+
class ErrorContext(object):
__slots__ = ('node', 'site')
@@ -219,7 +228,7 @@ class DMLWarning(LogMessage):
def preprocess(self):
# Don't print anything if the user asked us not to
- if warning_is_ignored(self.tag()):
+ if warning_occurrence_is_ignored(self):
return False
if DMLWarning.next_warning_yields_error:
self.print_site_message(self.site,
@@ -266,6 +275,35 @@ def log(self):
os.path.normcase(arg.loc()) if isinstance(arg, Site) else arg
for arg in self.args]))
+def site_line_pragmas(s):
+ if s is None or isinstance(s, SimpleSite):
+ return []
+
+ pragmas = []
+ filename = s.filename()
+ tgt_lineno = s.lineno
+
+ while (filename, tgt_lineno) in dml.globals.line_pragmas:
+ (start_lineno, inline_pragmas) = dml.globals.line_pragmas[(filename,
+ tgt_lineno)]
+ pragmas.extend(reversed(inline_pragmas))
+
+ # A minor HACK to handle the case of:
+ # /*% PRAGMA foo %*/ /*% PRAGMA
+ # bar %*/
+ # some_statement
+ #
+ # Otherwise 'foo' won't be captured
+ if (start_lineno + 1 < tgt_lineno
+ and ((filename, start_lineno + 1)
+ in dml.globals.line_pragmas)):
+ tgt_lineno = start_lineno + 1
+ else:
+ tgt_lineno = start_lineno
+
+ pragmas.reverse()
+ return pragmas
+
class Site(metaclass=abc.ABCMeta):
__slots__ = ()
@abc.abstractmethod
diff --git a/py/dml/messages.py b/py/dml/messages.py
index 5a277e0c1..d6793b943 100644
--- a/py/dml/messages.py
+++ b/py/dml/messages.py
@@ -1991,6 +1991,13 @@ class EDISCARDREF(DMLError):
fmt = ("'_' can only be used as an assignment target "
+ "(to discard some value)")
+class EWARNING(DMLError):
+ """
+ An invalid warning tag was specified in a WARNING pragma.
+ """
+ version = "1.4"
+ fmt = "Not a valid warning tag: '%s'"
+
#
# WARNINGS (keep these as few as possible)
#
diff --git a/py/dml/output.py b/py/dml/output.py
index e58173700..1dd6e67ff 100644
--- a/py/dml/output.py
+++ b/py/dml/output.py
@@ -6,7 +6,7 @@
from pathlib import Path
import dml.globals
-from .logging import ICE, SimpleSite
+from .logging import ICE, SimpleSite, site_line_pragmas
__all__ = (
'NoOutput',
@@ -185,30 +185,8 @@ def coverity_marker(event, classification=None, site=None):
def coverity_markers(markers, site=None):
site_with_loc = site is not None and not isinstance(site, SimpleSite)
if dml.globals.coverity and site_with_loc:
- custom_markers = []
- filename = site.filename()
- tgt_lineno = site.lineno
-
- while (filename, tgt_lineno) in dml.globals.coverity_pragmas:
- (start_lineno,
- inline_markers) = dml.globals.coverity_pragmas[(filename,
- tgt_lineno)]
- custom_markers.extend(reversed(inline_markers))
-
- # A minor HACK to handle the case of:
- # /*% COVERITY foo %*/ /*% COVERITY
- # bar %*/
- # some_statement
- #
- # Otherwise 'foo' won't be captured
- if (start_lineno + 1 < tgt_lineno
- and ((filename, start_lineno + 1)
- in dml.globals.coverity_pragmas)):
- tgt_lineno = start_lineno + 1
- else:
- tgt_lineno = start_lineno
-
- custom_markers.reverse()
+ custom_markers = [d for (p, d) in site_line_pragmas(site)
+ if p == "COVERITY"]
markers = custom_markers + markers
if dml.globals.coverity and markers:
diff --git a/py/dml/toplevel.py b/py/dml/toplevel.py
index 8e2e347ab..bf5c137f9 100644
--- a/py/dml/toplevel.py
+++ b/py/dml/toplevel.py
@@ -196,8 +196,10 @@ def scan_statements(filename, site, stmts):
# For the moment, we ban usages of * inside pragmas to not make multiple usages
# of pragmas ruin everything. In the future if we introduce pragmas that may
# want to contain * we need to be smarter about this.
-pragma_re = re.compile(r'/\*%\s*([^\s*]+)\s*(?:\s([^*\s][^*]*))?%\*/')
+pragma_re = re.compile(r'/\*%\s*([^\s*]+)\s*(?:\s([^*\s][^*]*?))?\s*%\*/')
+
pragma_coverity_data_re = re.compile(r'^(\S+)\s*(?:\s(\S[\s\S]*))?$')
+warning_coverity_data_re = re.compile(r'^(\S+)$')
def check_bidi(filename, filestr):
for m in bidi_re.finditer(filestr):
@@ -226,25 +228,32 @@ def parse_pragma(filename, start_lineno, end_lineno, pragma, data):
None,
"COVERITY pragma must specify event to suppress, "
+ "and optionally classification"))
- else:
- return ('COVERITY',
- (filename, start_lineno, end_lineno + 1, data.groups()))
+ return None
+ data = data.groups()
+ elif pragma == 'WARNING':
+ data = data and warning_coverity_data_re.match(data)
+ if data is None:
+ report(ESYNTAX(SimpleSite(f"{filename}:{start_lineno}"),
+ None,
+ "WARNING pragma must specify warning to suppress"))
+ return None
+ data = data.group(1)
else:
report(EPRAGMA(SimpleSite(f"{filename}:{start_lineno}"), pragma))
return None
-def process_pragma(t):
- (pragma, data) = t
- if pragma == 'COVERITY':
- (filename, start_lineno, end_lineno, data) = data
- # The first COVERITY pragma we encounter for a given end_lineno is
- # the only one whose starting line may differ.
- (dml.globals.coverity_pragmas
- .setdefault((filename, end_lineno), (start_lineno, []))
- [1].append(data))
- else:
- raise ICE(f'unknown pragma: {pragma}')
+ return (pragma, filename, start_lineno, end_lineno + 1, data)
+def process_pragma(t):
+ (pragma, filename, start_lineno, tgt_lineno, data) = t
+ if pragma == 'WARNING':
+ if not is_warning_tag(data):
+ report(EWARNING(SimpleSite(f'{filename}:{start_lineno}:1'), data))
+ return None
+
+ (dml.globals.line_pragmas
+ .setdefault((filename, tgt_lineno), (start_lineno, []))
+ [1].append((pragma, data)))
def parse_file(dml_filename):
try:
diff --git a/test/1.4/errors/T_EWARNING.dml b/test/1.4/errors/T_EWARNING.dml
new file mode 100644
index 000000000..1e048248b
--- /dev/null
+++ b/test/1.4/errors/T_EWARNING.dml
@@ -0,0 +1,11 @@
+/*
+ © 2026 Intel Corporation
+ SPDX-License-Identifier: MPL-2.0
+*/
+dml 1.4;
+
+/// ERROR EWARNING
+/*% WARNING EAFTER %*/
+/// ERROR EWARNING
+/*% WARNING WHATEVER %*/
+device test;
diff --git a/test/1.4/pragmas/T_WARNING.dml b/test/1.4/pragmas/T_WARNING.dml
new file mode 100644
index 000000000..68f81e49f
--- /dev/null
+++ b/test/1.4/pragmas/T_WARNING.dml
@@ -0,0 +1,47 @@
+/*
+ © 2026 Intel Corporation
+ SPDX-License-Identifier: MPL-2.0
+*/
+
+dml 1.4;
+
+device test;
+
+/// COMPILE-ONLY
+
+template t {
+ param special default -1;
+ param then_level default 1;
+
+ method check(uint32 i) {
+ /// WARNING WREDUNDANTLEVEL
+ log info, 1 then then_level: "%s",
+ /// WARNING WNEGCONSTCOMP
+ i == special ? "special" : "regular";
+
+ // no warning
+ /*% WARNING WREDUNDANTLEVEL %*/
+ /*% WARNING WNEGCONSTCOMP %*/
+ log info, 1 then then_level: "%s", i == special ? "special" : "regular";
+ }
+}
+
+is t;
+method init() {
+ local uint32 x = 0;
+
+ // pragma must immediately precede the line causing the warning, or another
+ // pragma targeting that line
+ /*% WARNING WNEGCONSTCOMP %*/
+ /// WARNING WNEGCONSTCOMP
+ _ = x == -1;
+
+ /*% WARNING WNEGCONSTCOMP
+ %*/
+ /*% COVERITY bar %*/
+ /*% COVERITY baz %*/ /*% WARNING WREDUNDANTLEVEL
+ %*/
+ log info, 1 then 1: "%d", cast(x == -1, uint1);
+
+ dev.check(0);
+}
diff --git a/test/tests.py b/test/tests.py
index 922ac4ab1..59f27ee06 100644
--- a/test/tests.py
+++ b/test/tests.py
@@ -288,7 +288,7 @@ def run_dmlc(self, filename, dmlc_extraargs):
reaper = dmlc_reaper_args(
exitcode_file,
dmlc_timeout_multipliers.get(self.fullname, 1))
- args = ["-T"]
+ args = []
if not line_directives:
args += ["--noline"]
args += dmlc_extraargs