Skip to content

Constant expression evaluation misses copy elision #60286

Closed
@AaronBallman

Description

@AaronBallman

The following code fails to compile on Clang:

struct A {
    int i = 0;

    consteval A() {}
    A(const A&) { i = 1; }
    consteval int f() { return i; }
};

int main() {
    return A{A{}}.f();
}

because we are consider the copy constructor call to be invalid due to not being consteval.

https://gcc.godbolt.org/z/P8xYjnYjM

Activity

llvmbot

llvmbot commented on Jan 25, 2023

@llvmbot
Member

@llvm/issue-subscribers-c-20

llvmbot

llvmbot commented on Jan 25, 2023

@llvmbot
Member

@llvm/issue-subscribers-clang-frontend

Fedr

Fedr commented on Jan 25, 2023

@Fedr

I think in your example A{A{}}.f() shall return 0 (copy is elided). Online demo: https://gcc.godbolt.org/z/rxdWsv6s8

But this is different from the original report, where the issue happens without static_assert: #53244

AaronBallman

AaronBallman commented on Jan 25, 2023

@AaronBallman
CollaboratorAuthor

Ugh, yeah, I mucked up the example code in the summary. The crux of the example at https://gcc.godbolt.org/z/sn68z5Gaf is this bit:

<source>:19:14: note: non-constexpr constructor 'A' cannot be used in a constant expression
    return A{A{}}.f();
             ^
<source>:11:5: note: declared here
    A(const A&) { i = 1; }
    ^

we should not be noting that constructor because it should have been elided, therefore:

<source>:19:12: error: call to consteval function 'A::f' is not a constant expression
    return A{A{}}.f();
           ^

should have not been diagnosed. I think our constant expression evaluator is getting confused.

zygoloid

zygoloid commented on Mar 13, 2023

@zygoloid
Collaborator

Note that copy elision is not performed as part of constant evaluation. If there is a bug here, the bug would be that we should not be calling a copy constructor at all as part of initialization.

AaronBallman

AaronBallman commented on Mar 13, 2023

@AaronBallman
CollaboratorAuthor

Oh, thank you for pointing that out Richard, I had completely missed the bit you linked.

Fznamznon

Fznamznon commented on Mar 22, 2023

@Fznamznon
Contributor

I've spent some time looking at this. It seems that when a potential immediate invocation is met, it is immediately wrapped by a ConstantExpr but not yet evaluated. There is also a TreeTransform that removes this ConstantExpr wrapper when corresponding expression evaluation context is popped.
There is a "strange" (according to the comments) place where CXXFunctionalCastExpr is added to AST:

Result = CXXFunctionalCastExpr::Create(

Basically there is a check that if CXXTemporaryObjectExpr was created, then no need to add an additional CXXFunctionalCastExpr. However due to default constructor being consteval and therefore its use being an immediate invocation, there was CXXTemporaryObjectExpr but wrapped with ConstantExpr representing an immediate invocation (mentioned above). That caused adding additional CXXFunctionalCastExpr and this CXXFunctionalCastExpr being in place caused the TreeTransform that happens at place where immediate invocations are handled (also mentioned above) to add constructor call causing the error. So, basically, adding something like

diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index e7f3852ae34c..d687e87fcb0e 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1590,6 +1590,9 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo,
   Expr *Inner = Result.get();
   if (CXXBindTemporaryExpr *BTE = dyn_cast_or_null<CXXBindTemporaryExpr>(Inner))
     Inner = BTE->getSubExpr();
+  if (ConstantExpr *CE = dyn_cast_or_null<ConstantExpr>(Inner))
+    if (CE->isImmediateInvocation())
+      Inner = CE->getSubExpr();
   if (!isa<CXXTemporaryObjectExpr>(Inner) &&
       !isa<CXXScalarValueInitExpr>(Inner)) {
     // If we created a CXXTemporaryObjectExpr, that node also represents the

Helps to stop diagnosing this code, which considered as valid by other compilers. If that sounds ok, I can put the patch for review.

Fznamznon

Fznamznon commented on Mar 24, 2023

@Fznamznon
Contributor
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @zygoloid@Fedr@AaronBallman@Fznamznon@llvmbot

        Issue actions

          Constant expression evaluation misses copy elision · Issue #60286 · llvm/llvm-project