public inbox for gcc@gcc.gnu.org
 help / color / mirror / Atom feed
* Testing if an expression has side effects at compile time
@ 2026-01-09 12:07 Alejandro Colomar
  2026-01-09 13:37 ` Florian Weimer
                   ` (4 more replies)
  0 siblings, 5 replies; 13+ messages in thread
From: Alejandro Colomar @ 2026-01-09 12:07 UTC (permalink / raw)
  To: gcc

[-- Attachment #1: Type: text/plain, Size: 1370 bytes --]

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);                       \
	})

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.


Have a lovely day!
Alex

-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Testing if an expression has side effects at compile time
  2026-01-09 12:07 Testing if an expression has side effects at compile time Alejandro Colomar
@ 2026-01-09 13:37 ` Florian Weimer
  2026-01-09 13:47   ` Alejandro Colomar
  2026-01-09 15:10 ` David Brown
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 13+ messages in thread
From: Florian Weimer @ 2026-01-09 13:37 UTC (permalink / raw)
  To: Alejandro Colomar via Gcc; +Cc: Alejandro Colomar

* Alejandro Colomar via Gcc:

> 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__))

This already works:

static inline auto
strnulD (auto s)
{
  return s + strlen (s);
}

No need to invent new syntax for it.

Thanks,
Florian


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Testing if an expression has side effects at compile time
  2026-01-09 13:37 ` Florian Weimer
@ 2026-01-09 13:47   ` Alejandro Colomar
  2026-01-09 14:11     ` Richard Earnshaw (foss)
  0 siblings, 1 reply; 13+ messages in thread
From: Alejandro Colomar @ 2026-01-09 13:47 UTC (permalink / raw)
  To: Florian Weimer; +Cc: Alejandro Colomar via Gcc

[-- Attachment #1: Type: text/plain, Size: 1884 bytes --]

Hi Florian,

On Fri, Jan 09, 2026 at 02:37:52PM +0100, Florian Weimer wrote:
> * Alejandro Colomar via Gcc:
> 
> > 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__))
> 
> This already works:
> 
> static inline auto
> strnulD (auto s)
> {
>   return s + strlen (s);
> }

Sorry; I forgot to mention I mean in C, not C++.

For a second, you got me illusioned and thought that might have been
implemented in C.  :-)

	alx@devuan:~/tmp$ cat strnul.c 
	#include <string.h>

	static inline auto
	strnulD(auto s)
	{
		return s + strlen(s);
	}

	int
	main(void)
	{
		const char *p = strnulD("foo");
	}
	alx@devuan:~/tmp$ gcc strnul.c 
	strnul.c:4:14: error: storage class specified for parameter ‘s’
	    4 | strnulD(auto s)
	      |              ^
	strnul.c:3:1: error: ‘auto’ requires a plain identifier, possibly with attributes, as declarator
	    3 | static inline auto
	      | ^~~~~~
	strnul.c: In function ‘main’:
	strnul.c:12:25: error: implicit declaration of function ‘strnulD’ [-Wimplicit-function-declaration]
	   12 |         const char *p = strnulD("foo");
	      |                         ^~~~~~~
	strnul.c:12:25: error: initialization of ‘const char *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]

> No need to invent new syntax for it.

Any chance we can get this in GNU C?


Have a lovely day!
Alex

-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Testing if an expression has side effects at compile time
  2026-01-09 13:47   ` Alejandro Colomar
@ 2026-01-09 14:11     ` Richard Earnshaw (foss)
  2026-01-09 14:20       ` Martin Uecker
  0 siblings, 1 reply; 13+ messages in thread
From: Richard Earnshaw (foss) @ 2026-01-09 14:11 UTC (permalink / raw)
  To: Alejandro Colomar, Florian Weimer; +Cc: Alejandro Colomar via Gcc

On 09/01/2026 13:47, Alejandro Colomar via Gcc wrote:
> Hi Florian,
> 
> On Fri, Jan 09, 2026 at 02:37:52PM +0100, Florian Weimer wrote:
>> * Alejandro Colomar via Gcc:
>>
>>> 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__))
>>
>> This already works:
>>
>> static inline auto
>> strnulD (auto s)
>> {
>>   return s + strlen (s);
>> }
> 
> Sorry; I forgot to mention I mean in C, not C++.
> 
> For a second, you got me illusioned and thought that might have been
> implemented in C.  :-)
> 
> 	alx@devuan:~/tmp$ cat strnul.c 
> 	#include <string.h>
> 
> 	static inline auto
> 	strnulD(auto s)
> 	{
> 		return s + strlen(s);
> 	}
> 
> 	int
> 	main(void)
> 	{
> 		const char *p = strnulD("foo");
> 	}
> 	alx@devuan:~/tmp$ gcc strnul.c 
> 	strnul.c:4:14: error: storage class specified for parameter ‘s’
> 	    4 | strnulD(auto s)
> 	      |              ^
> 	strnul.c:3:1: error: ‘auto’ requires a plain identifier, possibly with attributes, as declarator
> 	    3 | static inline auto
> 	      | ^~~~~~
> 	strnul.c: In function ‘main’:
> 	strnul.c:12:25: error: implicit declaration of function ‘strnulD’ [-Wimplicit-function-declaration]
> 	   12 |         const char *p = strnulD("foo");
> 	      |                         ^~~~~~~
> 	strnul.c:12:25: error: initialization of ‘const char *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
> 
>> No need to invent new syntax for it.
> 
> Any chance we can get this in GNU C?
> 
> 

C23 has auto in C.

R.

> Have a lovely day!
> Alex
> 


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Testing if an expression has side effects at compile time
  2026-01-09 14:11     ` Richard Earnshaw (foss)
