[PATCH] D63960: [C++20] Add consteval-specific semantic for functions
Richard Smith - zygoloid via Phabricator via cfe-commits
cfe-commits at lists.llvm.org
Mon Sep 30 15:52:13 PDT 2019
rsmith added a comment.
Very cool, this is an elegant approach.
================
Comment at: clang/include/clang/Basic/DiagnosticASTKinds.td:57
+def note_consteval_address_accessible : Note<
+ "%select{pointer|reference}0 on a consteval declaration "
+ "is not a constant expression">;
----------------
on -> to
================
Comment at: clang/include/clang/Basic/DiagnosticSemaKinds.td:2366
+def err_invalid_consteval_decl_kind : Error<
+ "operator %select{new|delete|new[]|delete[]}0 cannot be declared consteval">;
def err_invalid_constexpr : Error<
----------------
Please add 's around the function name. Instead of encoding the name as an integer, you can stream the declaration itself into the diagnostic and just use `"%0 cannot be declared consteval"` here.
================
Comment at: clang/include/clang/Sema/Sema.h:806-808
+ /// Should be set for most operation.
+ /// It is unset to disable tracking of immediate invocation candidates and
+ /// reference on consteval.
----------------
The comment should first describe what this field means, before saying how to use it (if necessary). So:
```
/// Whether immediate invocation candidates and references to consteval functions should be tracked.
```
or something like that.
That said, consider renaming this to `RebuildingImmediateInvocation` to avoid suggesting it's a general mechanism to turn off immediate invocation tracking.
================
Comment at: clang/include/clang/Sema/Sema.h:1072
+ llvm::SmallVector<llvm::PointerIntPair<ConstantExpr *, 1>, 8>
+ ImmediateInvocationsCandidates;
+
----------------
`ImmediateInvocationsCandidates` -> `ImmediateInvocationCandidates`
================
Comment at: clang/include/clang/Sema/Sema.h:1076
+ /// context not already know to be immediately invoked.
+ llvm::SmallPtrSet<DeclRefExpr *, 8> ReferenceOnConsteval;
+
----------------
`ReferenceOnConsteval` -> `ReferencesToConsteval` or similar.
================
Comment at: clang/lib/AST/ExprConstant.cpp:1932
+ if (FD->isConsteval()) {
+ Info.FFDiag(Loc, diag::note_consteval_address_accessible)
+ << !Type->isAnyPointerType();
----------------
It would be useful for the diagnostic to say what consteval function we have a pointer to, and maybe include a note pointing at its declaration.
================
Comment at: clang/lib/Sema/SemaDeclCXX.cpp:11188-11189
+ ConstexprSpecKind ConstexprKind = DetermineSpecialMemberConstexprKind(
+ Constexpr, ClassDecl->hasDefaultedConstevalDefaultCtor());
+
----------------
I don't think this is necessary: implicit special members are never `consteval`. (We use `DeclareImplicitDefaultConstructor` when there is no user-declared default constructor, in which case there can't possibly be a defaulted consteval default constructor.) I don't think you need any of the `DetermineSpecialMemberConstexprKind` machinery, nor the tracking of `DefaultedSpecialMemberIsConsteval` in `CXXRecordDecl`.
================
Comment at: clang/lib/Sema/SemaExpr.cpp:15032
+ return E;
+ if (auto *Call = dyn_cast<CallExpr>(E.get()))
+ if (auto *DeclRef =
----------------
rsmith wrote:
> Please add a comment to this to point out that it's just an optimization, for example:
>
> "Opportunistically remove the callee from ReferencesToConsteval if we can. It's OK if this fails; we'll also remove this in HandleImmediateInvocations, but catching it here allows us to avoid walking the AST looking for it in simple cases."
Consider using `E.get()->IgnoreImplicit()` here; we want to step over (at least) any `CXXBindTemporaryExpr`s inserted by `MaybeBindToTemporary`.
================
Comment at: clang/lib/Sema/SemaExpr.cpp:15032-15035
+ if (auto *Call = dyn_cast<CallExpr>(E.get()))
+ if (auto *DeclRef =
+ dyn_cast<DeclRefExpr>(Call->getCallee()->IgnoreImplicit()))
+ ExprEvalContexts.back().ReferenceOnConsteval.erase(DeclRef);
----------------
Please add a comment to this to point out that it's just an optimization, for example:
"Opportunistically remove the callee from ReferencesToConsteval if we can. It's OK if this fails; we'll also remove this in HandleImmediateInvocations, but catching it here allows us to avoid walking the AST looking for it in simple cases."
================
Comment at: clang/lib/Sema/SemaExpr.cpp:15036-15039
+ ConstantExpr *Res = ConstantExpr::Create(
+ getASTContext(), E.get(),
+ ConstantExpr::getStorageKind(E.get()->getType().getTypePtr(),
+ getASTContext()));
----------------
We will need an `ExprWithCleanups` wrapping `E.get()` if there might be any temporaries within it.
We don't actually know whether there definitely are any such temporaries, but we can conservatively assume we might need an `ExprWithCleanups` if `Cleanup.exprNeedsCleanups()` returns `true`.
You can test this with something like:
```
struct A { int *p = new int(42); consteval int get() { return *p; } constexpr ~A() { delete p; } };
int k = A().get();
```
... which should be valid, but will be rejected (or might assert) if we don't destroy the `A()` temporary as part of evaluating the `consteval` call to `get`.
================
Comment at: clang/lib/Sema/SemaExpr.cpp:15071
+ llvm::PointerIntPair<ConstantExpr *, 1> RHS) {
+ return LHS.getPointer()->getBeginLoc() < RHS.getPointer()->getBeginLoc();
+ }
----------------
`<` on `SourceLocation` doesn't really mean anything. It's not correct to use it here. Also, using source locations will not give you outer expressions before inner ones. Consider:
```
enum E {};
consteval E f();
consteval int operator+(E, int);
void g() {
f() + 1;
}
```
Here, the begin location of both the call to `f()` and the call to `operator+` are in the same place: at the `f` token.
I don't think you need this sort at all: instead, you can process immediate invocations in `ImmediateInvocationCandidates` in reverse order: subexpressions must have been built before their enclosing expressions, so walking in reverse order will guarantee that you process an outer candidate before any inner ones.
================
Comment at: clang/lib/Sema/SemaExpr.cpp:15102
+ ExprCompType{});
+ if (It != IISet.end()) {
+ It->setInt(1); // Mark as deleted
----------------
We should look for an exact match here, not merely for something nearby. (Maybe: form a pointer set of immediate invocation pointer expressions, and remove things from the set when you encounter them in this walk; then when iterating over the immediate invocations, skip ones that have already been erased from the set.)
================
Comment at: clang/lib/Sema/SemaExpr.cpp:15118-15119
+ }
+ /// FIXME: Add an option to tree transfrom that prevents rebuilding
+ /// nodes that only need rewiring and use this option here.
+ bool AlwaysRebuild() { return false; }
----------------
I don't imagine this will ever actually happen, because immutability of the AST is so important to other parts of Clang. This should be a rare case, so it likely doesn't matter; I'd just remove this FIXME.
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D63960/new/
https://reviews.llvm.org/D63960
More information about the cfe-commits
mailing list