Main Content

MISRA C++:2023 Rule 7.0.6

Assignment between numeric types shall be appropriate

Since R2024b

Description

Rule Definition

Assignment between numeric types shall be appropriate.

Rationale

Implicit conversions between different data types can result in information loss, change of signedness, undefined behavior, and other implementation-defined behavior. For overloaded functions, when an implicit type conversion of a passed argument occurs, an incorrect function overload can be selected. Avoiding implicit conversion of passed arguments allows the correct function overload to be chosen

Polyspace Implementation

Polyspace® reports a violation when the assignment between objects of numeric type is not considered appropriate as defined in the MISRA™ C++:2023 standard.

To be appropriate, the source and target in an assignment must have the same type when:

  • The source is an argument of a function call and the corresponding function parameter is affected by function overloading. Using the same type allows the correct function overload to be chosen based on the argument type.

    For example:

    // Function overload for int type
    void processNumber(int num) {
        //...
    }
    
    // Function overload for double type
    void processNumber(double num) {
        //...
    }
    
    int main() {
        int myInt = 5;
        double myDouble = 5.5;
    
        // The correct overload is chosen based on the argument type
        processNumber(myInt);    // Compliant, calls the int version
        processNumber(myDouble); // Compliant, calls the double version
    
        return 0;
    }

  • A function call passes the source to the ellipsis parameter of a function. In this scenario, the target type must be the promoted type of the source as the source automatically converts to a promoted type before the assignment.

    For example:

    #include <cstdarg> 
      
    double average(int count, ...) 
    { 
        // calculate and return average
    } 
      
    
    int main() 
    { 
        int x, y = 2;
        double avg = average(6, x, y);     // Compliant, all types match
      
        return 0; 
    } 
    

All other assignments must meet one of these criteria:

  • The source and target have types of the same type category, signedness, and size. MISRA™ defines the following type categories:

    • Character: char, wchar_t, char16_t, and char32_t.

    • Integral: All signed and unsigned integer types, which includes signed char and unsigned char.

    • Floating: float, double, long double.

    • Other: bool, void, and nullptr_t.

  • If the source and target do not have the same size, the source size must be smaller than the target size and the source must be an id-expression.

    int main() {
        int16_t smallNumber = 12345; // Source: 16-bit signed integer (smaller size)
        int32_t largeNumber;         // Target: 32-bit signed integer (larger size)
    
        largeNumber = smallNumber;   // Compliant
    
        return 0;
    }

  • If the source is an integer constant expression, the target has a numeric type with a large enough range to represent the value, or a bit-field with a value representation width and signedness that can represent the value.

    int main() {
        // Integer constant expression
        const int source = 100;
    
        // Target: Numeric type with a large enough range
        long target; 
    
        target = source; // Compliant
        return 0;
    }

    In this case, the switch statement takes an int8_t argument. However, case 128 is outside the values that can be represented by an int8_t value.

    int main() {
        int8_t test;
    
        switch(test)
    	{
    		case 1:		//Compliant
    			//...
    			break;
    		case 128:	//Noncompliant
    			//...
    			break;
    		case -128:	//Compliant
    			//...
    			break;
    		default:
    			break;
    	}
    
        return 0;
    }

Because floating-point types often cannot represent values exactly, loss of precision when assigning a constant value is not a violation of this rule as long as the source is within the range of the target type.

An exception is assigning a value to a parameter within a call to a constructor with a single numeric argument. In this case, the target type can be a wider version of the source type provided that the class has no constructors that are callable with a single argument, other than the copy and move constructors. This exception enables the creation of a class object which can then be passed to functions as an argument without the need of an explicit type conversion to widen the source type.

Troubleshooting

If you expect a rule violation but Polyspace does not report it, see Diagnose Why Coding Standard Violations Do Not Appear as Expected.

Examples

expand all

void exampleA(int num) {
	//...
}
void exampleA(double num) {
	//...
}


int main() {
    short s = 10; 
    int i = s;                             // Compliant

    unsigned int uInt = 300;
    int signedFromUnsigned = uInt;         // Noncompliant
	
    short exArg = 10;
    exampleA(exArg);                       // Noncompliant

    return 0;
}

In this example:

  • The assignment int i = s is compliant because the target type int has a larger size than the source type short.

  • The assignment int signedFromUnsigned = uInt is noncompliant because the variables can represent values of different signedness.

  • The function call exampleA(exArg) is noncompliant because there are two overloaded functions named exampleA(), one taking an int and one taking a double. Due to implicit conversion, the exArg is converted from short to int and the exampleA(int num) function is chosen. This choice depends on the implicit conversion.

Check Information

Group: Standard Conversions
Category: Required

Version History

Introduced in R2024b