@ 2026-01-09 14:20       ` Martin Uecker
  0 siblings, 0 replies; 13+ messages in thread
From: Martin Uecker @ 2026-01-09 14:20 UTC (permalink / raw)
  To: Richard Earnshaw (foss), Alejandro Colomar, Florian Weimer
  Cc: Alejandro Colomar via Gcc

Am Freitag, dem 09.01.2026 um 14:11 +0000 schrieb Richard Earnshaw (foss) via Gcc:
> On 09/01/2026 13:47, Alejandro Colomar via Gcc wrote:
> > Hi Florian,
> > 
> > On Fri, Jan 09, 2026 at 02:37:52PM +0100, Florian Weimer wrote:
> > > * Alejandro Colomar via Gcc:
> > > 
> > > > 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__))
> > > 
> > > This already works:
> > > 
> > > static inline auto
> > > strnulD (auto s)
> > > {
> > >   return s + strlen (s);
> > > }
> > 
> > Sorry; I forgot to mention I mean in C, not C++.
> > 
> > For a second, you got me illusioned and thought that might have been
> > implemented in C.  :-)
> > 
> > 	alx@devuan:~/tmp$ cat strnul.c 
> > 	#include <string.h>
> > 
> > 	static inline auto
> > 	strnulD(auto s)
> > 	{
> > 		return s + strlen(s);
> > 	}
> > 
> > 	int
> > 	main(void)
> > 	{
> > 		const char *p = strnulD("foo");
> > 	}
> > 	alx@devuan:~/tmp$ gcc strnul.c 
> > 	strnul.c:4:14: error: storage class specified for parameter ‘s’
> > 	    4 | strnulD(auto s)
> > 	      |              ^
> > 	strnul.c:3:1: error: ‘auto’ requires a plain identifier, possibly with attributes, as declarator
> > 	    3 | static inline auto
> > 	      | ^~~~~~
> > 	strnul.c: In function ‘main’:
> > 	strnul.c:12:25: error: implicit declaration of function ‘strnulD’ [-Wimplicit-function-declaration]
> > 	   12 |         const char *p = strnulD("foo");
> > 	      |                         ^~~~~~~
> > 	strnul.c:12:25: error: initialization of ‘const char *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversion]
> > 
> > > No need to invent new syntax for it.
> > 
> > Any chance we can get this in GNU C?
> > 
> > 
> 
> C23 has auto in C.
> 
Yes, but a much more limited form. Above function is a template
and I do not really would want to have template in C.

Martin


^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Testing if an expression has side effects at compile time
  2026-01-09 12:07 Testing if an expression has side effects at compile time Alejandro Colomar
  2026-01-09 13:37 ` Florian Weimer
