[1/5] c++: gcc_assert(false) replaced with gcc_unreachable()

Message ID 20260407110826.8427-1-yangkun@disroot.org
State Committed
Commit 6a3ff2a7f5afe62f3800a90aa496d68636f83127
Headers
Series [1/5] c++: gcc_assert(false) replaced with gcc_unreachable() |

Commit Message

yangkun April 7, 2026, 11:08 a.m. UTC
  gcc/cp/ChangeLog:
	* pt.cc (tsubst_expr): gcc_unreachable, not gcc_assert.
	* parser.cc (cp_parser_template_id): Likewise.
	* reflect.cc (eval_template_of): Likewise.
---
 gcc/cp/parser.cc  | 2 +-
 gcc/cp/pt.cc      | 2 +-
 gcc/cp/reflect.cc | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)
  

Comments

Patrick Palka April 8, 2026, 2:19 p.m. UTC | #1
On Tue, 7 Apr 2026, Yang Kun wrote:

> gcc/cp/ChangeLog:
> 	* pt.cc (tsubst_expr): gcc_unreachable, not gcc_assert.
> 	* parser.cc (cp_parser_template_id): Likewise.
> 	* reflect.cc (eval_template_of): Likewise.

Is this really an improvement?  gcc_unreachable is UB in release builds,
whereas gcc_assert(false) reliably causes an ICE.  It seems to me that
gcc_assert(false) is therefore safer since a gcc_unreachable code path
could risk silently accepting an invalid testcase, or producing wrong
code, instead of just ICEing.  And gcc_assert(false) is already marked
cold and noreturn so there's not much codegen difference vs using
gcc_unreachable.  gcc_unreachable seems best suited for code paths
that are "obviously" unreachable (even in the presence of unknown bugs
in callers), but not obvious to the compiler.

> ---
>  gcc/cp/parser.cc  | 2 +-
>  gcc/cp/pt.cc      | 2 +-
>  gcc/cp/reflect.cc | 2 +-
>  3 files changed, 3 insertions(+), 3 deletions(-)
> 
> diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
> index 3a8d56dc1..7b800af78 100644
> --- a/gcc/cp/parser.cc
> +++ b/gcc/cp/parser.cc
> @@ -21329,7 +21329,7 @@ cp_parser_template_id (cp_parser *parser,
>  	  return error_mark_node;
>  	}
>        else
> -	gcc_assert (false);
> +	gcc_unreachable ();
>  
>        template_id = lookup_template_function (templ, arguments);
>        if (TREE_CODE (template_id) == TEMPLATE_ID_EXPR)
> diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> index 673da9a19..185f75f5e 100644
> --- a/gcc/cp/pt.cc
> +++ b/gcc/cp/pt.cc
> @@ -22348,7 +22348,7 @@ tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
>  						 complain);
>  	else if (concept_check_p (function))
>  	  /* Calls to concepts should have been previously diagnosed.  */
> -	  gcc_assert (false);
> +	  gcc_unreachable ();
>  	else
>  	  ret = finish_call_expr (function, &call_args,
>  				  /*disallow_virtual=*/qualified_p,
> diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
> index 6e3b8d2e9..71573d27f 100644
> --- a/gcc/cp/reflect.cc
> +++ b/gcc/cp/reflect.cc
> @@ -2908,7 +2908,7 @@ eval_template_of (location_t loc, const constexpr_ctx *ctx, tree r,
>    else if (VAR_OR_FUNCTION_DECL_P (r) && DECL_TEMPLATE_INFO (r))
>      r = DECL_TI_TEMPLATE (r);
>    else
> -    gcc_assert (false);
> +    gcc_unreachable ();
>  
>    gcc_assert (TREE_CODE (r) == TEMPLATE_DECL);
>    return get_reflection_raw (loc, r);
> -- 
> 2.53.0
> 
>
  
Jakub Jelinek April 8, 2026, 2:48 p.m. UTC | #2
On Wed, Apr 08, 2026 at 10:19:54AM -0400, Patrick Palka wrote:
> On Tue, 7 Apr 2026, Yang Kun wrote:
> 
> > gcc/cp/ChangeLog:
> > 	* pt.cc (tsubst_expr): gcc_unreachable, not gcc_assert.
> > 	* parser.cc (cp_parser_template_id): Likewise.
> > 	* reflect.cc (eval_template_of): Likewise.
> 
> Is this really an improvement?  gcc_unreachable is UB in release builds,
> whereas gcc_assert(false) reliably causes an ICE.  It seems to me that
> gcc_assert(false) is therefore safer since a gcc_unreachable code path
> could risk silently accepting an invalid testcase, or producing wrong
> code, instead of just ICEing.  And gcc_assert(false) is already marked
> cold and noreturn so there's not much codegen difference vs using
> gcc_unreachable.  gcc_unreachable seems best suited for code paths
> that are "obviously" unreachable (even in the presence of unknown bugs
> in callers), but not obvious to the compiler.

system.h documents that gcc_assert (0) shouldn't be used and
gcc_unreachable () should be used instead.

	Jakub
  
Jakub Jelinek April 8, 2026, 2:57 p.m. UTC | #3
On Wed, Apr 08, 2026 at 10:19:54AM -0400, Patrick Palka wrote:
> On Tue, 7 Apr 2026, Yang Kun wrote:
> 
> > gcc/cp/ChangeLog:
> > 	* pt.cc (tsubst_expr): gcc_unreachable, not gcc_assert.
> > 	* parser.cc (cp_parser_template_id): Likewise.
> > 	* reflect.cc (eval_template_of): Likewise.
> 
> Is this really an improvement?  gcc_unreachable is UB in release builds,
> whereas gcc_assert(false) reliably causes an ICE.  It seems to me that

No, gcc_assert(false) is also UB in release builds.
The only difference is for !ENABLE_ASSERT_CHECKING when built by
gcc older than 4.5 (so never for GCC, as we only support 5.4+, only for
non-GCC), where gcc_assert (false) does nothing and gcc_unreachable ()
does ICE, otherwise it is the same except more tokens for gcc_assert
(false).
1) ENABLE_ASSERT_CHECKING
  ((void)(!(false) ? fancy_abort (__FILE__, __LINE__, __FUNCTION__), 0 : 0))
   vs.
  (fancy_abort (__FILE__, __LINE__, __FUNCTION__))
2) !ENABLE_ASSERT_CHECKING && GCC_VERSION >= 4005
  ((void)(UNLIKELY (!(false)) ? __builtin_unreachable (), 0 : 0))
   vs.
  __builtin_unreachable ()
