Bug 86678 - constexpr evaluation incorrectly diagnoses unevaluated call to non-constexpr function
Summary: constexpr evaluation incorrectly diagnoses unevaluated call to non-constexpr ...
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: unknown
: P3 normal
Target Milestone: 9.0
Assignee: Jason Merrill
URL:
Keywords: rejects-valid
: 67026 (view as bug list)
Depends on:
Blocks: constexpr
  Show dependency treegraph
 
Reported: 2018-07-26 05:48 UTC by Eric Fiselier
Modified: 2021-08-28 21:25 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail:
Last reconfirmed: 2018-08-14 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Eric Fiselier 2018-07-26 05:48:54 UTC
Reproducer:

#include <cassert>

template <class T>
constexpr int foo() {
    if (sizeof(T))
        return 1;
    assert(false && "BOOM!");
}

constexpr int V = foo<int>();
Comment 1 Jonathan Wakely 2018-07-26 10:33:22 UTC
Reduced:

void fail();

template <class T>
constexpr int foo() {
    if (sizeof(T))
        return 1;
    if (1)
      fail();
}

constexpr int V = foo<int>();


a.cc:11:27: error: 'constexpr int foo() [with T = int]' called in a constant expression
 constexpr int V = foo<int>();
                   ~~~~~~~~^~
a.cc:4:15: note: 'constexpr int foo() [with T = int]' is not usable as a 'constexpr' function because:
 constexpr int foo() {
               ^~~
a.cc:8:11: error: call to non-'constexpr' function 'void fail()'
       fail();
       ~~~~^~
Comment 2 Eric Fiselier 2018-07-26 10:40:26 UTC
This is a bug according to [expr.const]p2 which states:

> An expression e is a core constant expression unless the evaluation of e, 
> following the rules of the abstract machine, would evaluate one of the 
> following expressions:
> [...]

The key phrase being "would evaluate one of". The example never evaluates a non-constant expression. GCC correctly accepts the control flow:

template <class T>
constexpr int foo() {
    if (sizeof(T))
        return 1;
    else
      assert(false && "BOOM!");
}

template <class T>
constexpr int bar() { return sizeof(T) ? 1 : throw 42; }

static_assert(foo() && bar());

In all both cases the unevaluated expressions do not cause constant evaluation to fail.


[1] http://eel.is/c++draft/expr.const#2
Comment 3 Eric Fiselier 2018-07-26 10:50:06 UTC
The `if (1)` isn't essential either.

void fail();

template <class T>
constexpr int foo() {
    if (sizeof(T))
        return 1;
    fail();
}

constexpr int x = foo<int>();

It seems to have something to do with whether the initial `if` statement is fully covered, regardless of what's actually evaluated. (Note that the branches of the initial `if` are evaluated, or not evaluated, correctly).

int fail();

template <class T>
constexpr int foo() {
    if (sizeof(T))
        return 1;
    else if (fail())
      fail(); // OK
   // Fallthrough is also OK
}

constexpr int x = foo<int>();
Comment 4 Jonathan Wakely 2018-08-14 16:09:23 UTC
constexpr bool always_true() { return true; }
int f() { return 1; }
constexpr int g() {
  if (always_true())
    return 0;
  return f();
}

ce.cc: In function 'constexpr int g()':
ce.cc:6:11: error: call to non-'constexpr' function 'int f()'
6 |   return f();
  |          ~^~

This makes it awkward to use the new __builtin_is_constant_evaluated().
Comment 5 Jason Merrill 2018-09-08 16:00:59 UTC
Author: jason
Date: Sat Sep  8 16:00:02 2018
New Revision: 264171

URL: https://gcc.gnu.org/viewcvs?rev=264171&root=gcc&view=rev
Log:
	PR c++/86678 - constexpr function with non-constant after return.

	In this testcase, the call to f() can never be a constant
	expression, but that's not a problem because it isn't always
	reached by calls to g.  We were wrongly rejecting this because
	potential_constant_expression_1 lacked the jump tracking that
	cxx_eval_constant_expression has.  So this patch adds a simpler
	version of that tracking.

	* constexpr.c (potential_constant_expression_1): Add jump_target.
	(breaks): Check for BREAK_STMT.
	(continues): Check for CONTINUE_STMT.

Added:
    trunk/gcc/testsuite/g++.dg/cpp1y/constexpr-return4.C
Modified:
    trunk/gcc/cp/ChangeLog
    trunk/gcc/cp/constexpr.c
Comment 6 Jason Merrill 2018-09-08 16:26:01 UTC
Fixed for GCC 9.
Comment 7 Ville Voutilainen 2018-11-14 09:11:17 UTC
*** Bug 67026 has been marked as a duplicate of this bug. ***
Comment 8 Jonathan Wakely 2019-05-13 09:53:44 UTC Comment hidden (obsolete)