@ 2026-01-09 15:10 ` David Brown
  2026-01-09 15:44   ` Alejandro Colomar
  2026-01-09 15:35 ` Ben Boeckel
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 13+ messages in thread
From: David Brown @ 2026-01-09 15:10 UTC (permalink / raw)
  To: Alejandro Colomar, gcc

On 09/01/2026 13: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_);

That is always the risk of macros.  Usually people just pick parameter 
names and local variable names that are very unlikely to collide.  If 
you have -Winit-self (or -Werror=init-self) then collisions will be 
detected (as "auto s_ = s_;_" now gives a warning or error).

You can also use __LINE__ to generate unique symbols :

#define strnulX(s) ({ \
     auto s__ ## __LINE__ = (s); \
     s__ ## __LINE__ + strlen(s__ ## __LINE__ ); \
     })

But here you can probably just use a static inline function.  It is, 
IME, always better to use a static inline function than a function-like 
macro when possible:

static inline char * strnulY(char * s) {
     return s + strlen(s);
}


<https://godbolt.org/z/oGvcfbh14>


> 
> Is there a way to implement something like this?:
> 
> 	#define strnulC(s)                           \
> 	({                                           \
> 		static_assert(!has_side_effects(s)); \
> 		s + strlen(s);                       \
> 	})
> 
> 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.
> 

Do you need your macro here to work with different types?  I'm not sure 
how that could make sense, since "strlen" is not generic and always 
treats its parameter as a char*.

If you do need something that can work with different types and you want 
to use functions to avoid possible issues with macro parameters, you can 
use _Generic :

#define strnulX(s) _Generic((s), \
		char * : strnulX_char(s), \
		char16_t * strnulX_char16(s), \
		char32_t * strnulX_char32(s)
	)(s)

Then define functions strnulX_char, strnulX_char16, and strnulX_char32 
as separate functions.  (_Generic does not evaluate its argument, so 
side-effects don't matter to it.)



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Testing if an expression has side effects at compile time
  2026-01-09 12:07 Testing if an expression has side effects at compile time Alejandro Colomar
  2026-01-09 13:37 ` Florian Weimer
  2026-01-09 15:10 ` David Brown
@ 2026-01-09 15:35 ` Ben Boeckel
  2026-01-09 15:45   ` Alejandro Colomar
  2026-01-09 15:39 ` Christopher Bazley
  2026-01-09 16:54 ` Tom Tromey
  4 siblings, 1 reply; 13+ messages in thread
From: Ben Boeckel @ 2026-01-09 15:35 UTC (permalink / raw)
  To: Alejandro Colomar; +Cc: gcc

On Fri, Jan 09, 2026 at 13:07:34 +0100, Alejandro Colomar via Gcc wrote:
> Is there a way to implement something like this?:
> 
> 	#define strnulC(s)                           \
> 	({                                           \
> 		static_assert(!has_side_effects(s)); \
> 		s + strlen(s);                       \
> 	})

`__builtin_constant_p` might be overly restrictive, but still helpful.

--Ben

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Testing if an expression has side effects at compile time
  2026-01-09 12:07 Testing if an expression has side effects at compile time Alejandro Colomar
                   ` (2 preceding siblings ...)
  2026-01-09 15:35 ` Ben Boeckel
@ 2026-01-09 15:39 ` Christopher Bazley
  2026-01-09 15:58   ` Alejandro Colomar
  2026-01-09 16:54 ` Tom Tromey
  4 siblings, 1 reply; 13+ messages in thread
From: Christopher Bazley @ 2026-01-09 15:39 UTC (permalink / raw)
  To: gcc; +Cc: Alejandro Colomar


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/

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Testing if an expression has side effects at compile time
  2026-01-09 15:10 ` David Brown
@ 2026-01-09 15:44   ` Alejandro Colomar
  2026-01-09 16:39     ` David Brown
  0 siblings, 1 reply; 13+ messages in thread
From: Alejandro Colomar @ 2026-01-09 15:44 UTC (permalink / raw)
  To: David Brown; +Cc: gcc

[-- Attachment #1: Type: text/plain, Size: 3303 bytes --]

Hi David,

On Fri, Jan 09, 2026 at 04:10:56PM +0100, David Brown wrote:
> On 09/01/2026 13:07, Alejandro Colomar via Gcc wrote:
> > 	#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_);
> 
> That is always the risk of macros.  Usually people just pick parameter names
> and local variable names that are very unlikely to collide.  If you have
> -Winit-self (or -Werror=init-self) then collisions will be detected (as
> "auto s_ = s_;_" now gives a warning or error).

Yup, I have that enabled, and indeed is triggering.

> You can also use __LINE__ to generate unique symbols :
> 
> #define strnulX(s) ({ \
>     auto s__ ## __LINE__ = (s); \
>     s__ ## __LINE__ + strlen(s__ ## __LINE__ ); \
>     })

This doesn't solve it, because it is called from another macro, which
has the same __LINE__ value.

> But here you can probably just use a static inline function.  It is, IME,
> always better to use a static inline function than a function-like macro
> when possible:
> 
> static inline char * strnulY(char * s) {
>     return s + strlen(s);
> }

I can't use this.  I want something that preserves 'const', like the
C23 APIs using QChar.

I could in fact do both a function and a macro, plus all the magic for
doing QChar, but that's too much complexity, and would be better solved
with 'auto' in inline functions.


Have a lovely day!
Alex

> 
> 
> <https://godbolt.org/z/oGvcfbh14>
> 
> 
> > 
> > Is there a way to implement something like this?:
> > 
> > 	#define strnulC(s)                           \
> > 	({                                           \
> > 		static_assert(!has_side_effects(s)); \
> > 		s + strlen(s);                       \
> > 	})
> > 
> > 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.
> > 
> 
> Do you need your macro here to work with different types?  I'm not sure how
> that could make sense, since "strlen" is not generic and always treats its
> parameter as a char*.
> 
> If you do need something that can work with different types and you want to
> use functions to avoid possible issues with macro parameters, you can use
> _Generic :
> 
> #define strnulX(s) _Generic((s), \
> 		char * : strnulX_char(s), \
> 		char16_t * strnulX_char16(s), \
> 		char32_t * strnulX_char32(s)
> 	)(s)
> 
> Then define functions strnulX_char, strnulX_char16, and strnulX_char32 as
> separate functions.  (_Generic does not evaluate its argument, so
> side-effects don't matter to it.)
> 
> 

-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Testing if an expression has side effects at compile time
  2026-01-09 15:35 ` Ben Boeckel
