rsmith added a comment.

In D87528#2270561 <https://reviews.llvm.org/D87528#2270561>, @rjmccall wrote:

> Richard, what do you think the right rule is for when to use the special 
> constant-evaluation rules for this feature in C++?  The C standard says that 
> constant expressions should use the default rules rather than considering the 
> dynamic environment, but C can get away with that because constant 
> expressions are restricted to a narrow set of syntactic situations.

Right, C has a lot of leeway here because floating-point operations aren't 
allowed in integer constant expressions in C; the only place FP operations are 
guaranteed to be evaluated during translation is in the initializers of 
variables of static storage duration, which are all notionally initialized 
before the program begins executing (and thus before a non-default FP 
environment can be entered) anyway. Needing to deal with (for example) 
floating-point operations in array bounds is a novel C++ problem.

I think the broadest set of places we could consider assuming the default FP 
environment is manifestly constant evaluated 
<http://eel.is/c++draft/expr.const#14> contexts; that's probably the most 
natural category in C++ for this kind of thing. That is the same condition 
under which `std::is_constant_evaluated()` returns `true`. (The "manifestly 
constant evaluated" property is already tracked within the constant evaluator 
by the `InConstantContext` flag.) And your list:

> - initializers for objects of static storage duration if they would be 
> constant initializers if they weren't covered by the pragma; this should 
> roughly align C and C++ semantics for things like `static const` within a 
> function
> - any `constexpr` / `constinit` context, and it should be ill-formed to use 
> the pragma in such a context; that is, it's ignored from outside and 
> prohibited (directly) inside

... roughly matches "manifestly constant evaluated". (Aside: I don't think we 
need to, or should, prohibit the pragma inside `constexpr` functions. We'll try 
to reject such functions anyway if they can't produce constant expressions, so 
a special case rule seems redundant, and such functions can legitimately 
contain code only intended for use in non-constant contexts. Disallowing it in 
`consteval` functions might be OK, but we don't disallow calls to 
non-`constexpr` functions from `consteval` functions, for which the same 
concerns would apply.)

"Manifestly constant evaluated" describes the set of expressions that an 
implementation is required to reduce to a constant, and covers two sets of 
cases:

- expressions for which the program is ill-formed if they're not constant: 
template arguments, array bounds, enumerators, case labels, `consteval` 
function calls, `constexpr` and `constinit` variables, concepts, constexpr if
- expressions whose semantics are potentially affected by constantness, and for 
which an implementation is required to guarantee to commit to the constant 
semantics whenever it can: when checking to see if a variable with 
static/thread storage duration has static initialization, or whether an 
automatic variable of reference or const-qualified integral type is usable in 
constant expressions

If we go that way, there'd be at least one class of surprising cases. Here's an 
example of it:

  void f() {
  #pragma STDC FENV_ACCESS ON
    fesetround(FE_DOWNWARD);
    CONST bool b = (1.0 / 3.0) * 3.0 < 1.0;
    if (b) g();
    fesetround(FE_TONEAREST);
  }

Under the "manifestly constant evaluated" rule, with `-DCONST=`, this would 
call `g()`, but with `-DCONST=const`, it would *not* call `g()`, because the 
initializer of `b` would be manifestly constant evaluated. That's both 
surprising and different from the behavior in C. (The surprise isn't novel; 
C++'s `std::is_constant_evaluated()` has the same surprising semantics. It's 
not completely unreasonable to claim that C++ says that reference and `const` 
integral variables are implicitly `constexpr` whenever they can be, though 
that's not how the rule is expressed.) Basing this off "manifestly constant 
evaluated" would also be C-incompatible in at least one other way: we'd treat 
FP operations in an array bound as being in the default FP environment in C++ 
if that makes the overall evaluation constant, but in C they always result in a 
VLA.

So I suppose the question is, are we comfortable with all that, or do we want 
to use a different rule?

There's another uninventive rule at the opposite end of the spectrum. Prior 
discussion in the C++ committee, before we had "manifestly constant evaluated" 
and associated machinery, seemed to be converging on this model:

1. constrained FP operations are always treated as non-constant in all 
contexts, and
2. in a C++ translation unit, the initial state for `FENV_ACCESS` is always 
`OFF`.

That approach has the advantage of being both simple and compatible with all 
code we currently support, as well as probably compatible with all directions 
the C++ committee might reasonably go in, and it gives the same result as C for 
all cases it accepts. It's also the most predictable option: all floating-point 
operations within `FENV_ACCESS ON` regions always support non-default FP 
environments. It would be much less friendly towards people trying to mix 
constant expressions and non-default floating-point environments, though; how 
important is that?

Providing a middle ground between these two options would likely have somewhat 
higher implementation complexity, but I think we could do that too if there's 
some middle-ground we're happier with (eg, manifestly constant evaluated minus 
initializers of automatic storage duration variables). My inclination would be 
to go for the more-restrictive rule, at least initially -- treat all 
constrained FP operations as non-constant in all contexts in C++ -- and 
consider switching to a different rule if we get user feedback that the 
restrictions are too unpleasant.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D87528/new/

https://reviews.llvm.org/D87528

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to