P4049¶
- Code
P4049
- Message
Operator is not defined for the operand types
This error occurs when an operator is applied to operand types it is not defined for. The message names the operator and the operand types.
The arithmetic operators +, -, * and / are defined for two
numeric operands (SINT through ULINT, REAL and LREAL) where one
widens to the other, and for the time and date combinations listed on the
ADD,
SUB,
MUL and
DIV pages, such as a
TIME plus a TIME or a DATE minus a DATE. MOD is defined for
two integer operands only. The function forms ADD, SUB, MUL and
DIV are checked the same way and report this error too, naming the two
operand types of the step that fails; MOD(a, b) is checked against its
signature and reports P4026 instead.
Each arithmetic expression is reported once, naming both operand types. When an operand of an expression is itself an expression that failed, the enclosing expression may be reported as well.
The selection functions MIN, MAX, LIMIT, SEL and MUX, and
ATAN2, compute at one type too, and report this error when their inputs
have no common type, naming the function and the type of each input.
Mixed numeric types¶
The following code will generate error P4049:
PROGRAM main
VAR
count : DINT := 7;
factor : REAL := 1.5;
scaled : REAL;
END_VAR
scaled := factor * count;
END_PROGRAM
A DINT does not widen to REAL without loss, because REAL cannot
hold every 32-bit integer exactly, and a REAL does not widen to DINT.
Neither operand can take the other’s type, so the operator is not defined for
the pair. The same applies to a signed and an unsigned integer of the same
width, such as DINT and UDINT. A bare real literal such as 1.5 is
a REAL, so count * 1.5 is reported for the same reason, while an
integer of 16 bits or fewer, such as INT, widens to REAL and is
accepted next to a REAL or a real literal.
To fix this error, convert one operand explicitly, type the literal as an
LREAL, which a DINT widens to (count * LREAL#1.5), or use a type
that holds both:
PROGRAM main
VAR
count : DINT := 7;
factor : REAL := 1.5;
scaled : REAL;
END_VAR
scaled := factor * DINT_TO_REAL(count); (* 10.5 *)
END_PROGRAM
See Type Conversions for which types widen to which.
Selection functions with inputs of different types¶
MIN, MAX, LIMIT, SEL and MUX compare or select between
their inputs, and ATAN2 combines its two, so each computes at one type: a
narrower input widens to a wider one, and the result has the wider type. When
no input’s type holds every other input, the call is reported, naming the
function as the operator and the type of each input by its parameter name.
The selector G of SEL and K of MUX choose between the other
inputs and take no part.
The following code will generate error P4049:
PROGRAM main
VAR
lowest : DINT := -1;
count : UDINT := 4000000000;
largest : LINT;
END_VAR
largest := MAX(lowest, count);
END_PROGRAM
A UDINT above 2147483647 does not fit a DINT, and a negative DINT
does not fit a UDINT, so neither type holds every value of the other and
there is no type at which to compare them.
To fix this error, convert the inputs to a type that holds both:
PROGRAM main
VAR
lowest : DINT := -1;
count : UDINT := 4000000000;
largest : LINT;
END_VAR
largest := MAX(DINT_TO_LINT(lowest), UDINT_TO_LINT(count)); (* 4000000000 *)
END_PROGRAM
A further input of such a type makes the conversion implicit:
LIMIT(lowest, count, ceiling) with ceiling : LINT compares at
LINT, because a DINT and a UDINT both widen to it.
A bare real literal is a REAL, and is accepted or reported with an
integer input as it is with an integer operand: MAX(lowest, 1.5) is this
error, because a DINT does not widen to REAL, while MIN(reading,
1.5) with reading : INT compares two REALs, and MAX(lowest, 1)
two DINTs, because a bare integer literal takes the other input’s type.
Time and date operands¶
The following code will generate error P4049:
PROGRAM main
VAR
interval : TIME := T#2s;
repeats : TIME := T#3s;
total : TIME;
END_VAR
total := interval * repeats;
END_PROGRAM
A duration can be multiplied or divided by a number, but not by another duration, and two dates can be subtracted but not added.
To fix this error, use a number for the factor:
PROGRAM main
VAR
interval : TIME := T#2s;
repeats : DINT := 3;
total : TIME;
END_VAR
total := interval * repeats; (* T#6s *)
END_PROGRAM
Negating a time of day or a date¶
The following code will generate error P4049:
PROGRAM main
VAR
opened : TIME_OF_DAY := TOD#08:00:00;
closed : TIME_OF_DAY;
END_VAR
closed := -opened;
END_PROGRAM
The unary - negates a number or a duration (TIME, LTIME). A time of
day, a date and a date and time are points in time, not amounts, so they have
no negative. The same applies to DATE, DATE_AND_TIME and their long
forms.
To fix this error, negate the duration you mean, such as the time since midnight:
PROGRAM main
VAR
opened : TIME_OF_DAY := TOD#08:00:00;
before : TIME;
END_VAR
before := -(opened - TOD#00:00:00); (* T#-8h *)
END_PROGRAM
Mixing a BOOL with a bit string¶
The following code will generate error P4049:
PROGRAM main
VAR
enabled : BOOL := TRUE;
flags : WORD := 16#00F0;
masked : WORD;
END_VAR
masked := flags AND enabled;
END_PROGRAM
AND, OR and XOR compute on operands of one bit type. A narrower
bit string widens to a wider one, so a BYTE with a WORD computes as a
WORD. A BOOL is a truth value rather than a one-bit string, and it
widens to no bit string, in every dialect. The same holds for AND_THEN
and OR_ELSE, and for the function forms AND, OR and XOR.
To fix this error, choose the bit string the BOOL stands for with
SEL:
PROGRAM main
VAR
enabled : BOOL := TRUE;
flags : WORD := 16#00F0;
masked : WORD;
END_VAR
masked := flags AND SEL(enabled, WORD#0, WORD#16#FFFF); (* 16#00F0 *)
END_PROGRAM
Comparing a reference with a number¶
The following code will generate error P4049:
PROGRAM main
VAR
count : INT;
target : REF_TO INT;
unset : BOOL;
END_VAR
unset := target = 0;
END_PROGRAM
A reference equals NULL or another reference, never a number. This
applies to a CODESYS POINTER TO as well: p <> 0 is an error, in every
dialect.
To fix this error, compare the reference with NULL:
PROGRAM main
VAR
count : INT;
target : REF_TO INT;
unset : BOOL;
END_VAR
unset := target = NULL;
END_PROGRAM
Negating a BOOL¶
The following code will generate error P4049:
PROGRAM main
VAR
running : BOOL := TRUE;
stopped : BOOL;
END_VAR
stopped := -running;
END_PROGRAM
The unary - negates a number or a duration. A BOOL is a truth value,
not a number, so it has no negative.
To fix this error, invert the BOOL with NOT:
PROGRAM main
VAR
running : BOOL := TRUE;
stopped : BOOL;
END_VAR
stopped := NOT running; (* FALSE *)
END_PROGRAM
Remainder of a real number¶
The following code will generate error P4049:
PROGRAM main
VAR
angle : REAL := 400.5;
wrapped : REAL;
END_VAR
wrapped := angle MOD 360.0;
END_PROGRAM
MOD accepts only integer operands. There is no floating-point remainder
operator.
To fix this error, convert the operands to an integer type when an integer remainder is what you want:
PROGRAM main
VAR
angle : REAL := 400.5;
wrapped : DINT;
END_VAR
wrapped := REAL_TO_DINT(angle) MOD 360;
END_PROGRAM
For a floating-point remainder, subtract the truncated quotient. TRUNC drops the fractional part of the
quotient, and the <INT>_TO_<REAL> conversion brings it back to the operand
type:
PROGRAM main
VAR
angle : REAL := 400.5;
wrapped : REAL;
END_VAR
wrapped := angle - DINT_TO_REAL(TRUNC(angle / 360.0)) * 360.0; (* 40.5 *)
END_PROGRAM
The result has the sign of the dividend, as MOD does for integers.
Arithmetic on bit strings¶
The following code will generate error P4049 unless bit-string arithmetic is enabled:
PROGRAM main
VAR
counter : BYTE;
END_VAR
counter := counter + 1;
END_PROGRAM
IEC 61131-3 defines no arithmetic on the bit-string types. CODESYS, TwinCAT and
RuSTy treat a bit string as the unsigned integer of its width, and IronPLC
does the same with --allow-bit-string-arithmetic. See
Enabling Dialects and Features for how to enable it.
Without the flag, declare the variable as an unsigned integer of the same width:
PROGRAM main
VAR
counter : USINT;
END_VAR
counter := counter + 1;
END_PROGRAM
Comparisons¶
The comparison operators =, <>, <, <=, > and >=, and
their function forms EQ, NE, LT, LE, GT and GE, are
defined for two operands where one widens to the other, the same pairs as the
arithmetic operators. The comparison is computed at the wider type.
The following code will generate error P4049:
PROGRAM main
VAR
offset : DINT := -1;
limit : UDINT := 4000000000;
below : BOOL;
END_VAR
below := offset < limit;
END_PROGRAM
Neither DINT nor UDINT holds every value of the other, so there is no
type to compare them at: comparing at DINT reads limit as a negative
number, and comparing at UDINT reads offset as a large positive one.
An untyped literal takes the type of the other operand when that type’s
category holds it (limit > 3), and is reported when it does not
(offset < 1.5).
To fix this error, convert one operand to a type that holds both, here
LINT, which UDINT widens to:
PROGRAM main
VAR
offset : DINT := -1;
limit : UDINT := 4000000000;
below : BOOL;
END_VAR
below := DINT_TO_LINT(offset) < limit; (* TRUE *)
END_PROGRAM
Bit-string operators¶
The bit-string operators AND, OR, XOR and NOT are defined over
the bit-string types (BOOL, BYTE, WORD, DWORD, LWORD). For
these operators, each operand outside that set is reported separately, naming
the expected category and the actual type. The short-circuit operators
AND_THEN and OR_ELSE, which some dialects enable, are checked as
AND and OR are.
The following code will generate error P4049:
PROGRAM main
VAR
flags : DINT := 10;
masked : DINT;
END_VAR
masked := flags AND 3;
END_PROGRAM
The variable flags is a DINT, but AND accepts only bit-string
operands. x AND 3 where x is a DINT does not mean a bitwise AND of
the two numbers.
To fix this error, declare the operands as a bit-string type of the width you want:
PROGRAM main
VAR
flags : BYTE := 16#0A;
masked : BYTE;
END_VAR
masked := flags AND BYTE#16#03; (* 16#02 *)
END_PROGRAM
The same applies to NOT, whose operand is a single bit string:
PROGRAM main
VAR
flags : BYTE := 16#0F;
inverted : BYTE;
END_VAR
inverted := NOT flags; (* 16#F0 *)
END_PROGRAM
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.