Testing if an expression has side effects at compile time

Christopher Bazley chris.bazley@arm.com
Fri Jan 9 15:39:28 GMT 2026


Hi Alex,

On 09/01/2026 12:07, Alejandro Colomar via Gcc wrote:
> Hi!
> 
> I'm trying to find a way to verify that an expression (usually passed as
> an argument to a macro) has no side effects.  Is this possible?
> 
> Let's say I want to implement strnul():
> 
> 	#define strnulA(s)  (s + strlen(s))  // Problem: evaluates twice
> 
> 	#define strnulB(s)        \
> 	({                        \
> 		auto  s_ = s;     \
> 		s_ + strlen(s_);  \
> 	})
> 
> The problem with strnulB() is that it has a local variable 's_'.  This
> means I can't pass an argument called s_.  That is, the following call
> doesn't work:
> 
> 	strnulB(s_);
> 
> Is there a way to implement something like this?:
> 
> 	#define strnulC(s)                           \
> 	({                                           \
> 		static_assert(!has_side_effects(s)); \
> 		s + strlen(s);                       \
> 	})

I think it would be impossible to implement such a feature perfectly 
because the expression 's' might invoke functions that are defined in 
other translation units (even source code that hasn't been written yet). 
Any implementation would need to be conservative.

> If there's no way, it would be interesting to add compound literals of
> function type to achieve this:
> 
> 	#define strnulD(...)                                          \
> 	((static inline auto                                          \
> 	  (auto s))                                                   \
> 	{                                                             \
> 		return s + strlen(s);                                 \
> 	}(__VA_ARGS__))
> 
> Which guarantees that the argument is evaluated once.

I don't favour statement literals precisely because they provide a 
mechanism for creating fake encapsulation. The 'fakeness' comes from the 
point that you raised: even if the programmer diligently assigns each 
macro argument to a local variable (which is a pain in itself), all of 
the identifiers in the enclosing scope are still in scope.

I would solve the same problem like this, except that I probably 
wouldn't bother with the templates for such a trivial function:

#define STRNUL_TEMPLATE(NAME, QCHAR) \
static inline QCHAR *NAME(QCHAR *s) \
{ \
     return s + strlen(s); \
}

STRNUL_TEMPLATE(strnulc, const char)
STRNUL_TEMPLATE(strnul, char)

#define STRNUL(S) \
   _Generic(S, \
            char const *: strnulc, \
            char *: strnul)(S)

int main(void)
{
     char s[] = "Mutable",
          *ps = STRNUL(s);
     const char cs[] = "Immutable",
                *pcs = STRNUL(cs);
     return 0;
}

I do support function literals as a more scenic alternative to lambdas, 
but I don't believe the proposal currently before WG14 
(https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3679.pdf) 
incorporates the idea of return type inference. I'd expect typeof
to be used for that purpose instead:

	#define STRNUL(S) \
	(static typeof(S) (typeof(S) s)) \
	{ \
		return s + strlen(s); \
	}(S)

Or maybe, in future C:

	#def STRNUL(S)
	(static typeof(S) (typeof(S) s))
	{
		return s + strlen(s);
	}(S)
         #enddef

Note that typeof never evaluates its operand and it is much simpler than 
C++ templates.

-- 
Christopher Bazley
Staff Software Engineer, GNU Tools Team.
Arm Ltd, 110 Fulbourn Road, Cambridge, CB1 9NJ, UK.
http://www.arm.com/


More information about the Gcc mailing list