[PATCH] D52939: ExprConstant: Propagate correct return values from constant evaluation.

Richard Smith - zygoloid via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 19 15:46:45 PDT 2018


rsmith added inline comments.


================
Comment at: clang/lib/AST/ExprConstant.cpp:711
+
+      /// Evaluate as a constant expression, as per C++11-and-later constexpr
+      /// rules. Stop if we find that the expression is not a constant
----------------
jyknight wrote:
> rsmith wrote:
> > This is not the intent. The evaluator uses the rules of the current language mode. However, it doesn't enforce the C and C++98 syntactic restrictions on what can appear in a constant expression, because it turns out to be cleaner to check those separately rather than to interleave them with evaluation.
> > 
> > We should probably document this as evaluating following the evaluation (but not syntactic) constant expression rules of the current language mode.
> I'm not really sure what you mean to distinguish between C/C++98 "evaluation constant expression rules" and the definition of a C/C++98 ICE?
> 
> Perhaps you mean that if evaluation succeeds, it will have produced a value consistent with the rules of the current language, but that it may not produce the diagnostics in all cases it ought to?
> 
> AFAICT, the evaluator is designed to emit diagnostics only for the C++11 (and later) constant expression rules, as well as for inability to evaluate. The diagnostic notes are usually dropped on the floor in C++98 and C modes, as only "fold" is typically used there -- along with the separate CheckICE to produce warnings.
> 
> I believe the only place the diagnostics were being actually emitted in pre-c++11 modes is for the diagnose_if and enable_if attributes' "potential constant expression" check. And there it really seems to me to be using the c++11 evaluation semantics.
> 
> The existence of isCXX11ConstantExpr suggests an intent for the function to provide the C++11 rules, as well, no?
> 
> 
> Perhaps you mean that if evaluation succeeds, it will have produced a value consistent with the rules of the current language, but that it may not produce the diagnostics in all cases it ought to?

The concern I have with this comment is that it describes a happenstance of implementation rather than the intent. The intent is certainly *not* that we will use the C++11 rules when calling this in C++98 mode, but it just so happens that there are no interesting differences there right now.

Looking at this another way, the fact that we only guarantee to produce "not a constant expression because" notes in C++11 onwards is unrelated to the evaluation mode. Rather, the reason for that is instead because all the validity / ICE checking in other language modes is based on the syntactic form of the expression (and is checked separately as a result).

As a concrete suggestion, how about something like:

"Evaluate as a constant expression. Stop if we find that the expression is not a constant expression. Note that this doesn't enforce the syntactic ICE requirements of C and C++98."


================
Comment at: clang/lib/AST/ExprConstant.cpp:952
+    /// with diagnostics.
+    bool keepEvaluatingAfterNote() {
       switch (EvalMode) {
----------------
jyknight wrote:
> rsmith wrote:
> > "Note" is beside the point here. We should be talking about non-constant expressions; that's what matters.
> Renamed to keepEvaluatingAfterNonConstexpr, updated comment.
Capitalize as "...NonConstExpr" instead, since this is just an abbreviation of the term "constant expression", not C++11's notion of "constexpr". (I'd also lengthen this to "...NonConstantExpr" or "...NonConstantExpression" to further clarify.)


================
Comment at: clang/lib/AST/ExprConstant.cpp:1440-1444
+      if (checkSubobject(Info, E, isa<FieldDecl>(D) ? CSK_Field : CSK_Base)) {
         Designator.addDeclUnchecked(D, Virtual);
+        return true;
+      }
+      return Info.keepEvaluatingAfterNote();
----------------
jyknight wrote:
> rsmith wrote:
> > `checkSubobject` should return `false` if it produced a diagnostic that should cause evaluation to halt; this call to `keepEvaluatingAfterNote` should not be necessary. (And likewise below.)
> Are you sure?
> 
> Today, checkSubobject returns false if it sees something "invalid" -- and the callers seem to depend on that -- but invalid subobject designators are still allowed during evaluation in fold mode.
> 
> E.g., this is valid in C:
> `struct Foo { int x[]; }; struct Foo f; int *g = f.x;`
> 
> But, in C++, `constexpr int *g = f.x;` is an error, diagnosing note_constexpr_unsupported_unsized_array.
> 
> All the LValue code, with its mutable "Result" state, and carrying of a couple different "Invalid" bits off on the side, is really rather confusing!
What I mean is that you should change the code to make what I said be true, if necessary. It's the callee's responsibility to say whether we hit something we can't evaluate past. The caller should not be guessing that the only reason that `checkSubobject` can fail is due to a `CCEDiag` -- if `checkSubobject` fails with an `FFDiag`, this will sometimes return `true`, which would be wrong.

Here's how I'd look at it: a `false` value is a token that indicates that someone has produced a diagnostic whose severity is such that we don't need to keep evaluating. Once that happens, we don't need to re-check whether to keep evaluating, because we've already been told that we shouldn't, and we just need to propagate that fact to our caller.


================
Comment at: clang/lib/AST/ExprConstant.cpp:710
+      // EM_PotentialConstantExpressionUnevaluated as a duplicate).  This will
+      // be somewhat tricky to change, because LLVM currently verifies that an
+      // expression is a constant expression (possibly strictly with
----------------
LLVM -> Clang (or "we" or similar)


================
Comment at: clang/lib/AST/ExprConstant.cpp:723
+      /// Fold the expression to a constant, using any techniques we can. Stop
+      /// if we hit a side-effect that we can't model, or undefined behavior.
       EM_ConstantFold,
----------------
Do we now guarantee to stop on UB in this mode? We didn't appear to do so previously. (Particularly, we'd evaluate past signed overflow and take the 2's complement value.)


================
Comment at: clang/lib/AST/ExprConstant.cpp:956
+    /// expression which is not a constant expression? If this returns true,
+    /// then the evaluation will be successful if and only no diagnostics are
+    /// emitted. If it returns false, evaluation can succeed with diagnostics.
----------------
only no -> only if no


================
Comment at: clang/lib/AST/ExprConstant.cpp:1198-1200
+  if (!Info.keepEvaluatingAfterNonConstexpr())
+    return false;
+  return true;
----------------
Simplify to `return Info.keepEvaluating...();`


================
Comment at: clang/lib/AST/ExprConstant.cpp:2527-2529
+  if (!LVal.adjustOffsetAndIndex(Info, E, Adjustment, SizeOfPointee))
+    return false;
   return true;
----------------
Simplify to `return LVal.adjust[...]`


================
Comment at: clang/lib/AST/ExprConstant.cpp:7438
+      return VisitIgnoredBaseExpression(E->getBase());
     }
 
----------------
Nit: drop the braces here.


================
Comment at: clang/lib/AST/ExprConstant.cpp:11490-11492
+  // _is_ a constant expression.  (Note: the above applies in
+  // EM_ConstantExpression mode, which we use here, but not in,
+  // e.g. EM_ConstantFold or others.)
----------------
I don't believe the parenthetical is true: in C++11 or later, we should always get a note if and only if the expression is not a constant expression, regardless of evaluation mode. Instead, I think the thing you're highlighting here is that the return value from `EvaluateAsRValue` does not indicate whether the expression is a constant expression unless the evaluation mode is `EM_ConstantExpression`.


================
Comment at: clang/lib/AST/ExprConstant.cpp:11495-11496
+
   if (!Diags.empty()) {
-    IsConstExpr = false;
     if (Loc) *Loc = Diags[0].first;
   }
----------------
Combine these two `if`s.


https://reviews.llvm.org/D52939





More information about the cfe-commits mailing list