This is an archive of the discontinued LLVM Phabricator instance.

Apply ConstantEvaluated evaluation contexts to more manifestly constant evaluated scopes
Needs ReviewPublic

Authored by wchilders on Mar 19 2020, 12:43 PM.

Details

Reviewers
Tyker
rsmith
Summary

This patch attempts to update application of evaluation contexts to better represent, what is manifestly constant evaluated.

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

Diff Detail

Event Timeline

wchilders created this revision.Mar 19 2020, 12:43 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 19 2020, 12:43 PM
Herald added a subscriber: cfe-commits. · View Herald Transcript
wchilders marked an inline comment as done.Mar 19 2020, 12:53 PM
wchilders added inline comments.
clang/lib/Sema/SemaExpr.cpp
15597 ↗(On Diff #251450)

This is my main concern with this patch. There's something subtle here, and it's unclear from an outsider's (my) perspective what the root issue is here.

Effectively we get into trouble when we first enter ActOnCXXEnterDeclInitializer on the isManifestlyEvaluatedVar branch. We then exit at ActOnCXXExitDeclInitializer, triggering this branch, as Rec.isConstantEvaluated() is true. This results in the cleanups being dropped. Then when we enter the constexpr evaluator for VarDecl::evaluateValue. the evaluator is unhappy as we're missing a cleanup for the created temporary.

Any input that allows this to be resolved without the special case, or that otherwise informs how this should work, and why unevaluated and constant evaluated are treated equally here, would be awesome. I'll note that in reviewing the revision history, this condition dates back to when ConstantEvaluated was created, so perhaps this is a "historical artifact" of that time?

rsmith added inline comments.Mar 19 2020, 2:02 PM
clang/lib/Sema/SemaDeclCXX.cpp
16793–16797 ↗(On Diff #251450)

We can't implement the checks for manifestly constant-evaluated initializers this way in general. Per [expr.const]/14, we need to treat the initializer as manifestly constant-evaluated if it's "the initializer of a variable that is usable in constant expressions or has constant initialization". We can't test either of those conditions in general until we know what the initializer is, because they can both depend on whether evaluation of the initializer succeeds. (This approach works for the case of constexpr variables, which are always usable in constant expressions, but not any of the other cases, and the approach we'll need for the other cases will also handle constexpr variables. There is a very modest benefit to special-casing constexpr variable initializers regardless -- we can avoid forming and then pruning out nested ConstantExpr nodes for immediate invocations inside the initializer -- but I think it's probably not worth the added complexity.)

To address the general problem, we should handle this in CheckCompleteVariableDeclaration, which is where we evaluate the initializer and generally determine whether the variable has constant initialization and / or is usable in constant expressions. Probably the cleanest approach -- and certainly the one I'd been intending to pursue -- would be to wrap the initializer with a ConstantExpr there in the relevant cases, and allow the usual handling of nested immediate invocations to prune out any ConstantExprs nested within the initializer representing inner calls to consteval functions. (I think I've mentioned elsewhere that I would like to remove the "evaluated value" storage on VarDecl in favor of using ConstantExpr for this purpose.)

wchilders added inline comments.Mar 19 2020, 4:49 PM
clang/lib/Sema/SemaDeclCXX.cpp
16793–16797 ↗(On Diff #251450)

There is a very modest benefit to special-casing constexpr variable initializers regardless -- we can avoid forming and then pruning out nested ConstantExpr nodes for immediate invocations inside the initializer -- but I think it's probably not worth the added complexity.

So, this patch is motivated for us by the desire to check if a "meta type" variable, belongs to a compile time, or runtime. Additionally, this is used being used to verify our compile time, "injection statement", is not appearing in a runtime context. This prevents reflections/metaprogramming values and logic from leaking nonsensically into runtime code.

I think this can be accomplished as you said in the CheckCompleteVariableDeclaration. We're already checking for out of place "meta type" variables there, though we catch fragments, and injection statements more eagerly. In retrospect, I don't think there is any issue removing the eager check from the fragments, and I don't think the checking of the injection statements should be affected by this case.

I'll try and look more into this in the morning.

Updated the patch to correct formatting issues, and removed application of the ConstantEvaluated evaluation context to var decls as it introduces too much complexity.

Additionally removed a fix for decltype with immediate functions that snuck into this patch. This will be submitted separately.