P2026¶
- Code
P2026
- Message
Constant value overflows the target type
This error occurs when a constant does not fit the type it is stored into.
Every integer type holds a fixed range of values:
Type |
Minimum |
Maximum |
|---|---|---|
SINT |
-128 |
127 |
INT |
-32768 |
32767 |
DINT |
-2147483648 |
2147483647 |
LINT |
-9223372036854775808 |
9223372036854775807 |
USINT |
0 |
255 |
UINT |
0 |
65535 |
UDINT |
0 |
4294967295 |
ULINT |
0 |
18446744073709551615 |
Storing a value outside that range would silently keep the low bits and
discard the rest, so c : SINT := 200 would hold -56. Nothing in the
program says the value changes, so the compiler reports it instead.
Example¶
The following code will generate error P2026:
PROGRAM main
VAR
count : USINT := 300;
total : SINT;
END_VAR
total := 200; (* Error: a SINT holds -128 to 127 *)
END_PROGRAM
To fix this error, use a value the type can hold:
PROGRAM main
VAR
count : USINT := 200;
END_VAR
END_PROGRAM
Or declare a type wide enough for the value you want:
PROGRAM main
VAR
count : UINT := 300;
END_VAR
END_PROGRAM
Where the check applies¶
The type a value is stored or compared as bounds the constants written for it. That includes an operand of an operator, because the operator does not widen the type:
PROGRAM main
VAR
count : USINT;
END_VAR
count := 255 + 1; (* Error: the sum is not a USINT *)
END_PROGRAM
It also includes a comparison and a CASE label, where a constant the
variable can never equal makes a branch that can never run:
PROGRAM main
VAR
total : SINT;
result : DINT;
END_VAR
IF total = 200 THEN (* Error: a SINT is never 200 *)
result := 1;
END_IF;
END_PROGRAM
A CASE label is checked by its value whatever radix it is written in,
and so is each bound of a subrange label. 16#FFFFFFFF is 4294967295, not
the -1 its bits would read as in a DINT:
PROGRAM main
VAR
selector : DINT;
small : SINT;
result : DINT;
END_VAR
CASE selector OF
16#FFFFFFFF: result := 1; (* Error: a DINT is never 4294967295 *)
END_CASE;
CASE small OF
100..300: result := 2; (* Error: a SINT is never 300 *)
END_CASE;
END_PROGRAM
The same holds wherever a constant is stored: an element of an array
initializer (including each repetition, as in [2(300)]), an element of a
structure or function block instance initializer, the default of a structure
field or a type declaration, and an argument passed to a function or function
block input, which is checked against the parameter’s type:
TYPE
Small : USINT := 300; (* Error: a USINT holds 0 to 255 *)
END_TYPE
FUNCTION Scale : USINT
VAR_INPUT
value : USINT;
END_VAR
Scale := value;
END_FUNCTION
PROGRAM main
VAR
readings : ARRAY[1..2] OF USINT := [1, 300]; (* Error *)
result : USINT;
END_VAR
result := Scale(value := 300); (* Error: the parameter is a USINT *)
END_PROGRAM
A generic parameter of a standard function, such as the ANY_NUM inputs of
ADD, states no range of its own, so its argument is not checked.
A subrange type states its own range, and a constant outside it is reported the same way:
TYPE
Ratio : INT(0..10);
END_TYPE
PROGRAM main
VAR
r : Ratio := 20; (* Error: Ratio holds 0 to 10 *)
END_VAR
END_PROGRAM
A prefixed literal states its own type¶
An integer literal can name its type with a prefix, as in INT#42. The
prefix is a statement about the value, so the value is checked against the
prefixed type as well as against the type it is stored into. INT#40000 is
not an INT, whatever it is assigned to:
PROGRAM main
VAR
wide : DINT := INT#40000; (* Error: an INT holds -32768 to 32767 *)
END_VAR
END_PROGRAM
To fix this error, name the type the value belongs to, or drop the prefix so that the destination alone decides:
PROGRAM main
VAR
wide : DINT := DINT#40000;
other : DINT := 40000;
END_VAR
END_PROGRAM
The two checks are independent. A literal that fits its own type but not the
destination, such as small : SINT := INT#200, is reported against the
destination only, and one that fits neither is reported once for each.
A radix form with an integer type prefix is checked by its value in the same
way: INT#16#FFFF is 65535, which no INT can hold. A bit pattern that
is meant to fill the word is spelled with a bit-string prefix, such as
WORD#16#FFFF, which is not checked.
Bit strings are not checked¶
BYTE, WORD, DWORD and LWORD hold bit patterns rather than
magnitudes, and wrapping one is a legitimate thing for a program to want. A
value too large for a bit string keeps its low bits without a diagnostic:
PROGRAM main
VAR
pattern : BYTE;
END_VAR
pattern := 255 + 1; (* Accepted: a BYTE wraps to 0 *)
END_PROGRAM
How a constant was written makes no difference to the check. 16#1FF is
511 whichever radix it was spelled in, and a USINT cannot hold 511.
Think IronPLC is wrong about this?
If you believe this diagnostic is incorrect, open an issue on GitHub with a small sample that demonstrates the problem.