[PATCH] D87528: Enable '#pragma STDC FENV_ACCESS' in frontend cf. D69272 - Work in Progress

Richard Smith - zygoloid via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Mon Sep 14 01:46:49 PDT 2020


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



More information about the cfe-commits mailing list