This is an archive of the discontinued LLVM Phabricator instance.

ConstantExpr cached APValues if present for constant evaluation
ClosedPublic

Authored by wchilders on Mar 19 2020, 10:50 AM.

Details

Summary

This patch allows the constant evaluator to use APValueResults from ConstantExprs.

There are some outstanding concerns I'll mark with inline comments.

Diff Detail

Event Timeline

wchilders created this revision.Mar 19 2020, 10:50 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 19 2020, 10:50 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript
wchilders added inline comments.Mar 19 2020, 11:04 AM
clang/lib/AST/Expr.cpp
2727

IgnoreParensSingleStep for some reason has been unwrapping ConstantExprs. This results in the constant evaluator, removing the ConstantExpr, and reevaluating the expression. There are no observed downsides to removing this condition, in the test suite, however, it's strange enough to note.

clang/lib/AST/ExprConstant.cpp
7354

This doesn't seem to be the right answer, and ConstantExprs don't have LValue APValues, at least, not that are reaching this case. We had a previous implementation that also, kind of punted on this issue with an override in TemporaryExprEvaluator: https://gitlab.com/lock3/clang/-/blob/9fbaeea06fc567ac472264bec2a72661a1e06c73/clang/lib/AST/ExprConstant.cpp#L9753

rsmith added inline comments.Mar 19 2020, 1:14 PM
clang/lib/AST/Expr.cpp
2727

We sadly don't really have any good, rigorous definitions of what the various Ignore* functions do. But the general idea of IgnoreParens is to ignore syntax with no semantic effect, and ConstantExpr doesn't describe syntax, so it probably shouldn't be stepped over here. (It is ignored by IgnoreImplicit, which is generally -- mostly -- trying to ignore semantics with no syntactic representation, which seems appropriate. It might make sense for IgnoreImpCasts to not step over FullExprs, but I expect that change would break some testcases.)

clang/lib/AST/ExprConstant.cpp
7354

The base class version seems appropriate to me, even for this case. We eventually want to use ConstantExpr to store the evaluated initializer value of a constexpr variable (replacing the existing special-case caching on VarDecl) and the like, not only for immediate invocations, and once we start doing that for reference variables we'll have glvalue ConstantExprs.

Is there some circumstance under which a glvalue ConstantExpr's APValue result is not of kind LValue?

wchilders marked an inline comment as done.Mar 19 2020, 3:18 PM
wchilders added inline comments.
clang/lib/AST/ExprConstant.cpp
7354

Is there some circumstance under which a glvalue ConstantExpr's APValue result is not of kind LValue?

Good question, the Sema/switch.c test fails if you assert(Result.isLValue()).

ConstantExpr 0x555562ab8760 'const int' lvalue Int: 3
`-DeclRefExpr 0x555562ab8740 'const int' lvalue Var 0x555562ab8230 'a' 'const int'

With an attributed line no. 359: https://github.com/llvm/llvm-project/blob/4b0029995853fe37d1dc95ef96f46697c743fcad/clang/test/Sema/switch.c#L359

The offending RValue is created in SemaExpr.cpp here: https://github.com/llvm/llvm-project/blob/f87563661d6661fd4843beb8f391acd077b08dbe/clang/lib/Sema/SemaExpr.cpp#L15190

The issue stems from evaluating this as an RValue to produce an integer, then caching that RValue in an lvalue constant expression. Do you have any suggestions? Perhaps an LValue to RValue conversion should be performed on the expression if it folds correctly, so that the ConstantExpr is actually an RValue?

rsmith added inline comments.Mar 19 2020, 4:56 PM
clang/lib/AST/ExprConstant.cpp
7354

I think we should be inserting the lvalue-to-rvalue conversion before we even try evaluating the expression. Does this go wrong along both the C++11 and pre-C++11 codepaths? (They do rather different conversions to the expression.) In any case, we're likely missing a call to DefaultLvalueConversion on whichever side is failing.

Judging by the fact that struct X { void f() { switch (0) case f: ; } }; misses the "non-static member function must be called" diagnostic only in C++98 mode, I'd imagine it's just the pre-C++11 codepath that's broken here.

Updated to assume LValue ConstantExprs have LValue APValues, with verification in debug mode. Additionally, added a missing LValue -> RValue conversion for VerifyIntegerConstantExpression.

wchilders marked 2 inline comments as done.Mar 20 2020, 12:40 PM
wchilders added inline comments.
clang/lib/AST/ExprConstant.cpp
7354

This is a C test, the C++ language level is actually irrelevant as best I can tell. I added the missing LValue -> RValue conversion so that the APValue is generated correctly. With that things function as you'd expect, I added an assertion to the LValue base evaluator to detect this case early in development, otherwise, the logic is now shared across evaluator classes.

rsmith accepted this revision.Mar 20 2020, 1:53 PM
rsmith added inline comments.
clang/lib/AST/ExprConstant.cpp
7350–7354

I think this override is now fully redundant and can be removed: the isLValue() assert is reached anyway when DerivedSuccess calls LValueExprEvaluatorBase::Success which calls LValue::setFrom.

This revision is now accepted and ready to land.Mar 20 2020, 1:53 PM
wchilders updated this revision to Diff 251763.Mar 20 2020, 2:19 PM
wchilders marked an inline comment as done.

Dropped the override for constexpr evaluator, LValue evaluation base class

wchilders marked 2 inline comments as done.Mar 20 2020, 2:20 PM
wchilders added inline comments.
clang/lib/AST/ExprConstant.cpp
7350–7354

Good catch, updated the patch to drop this. :)

I should note, I don't have commit access, so I'm unable to commit this myself. Attribution would be Wyatt Childers <wchilders@lock3software.com>

This revision was automatically updated to reflect the committed changes.