@ 2026-01-09 15:45   ` Alejandro Colomar
  0 siblings, 0 replies; 13+ messages in thread
From: Alejandro Colomar @ 2026-01-09 15:45 UTC (permalink / raw)
  To: Ben Boeckel; +Cc: gcc

[-- Attachment #1: Type: text/plain, Size: 716 bytes --]

Hi Ben,

On Fri, Jan 09, 2026 at 10:35:24AM -0500, Ben Boeckel wrote:
> On Fri, Jan 09, 2026 at 13:07:34 +0100, Alejandro Colomar via Gcc wrote:
> > Is there a way to implement something like this?:
> > 
> > 	#define strnulC(s)                           \
> > 	({                                           \
> > 		static_assert(!has_side_effects(s)); \
> > 		s + strlen(s);                       \
> > 	})
> 
> `__builtin_constant_p` might be overly restrictive, but still helpful.

Because string APIs don't get constant expressions, I don't think it
would work in this case.  But a similar builtin might work.


Have a lovely day!
Alex

> 
> --Ben

-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Testing if an expression has side effects at compile time
  2026-01-09 15:39 ` Christopher Bazley
@ 2026-01-09 15:58   ` Alejandro Colomar
  0 siblings, 0 replies; 13+ messages in thread
From: Alejandro Colomar @ 2026-01-09 15:58 UTC (permalink / raw)
  To: Christopher Bazley; +Cc: gcc

[-- Attachment #1: Type: text/plain, Size: 4808 bytes --]

Hi Chris,

On Fri, Jan 09, 2026 at 03:39:28PM +0000, Christopher Bazley wrote:
> 
> 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)

It can be done simpler than this.  I've implemented the QChar APIs from
libc as simple wrappers around the old functions:

	#define __QVoidptrof(p)  typeof(1?(p):(void*){0})
	#define __QCharptrof(s)  typeof                               \
	(                                                             \
	       _Generic((__QVoidptrof(s)){0},                         \
		       const void *: (const char *) 0,                \
		       void *:       (char *) 0                       \
	       )                                                      \
	)

	define strchr(s, chr)  ((__QCharptrof(s)) strchr(s, chr))

Essentially the same could be done with strnul() by just adding the
function definition:

	#define strnul(s)  ((__QCharptrof(s)) strnul(s))

	char *
	(strnul)(const char s)
	{
		return s + strlen(s);
	}

But I was hoping I could do it even simpler.  A function taking and
returning 'auto' would certainly be simpler.  Maybe I'm trying to
simplify too much, though.

> 
> 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.

Have a lovely day!
Alex

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

-- 
<https://www.alejandro-colomar.es>

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 833 bytes --]

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Testing if an expression has side effects at compile time
  2026-01-09 15:44   ` Alejandro Colomar
