Skip to content

Commit 0e22dde

Browse files
committed
Generalize WNEGCONSTCOMP to warn for all constants outside a type's range
1 parent 535e736 commit 0e22dde

File tree

8 files changed

+143
-52
lines changed

8 files changed

+143
-52
lines changed

RELEASENOTES-1.4.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,4 +312,7 @@
312312
- `release 6 6313`
313313
- `release 7 7026`
314314
- `note 6` The warning message for comparing a value of unsigned type to a negative constant has been improved to also warn for unsigned types shorter than 64 bits.
315-
- `release 6 6315`
315+
- `release 6 6315`
316+
- `note 6` Added warnings for comparing an integer to constant values
317+
outside the integer's range. E.g., given a variable `x` of type `uint8`,
318+
warnings will now be reported on expressions like `x >= 256` or `x == 3.14`.

lib/1.4/dml-builtins.dml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2839,7 +2839,8 @@ template register is (_conf_attribute, get, set, shown_desc,
28392839
param _le_byte_order : bool;
28402840
param _is_read_only : bool;
28412841
param mapped : bool;
2842-
param mapped = offset != unmapped_offset;
2842+
// the explicit cast avoids WTYPELIMITS
2843+
param mapped = cast(offset, uint64) != unmapped_offset;
28432844

28442845
shared independent method _size() -> (int) { return this.bitsize / 8; }
28452846
shared method _num_fields() -> (uint32) {

py/dml/ctree.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,10 +1392,18 @@ def make(cls, site, lh, rh):
13921392
and lh.constant and rh.constant):
13931393
return mkBoolConstant(site, cls.eval_const(lh.value, rh.value))
13941394
if lhtype.is_int:
1395+
if rh.constant and rhtype.is_arith and (
1396+
cls.eval_const(lhtype.min(), rh.value)
1397+
== cls.eval_const(lhtype.max(), rh.value)):
1398+
report(WTYPELIMITS(site, rh, lhtype))
13951399
lh_maybe_negative = lhtype.signed
13961400
lh = as_int(lh)
13971401
lhtype = realtype(lh.ctype())
13981402
if rhtype.is_int:
1403+
if lh.constant and lhtype.is_arith and (
1404+
cls.eval_const(lh.value, rhtype.min())
1405+
== cls.eval_const(lh.value, rhtype.max())):
1406+
report(WTYPELIMITS(site, lh, rhtype))
13991407
rh_maybe_negative = rhtype.signed
14001408
rh = as_int(rh)
14011409
rhtype = realtype(rh.ctype())
@@ -1404,9 +1412,7 @@ def make(cls, site, lh, rh):
14041412
and lh_maybe_negative != rh_maybe_negative):
14051413
(signed_expr, unsigned_expr) = ((lh, rh) if lh_maybe_negative
14061414
else (rh, lh))
1407-
if signed_expr.constant and signed_expr.value < 0:
1408-
report(WNEGCONSTCOMP(site, signed_expr,
1409-
unsigned_expr.ctype()))
1415+
14101416
# we must convert (uint64)x < (int64)y to DML_lt(x, y), because
14111417
# C:'s < would do an unsigned comparison. No need to do this if y
14121418
# is a small constant, though.
@@ -1602,10 +1608,18 @@ def make(cls, site, lh, rh):
16021608
AddressOfMethod))
16031609
return mkBoolConstant(site, False)
16041610
if lhtype.is_int:
1611+
if rh.constant and rhtype.is_arith and (
1612+
not (lhtype.min() <= rh.value <= lhtype.max())
1613+
or (rhtype.is_float and rh.value % 1)):
1614+
report(WTYPELIMITS(site, rh, lhtype))
16051615
lh_maybe_negative = lhtype.signed
16061616
lh = as_int(lh)
16071617
lhtype = realtype(lh.ctype())
16081618
if rhtype.is_int:
1619+
if lh.constant and lhtype.is_arith and (
1620+
not (rhtype.min() <= lh.value <= rhtype.max())
1621+
or (lhtype.is_float and lh.value % 1)):
1622+
report(WTYPELIMITS(site, lh, rhtype))
16091623
rh_maybe_negative = rhtype.signed
16101624
rh = as_int(rh)
16111625
rhtype = realtype(rh.ctype())
@@ -1618,8 +1632,6 @@ def make(cls, site, lh, rh):
16181632
# unsigned to a constant literal.
16191633
(signed_expr, unsigned_expr) = ((lh, rh) if lh_maybe_negative
16201634
else (rh, lh))
1621-
if signed_expr.constant and signed_expr.value < 0:
1622-
report(WNEGCONSTCOMP(site, signed_expr, unsigned_expr.ctype()))
16231635
if not (signed_expr.constant and 0 <= signed_expr.value < 1 << 63):
16241636
return mkApply(
16251637
site, mkLit(

py/dml/ctree_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,9 +1186,9 @@ def compare_numeric(self, lh, rh, op, result):
11861186
rh_var = variable('rh', rh.ctype())
11871187
var_cond = op(site, lh_var, rh_var)
11881188
# compare constant and variable
1189-
logging.ignore_warning('WNEGCONSTCOMP')
1189+
logging.ignore_warning('WTYPELIMITS')
11901190
mix_cond = op(site, lh, rh_var)
1191-
logging.enable_warning('WNEGCONSTCOMP')
1191+
logging.enable_warning('WTYPELIMITS')
11921192
self.assertIsInstance(var_cond.ctype(), types.TBool)
11931193
self.assertIsInstance(mix_cond.ctype(), types.TBool)
11941194
return ['%s = %s;' % (lh.ctype().declaration('lh'), lh.read()),

py/dml/messages.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2110,16 +2110,18 @@ def log(self):
21102110
self.other_site,
21112111
"original non-throwing declaration")
21122112

2113-
class WNEGCONSTCOMP(DMLWarning):
2114-
"""DML uses a special method when comparing an unsigned and signed integer,
2115-
meaning that comparing a negative constant to an unsigned integer always
2116-
has the same result, which is usually not the intended behaviour."""
2113+
class WTYPELIMITS(DMLWarning):
2114+
"""When comparing an integer with a constant outside the range of the
2115+
integer's type, the comparison always has the same result. One
2116+
common example is that a value of type `uint64` always is
2117+
considered to be different from -1; in this case, the constant
2118+
can be rewritten as `cast(-1, uint64)`.
2119+
"""
21172120
def __init__(self, site, expr, ty):
2118-
DMLWarning.__init__(self, site)
2121+
super().__init__(site)
21192122
self.expr = expr
21202123
self.ty = ty
2121-
fmt = ("Comparing negative constant to unsigned integer has a constant "
2122-
+ "result")
2124+
fmt = 'Comparison with constant outside the range of data type'
21232125
def log(self):
21242126
DMLError.log(self)
21252127
self.print_site_message(

py/dml/types.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,17 @@ def __init__(self, bits, signed, members=None, const=False):
511511
is_arith = True
512512
is_endian = False
513513

514+
def min(self):
515+
if self.signed:
516+
return -(1 << (self.bits - 1))
517+
else:
518+
return 0
519+
def max(self):
520+
if self.signed:
521+
return (1 << (self.bits - 1)) - 1
522+
else:
523+
return (1 << self.bits) - 1
524+
514525
@property
515526
def is_bitfields(self):
516527
return self.members is not None

test/1.4/errors/T_WNEGCONSTCOMP.dml

Lines changed: 0 additions & 36 deletions
This file was deleted.

test/1.4/errors/T_WTYPELIMITS.dml

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
© 2021 Intel Corporation
3+
SPDX-License-Identifier: MPL-2.0
4+
*/
5+
dml 1.4;
6+
7+
device test;
8+
9+
param inf = 1.0/+0.0;
10+
param nan = inf/inf;
11+
12+
method init() {
13+
local uint64 a = -1;
14+
// Test all operands
15+
/// WARNING WTYPELIMITS
16+
assert a != -1;
17+
/// WARNING WTYPELIMITS
18+
assert -1 != a;
19+
/// WARNING WTYPELIMITS
20+
assert !(-1 == a);
21+
/// WARNING WTYPELIMITS
22+
assert a > -1;
23+
/// WARNING WTYPELIMITS
24+
assert !(a < -1);
25+
/// WARNING WTYPELIMITS
26+
assert a >= -1;
27+
/// WARNING WTYPELIMITS
28+
assert !(a <= -1);
29+
30+
// Upper and lower bounds are tight, regardless if the constant is on RH or
31+
// LH side
32+
local uint8 a8 = -1;
33+
/// WARNING WTYPELIMITS
34+
assert a8 != -1;
35+
/// WARNING WTYPELIMITS
36+
assert -1 != a8;
37+
// no warning
38+
assert a8 != 0;
39+
assert 0 != a8;
40+
/// WARNING WTYPELIMITS
41+
assert a8 != 256;
42+
/// WARNING WTYPELIMITS
43+
assert 256 != a8;
44+
// no warning
45+
assert a8 == 255;
46+
assert 255 == a8;
47+
/// WARNING WTYPELIMITS
48+
assert a8 >= 0;
49+
/// WARNING WTYPELIMITS
50+
assert 0 <= a8;
51+
/// no warning
52+
assert a8 > 0;
53+
assert 0 < a8;
54+
/// WARNING WTYPELIMITS
55+
assert !(a8 > 255);
56+
/// WARNING WTYPELIMITS
57+
assert !(255 < a8);
58+
/// no warning
59+
assert a8 >= 255;
60+
assert 255 <= a8;
61+
62+
// Limits for signed integers are tight
63+
local int8 i8 = 128;
64+
/// WARNING WTYPELIMITS
65+
assert i8 != 128;
66+
// no warning
67+
assert i8 != 127;
68+
/// WARNING WTYPELIMITS
69+
assert i8 != -129;
70+
// no warning
71+
assert i8 == -128;
72+
73+
// Non-integer float constants also give warnings
74+
/// WARNING WTYPELIMITS
75+
assert i8 != 0.1;
76+
/// WARNING WTYPELIMITS
77+
assert 0.1 != i8;
78+
// no warning
79+
assert i8 != 0.0;
80+
assert 0.0 != i8;
81+
82+
// Sanity
83+
local int64 b = -1;
84+
// No warning
85+
assert b == -1;
86+
// No warning
87+
assert !(b < -1);
88+
// No warnings for inf and nan, as they aren't considered constant by DML
89+
assert a < inf;
90+
assert a > -inf;
91+
assert a != -inf;
92+
assert !(a < nan);
93+
assert !(a > nan);
94+
assert a != nan;
95+
96+
// Test workaround
97+
assert a == cast(-1, uint64);
98+
}

0 commit comments

Comments
 (0)