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:

Integer type ranges

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.