@ 2026-01-09 16:39     ` David Brown
  0 siblings, 0 replies; 13+ messages in thread
From: David Brown @ 2026-01-09 16:39 UTC (permalink / raw)
  To: gcc

On 09/01/2026 16:44, Alejandro Colomar via Gcc wrote:
> Hi David,
> 
> On Fri, Jan 09, 2026 at 04:10:56PM +0100, David Brown wrote:
>> On 09/01/2026 13:07, Alejandro Colomar via Gcc wrote:
>>> 	#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_);
>>
>> That is always the risk of macros.  Usually people just pick parameter names
>> and local variable names that are very unlikely to collide.  If you have
>> -Winit-self (or -Werror=init-self) then collisions will be detected (as
>> "auto s_ = s_;_" now gives a warning or error).
> 
> Yup, I have that enabled, and indeed is triggering.
> 
>> You can also use __LINE__ to generate unique symbols :
>>
>> #define strnulX(s) ({ \
>>      auto s__ ## __LINE__ = (s); \
>>      s__ ## __LINE__ + strlen(s__ ## __LINE__ ); \
>>      })
> 
> This doesn't solve it, because it is called from another macro, which
> has the same __LINE__ value.
> 

You are really setting yourself up for a challenge here, aren't you? :-)

>> But here you can probably just use a static inline function.  It is, IME,
>> always better to use a static inline function than a function-like macro
>> when possible:
>>
>> static inline char * strnulY(char * s) {
>>      return s + strlen(s);
>> }
> 
> I can't use this.  I want something that preserves 'const', like the
> C23 APIs using QChar.
> 
> I could in fact do both a function and a macro, plus all the magic for
> doing QChar, but that's too much complexity, and would be better solved
> with 'auto' in inline functions.
> 

static inline char * strnulZ_char(char * s) {
     return s + strlen(s);
}

static inline const char * strnulZ_cchar(const char * s) {
     return s + strlen(s);
}

#define strnulZ(s) _Generic((s), \
         char * : strnulZ_char, \
         const char * : strnulZ_cchar \
     )(s)


Sure, it would be easier if C supported templates, but it is a /lot/ 
easier to write one _Generic macro than for the GCC developers to 
implement templates in C!

You might consider switching to C++ as an alternative, even if you only 
use a few of the language's features (like templates).

David


> 
> Have a lovely day!
> Alex
> 
>>
>>
>> <https://godbolt.org/z/oGvcfbh14>
>>
>>
>>>
>>> Is there a way to implement something like this?:
>>>
>>> 	#define strnulC(s)                           \
>>> 	({                                           \
>>> 		static_assert(!has_side_effects(s)); \
>>> 		s + strlen(s);                       \
>>> 	})
>>>
>>> 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.
>>>
>>
>> Do you need your macro here to work with different types?  I'm not sure how
>> that could make sense, since "strlen" is not generic and always treats its
>> parameter as a char*.
>>
>> If you do need something that can work with different types and you want to
>> use functions to avoid possible issues with macro parameters, you can use
>> _Generic :
>>
>> #define strnulX(s) _Generic((s), \
>> 		char * : strnulX_char(s), \
>> 		char16_t * strnulX_char16(s), \
>> 		char32_t * strnulX_char32(s)
>> 	)(s)
>>
>> Then define functions strnulX_char, strnulX_char16, and strnulX_char32 as
>> separate functions.  (_Generic does not evaluate its argument, so
>> side-effects don't matter to it.)
>>
>>
> 



^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: Testing if an expression has side effects at compile time
  2026-01-09 12:07 Testing if an expression has side effects at compile time Alejandro Colomar
                   ` (3 preceding siblings ...)
  2026-01-09 15:39 ` Christopher Bazley
@ 2026-01-09 16:54 ` Tom Tromey
  4 siblings, 0 replies; 13+ messages in thread
From: Tom Tromey @ 2026-01-09 16:54 UTC (permalink / raw)
  To: Alejandro Colomar via Gcc; +Cc: Alejandro Colomar

>>>>> "Alejandro" == Alejandro Colomar via Gcc <gcc@gcc.gnu.org> writes:

Alejandro> I'm trying to find a way to verify that an expression (usually passed as
Alejandro> an argument to a macro) has no side effects.  Is this possible?

FWIW there's a bug open about this:

    https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57612

Tom

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2026-01-09 16:54 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-01-09 12:07 Testing if an expression has side effects at compile time Alejandro Colomar
2026-01-09 13:37 ` Florian Weimer
2026-01-09 13:47   ` Alejandro Colomar
2026-01-09 14:11     ` Richard Earnshaw (foss)
2026-01-09 14:20       ` Martin Uecker
2026-01-09 15:10 ` David Brown
2026-01-09 15:44   ` Alejandro Colomar
2026-01-09 16:39     ` David Brown
2026-01-09 15:35 ` Ben Boeckel
2026-01-09 15:45   ` Alejandro Colomar
2026-01-09 15:39 ` Christopher Bazley
2026-01-09 15:58   ` Alejandro Colomar
2026-01-09 16:54 ` Tom Tromey

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).