3) !ENABLE_ASSERT_CHECKING && GCC_VERSION < 4005
  ((void)(0 && (false)))
   vs.
  (fancy_abort (__FILE__, __LINE__, __FUNCTION__))
fancy_abort has __attribute__((noreturn, cold)), so it is cold, and
__builtin_unreachable has that implicitly too.

	Jakub
  
Patrick Palka April 8, 2026, 3:04 p.m. UTC | #4
On Wed, 8 Apr 2026, Jakub Jelinek wrote:

> On Wed, Apr 08, 2026 at 10:19:54AM -0400, Patrick Palka wrote:
> > On Tue, 7 Apr 2026, Yang Kun wrote:
> > 
> > > gcc/cp/ChangeLog:
> > > 	* pt.cc (tsubst_expr): gcc_unreachable, not gcc_assert.
> > > 	* parser.cc (cp_parser_template_id): Likewise.
> > > 	* reflect.cc (eval_template_of): Likewise.
> > 
> > Is this really an improvement?  gcc_unreachable is UB in release builds,
> > whereas gcc_assert(false) reliably causes an ICE.  It seems to me that
> 
> No, gcc_assert(false) is also UB in release builds.
> The only difference is for !ENABLE_ASSERT_CHECKING when built by
> gcc older than 4.5 (so never for GCC, as we only support 5.4+, only for
> non-GCC), where gcc_assert (false) does nothing and gcc_unreachable ()
> does ICE, otherwise it is the same except more tokens for gcc_assert
> (false).
> 1) ENABLE_ASSERT_CHECKING
>   ((void)(!(false) ? fancy_abort (__FILE__, __LINE__, __FUNCTION__), 0 : 0))
>    vs.
>   (fancy_abort (__FILE__, __LINE__, __FUNCTION__))
> 2) !ENABLE_ASSERT_CHECKING && GCC_VERSION >= 4005
>   ((void)(UNLIKELY (!(false)) ? __builtin_unreachable (), 0 : 0))
>    vs.
>   __builtin_unreachable ()
> 3) !ENABLE_ASSERT_CHECKING && GCC_VERSION < 4005
>   ((void)(0 && (false)))
>    vs.
>   (fancy_abort (__FILE__, __LINE__, __FUNCTION__))
> fancy_abort has __attribute__((noreturn, cold)), so it is cold, and
> __builtin_unreachable has that implicitly too.

D'oh, sorry for the noise, I missed the !ENABLE_ASSERT_CHECKING check
in gcc_unreachable.  Now I see they're indeed equivant with
--enable-checking=release.

> 
> 	Jakub
> 
>
  

Patch

diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc
index 3a8d56dc1..7b800af78 100644
--- a/gcc/cp/parser.cc
+++ b/gcc/cp/parser.cc
@@ -21329,7 +21329,7 @@  cp_parser_template_id (cp_parser *parser,
 	  return error_mark_node;
 	}
       else
-	gcc_assert (false);
+	gcc_unreachable ();
 
       template_id = lookup_template_function (templ, arguments);
       if (TREE_CODE (template_id) == TEMPLATE_ID_EXPR)
diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
index 673da9a19..185f75f5e 100644
--- a/gcc/cp/pt.cc
+++ b/gcc/cp/pt.cc
@@ -22348,7 +22348,7 @@  tsubst_expr (tree t, tree args, tsubst_flags_t complain, tree in_decl)
 						 complain);
 	else if (concept_check_p (function))
 	  /* Calls to concepts should have been previously diagnosed.  */
-	  gcc_assert (false);
+	  gcc_unreachable ();
 	else
 	  ret = finish_call_expr (function, &call_args,
 				  /*disallow_virtual=*/qualified_p,
diff --git a/gcc/cp/reflect.cc b/gcc/cp/reflect.cc
index 6e3b8d2e9..71573d27f 100644
--- a/gcc/cp/reflect.cc
+++ b/gcc/cp/reflect.cc
@@ -2908,7 +2908,7 @@  eval_template_of (location_t loc, const constexpr_ctx *ctx, tree r,
   else if (VAR_OR_FUNCTION_DECL_P (r) && DECL_TEMPLATE_INFO (r))
     r = DECL_TI_TEMPLATE (r);
   else
-    gcc_assert (false);
+    gcc_unreachable ();
 
   gcc_assert (TREE_CODE (r) == TEMPLATE_DECL);
   return get_reflection_raw (loc, r);