Main Content

AUTOSAR C++14 Rule A15-4-2

If a function is declared to be noexcept, noexcept(true) or noexcept(<true condition>), then it shall not exit with an exception

Description

Rule Definition

If a function is declared to be noexcept, noexcept(true) or noexcept(<true condition>), then it shall not exit with an exception.

Rationale

You can specify that a callable entity does not raise an exception by specifying it as noexcept, or noexcept(true), or noexcept(<true condition>). The compiler expects that a noexcept function does not exit with an exception. Based on this assumption, the compiler omits the exception handing process for noexcept functions. When a noexcept function exits with an exception, the exception becomes unhandled.

If a noexcept function exits with an exception, the compiler invokes std::terminate() implicitly. The function std::terminate() terminates the program execution in an implementation-defined manner. That is, the exact process of program termination depends on the particular set of software and hardware that you use. For instance, std:terminate() might invoke std::abort() to abnormally abort the execution without unwinding the stack, leading to resource leak and security vulnerabilities.

Specify functions as noexcept or noexcept(true) only when you know the functions raise no exceptions. If you cannot determine the exception specification of a function, specify it by using noexcept(false).

Polyspace Implementation

If you specify a callable entity by using noexcept, noexcept(true), or noexcept(<true condition>), Polyspace® checks the callable entity for unhandled exceptions and flags the callable entity if it might exit with an exception.

When a callable entity invokes other callable entities, Polyspace makes certain assumptions to calculate whether there might be unhandled exceptions.

  • Functions: When a noexcept function calls another function, Polyspace checks whether the called function might raise an exception only if it is specified as noexcept(<false>). If the called function is specified as noexcept, Polyspace assumes that it does not raise an exception. Some standard library functions, such as the constructor of std::string, use pointers to functions to perform memory allocation, which might raise exceptions. Because these functions are not specified as noexcept(<false>), Polyspace does not flag a function that calls these standard library functions.

  • External function: When a noexcept function calls an external function, Polyspace flags the function declaration if the external function is specified as noexcept(<false>).

  • Virtual function: When a function calls a virtual function, Polyspace flags the function declaration if the virtual function is specified as noexcept(<false>) in a derived class. For instance, if a noexcept function calls a virtual function that is declared as noexcept(<true>) in the base class, and noexcept(<false>) in a subsequent derived class, Polyspace flags the declaration of the noexcept function.

  • Pointers to function: When a noexcept function invokes a pointer to a function, Polyspace assumes that the pointer to function does not raise exceptions.

When analyzing whether a function raises unhandled exceptions, Polyspace ignores:

  • Exceptions raised in destructors

  • Exceptions raised in atexit() operations

Polyspace also ignores the dynamic context when checking for exceptions. For instance, a function might raise unhandled exceptions that arise only in certain dynamic contexts. Polyspace flags such a function even if the exception might not be raised.

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

This example shows how Polyspace flags noexcept functions that might raise unhandled exceptions. Consider this code containing several noexcept functions. These functions invoke other callable entities like functions, external functions, and virtual functions.

#include <stdexcept>
#include <typeinfo>
void LibraryFunc(); 
void LibraryFunc_noexcept_false() noexcept(false);  
void LibraryFunc_noexcept_true() noexcept(true);    



void SpecFalseCT() noexcept  // Noncompliant
{
	try {
		LibraryFunc_noexcept_false();
	} catch (int &e) {
		LibraryFunc_noexcept_false();  
	} catch (std::exception &e) {
	} catch (...) {
	}
}

class A {
public:
	virtual void f() {}              
};

class B : A {
public:
	virtual void f() noexcept {}     
};

class C : B {
public:
	virtual void f() noexcept {}     
};

class D : A {
public:
	virtual void f() noexcept(false) { throw(2);}
};

void A1(A &a) noexcept {          // Noncompliant
	a.f();
}

void D2(D &d) noexcept {          //Compliant
	try {
		d.f();
	} catch (int i) {
	} catch (...) {
	}
}

void B2(B *b) noexcept {          // Compliant
	b->f();
}
template <class T>
T f_tp(T a) noexcept(sizeof(T)<=4)    // Noncompliant
{
	if (sizeof(T) >4 ) {
		throw std::runtime_error("invalid case");
	}
	return a;
}
void instantiate(void)
{
	f_tp<char>(1);
}
void f() noexcept {               //Noncompliant
	throw std::runtime_error("dead code");
}

void g() noexcept {               // Compliant
	f();
}  

  • Polyspace flags the declaration of the function template f_tp because:

    • The condition sizeof(T)<=4 evaluates to true for char so the template becomes a noexcept(true) function.

    • Polyspace analyzes the noexcept(true) instance of the template statically. Polyspace deduces that the template might raise an exception because of the throw statement, even though the condition sizeof(T)>4 is false. That is, Polyspace flags the template even though the throw statement is never reached.

    Polyspace ignores function templates that are not instantiated.

  • Polyspace flags the noexcept function SpecFaleCT() because this function calls the noexcept(false) external function LibraryFunc_noexcept_false() without encapsulating it in a try-catch block. Any exceptions raised by this call to the external function might raise an unhandled exception.

  • Polyspace flags the declaration of the noexcept function A1() because this function might call the noexcept(false) function D.f() when the input parameter a is of class D. Depending on the class of the input parameter, the noexcept polymorphic function A1() might raise an unhandled exception.

  • Polyspace flags the function f() because it is a noexcept function that uses throw to raise an unhandled exception. Polyspace does not flag the noexcept function g() even though it calls f() because f() is specified as noexcept.

  • Polyspace does not flag the noexcept function D2() even though it calls the noexcept(false) function D.f() because D2() handles the exceptions that might arise by using a catch(...) block.

Check Information

Group: Exception Handling
Category: Required, Automated