Skip to content

Clang does not accept consteval operators #62886

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
usx95 opened this issue May 23, 2023 · 8 comments
Closed

Clang does not accept consteval operators #62886

usx95 opened this issue May 23, 2023 · 8 comments
Assignees
Labels
c++20 clang:frontend Language frontend issues, e.g. anything involving "Sema" consteval C++20 consteval rejects-valid

Comments

@usx95
Copy link
Contributor

usx95 commented May 23, 2023

GCC: g++.dg/cpp2a/consteval31.C

struct A {
    consteval A operator+() { return {}; }
};
consteval A operator~(A) { return {}; }
consteval A operator+(A, A) { return {}; }

template <class> void f() {
    A a;
    A b = ~a; // cannot take address of consteval function 'operator~' outside of an immediate invocation
    A c = a + a; // similar error
    A d = +a; // similar error
}
template void f<int>();

https://godbolt.org/z/MxncM3Yaq
Looks important to fix and a possible blocker to #57094

@usx95 usx95 added c++20 clang:frontend Language frontend issues, e.g. anything involving "Sema" rejects-valid consteval C++20 consteval labels May 23, 2023
@llvmbot
Copy link
Member

llvmbot commented May 23, 2023

@llvm/issue-subscribers-c-20

@llvmbot
Copy link
Member

llvmbot commented May 23, 2023

@llvm/issue-subscribers-clang-frontend

@usx95
Copy link
Contributor Author

usx95 commented May 23, 2023

This only happens inside a template.
Eg. A f() { return A{} + A{}; } works fine.

@Fznamznon
Copy link
Contributor

Wow, I'd like to take a look.

@Fznamznon Fznamznon self-assigned this May 23, 2023
@Fznamznon
Copy link
Contributor

I spent time debugging issue with operator ~ but I suppose the bug here is the same for all operators.
The problem is that while instantiating a template there is two different DeclRefExprs referencing the same operator's declaration are somehow marked as referenced and therefore added to an ReferenceToConsteval array, but at the end the operator call is evaluated only once, so only one refence is removed from the array. Every reference to consteval function in ReferenceToConsteval array may produce the diagnostic we're seeing if not removed properly.

I'm feeling there might be a bug in template instantiation process.

The first time the original DeclRefExpr is added to ReferenceToConsteval then:

#0  clang::Sema::MarkDeclRefReferenced (this=0x16579cf0, E=0x165ab7e8, Base=0x0) at source/llorg/llvm-project/clang/lib/Sema/SemaExpr.cpp:20222
#1  0x000000000a596faf in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformDeclRefExpr (this=0x7fffffff7430, E=0x165ab7e8)
    at source/llorg/llvm-project/clang/lib/Sema/TreeTransform.h:10791
#2  0x000000000a58ae69 in (anonymous namespace)::TemplateInstantiator::TransformDeclRefExpr (this=0x7fffffff7430, E=0x165ab7e8)
    at source/llorg/llvm-project/clang/lib/Sema/SemaTemplateInstantiate.cpp:2081
#3  0x000000000a59603a in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformExpr (this=0x7fffffff7430, E=0x165ab7e8)
    at source/llorg/llvm-project/debug/tools/clang/include/clang/AST/StmtNodes.inc:1109
#4  0x000000000a5a587f in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformImplicitCastExpr (this=0x7fffffff7430, E=0x165ab868)
    at source/llorg/llvm-project/clang/lib/Sema/TreeTransform.h:11552
#5  0x000000000a595ee2 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformExpr (this=0x7fffffff7430, E=0x165ab868)
    at source/llorg/llvm-project/debug/tools/clang/include/clang/AST/StmtNodes.inc:1049
#6  0x000000000a5a4e84 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformCXXOperatorCallExpr (this=0x7fffffff7430, E=0x165ab880)
    at source/llorg/llvm-project/clang/lib/Sema/TreeTransform.h:11939
#7  0x000000000a595d09 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformExpr (this=0x7fffffff7430, E=0x165ab880)
    at source/llorg/llvm-project/debug/tools/clang/include/clang/AST/StmtNodes.inc:959
#8  0x000000000a59c83e in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformInitializer (this=0x7fffffff7430, Init=0x165ab880, NotCopyInit=false)
    at source/llorg/llvm-project/clang/lib/Sema/TreeTransform.h:4058

And then the whole operator is rebuilt for some reason, including the new reference to an overloaded operator, the new DeclRefExpr is added here:

#0  clang::Sema::MarkDeclRefReferenced (this=0x16579cf0, E=0x165abd28, Base=0x0) at /source/llorg/llvm-project/clang/lib/Sema/SemaExpr.cpp:20222
#1  0x000000000a209542 in CreateFunctionRefExpr (S=..., Fn=0x1658e770, FoundDecl=0x1658e770, Base=0x0, HadMultipleCandidates=false, Loc=..., LocInfo=...)
    at /source/llorg/llvm-project/clang/lib/Sema/SemaOverload.cpp:76
#2  0x000000000a239838 in clang::Sema::CreateOverloadedUnaryOp (this=0x16579cf0, OpLoc=..., Opc=clang::UO_Not, Fns=..., Input=0x165abcf8, PerformADL=false)
    at /source/llorg/llvm-project/clang/lib/Sema/SemaOverload.cpp:13702
#3  0x000000000a5c1756 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::RebuildCXXOperatorCallExpr (this=0x7fffffff7430, Op=clang::OO_Tilde,
    OpLoc=..., OrigCallee=0x165ab7e8, First=0x165abcc0, Second=0x0) at /source/llorg/llvm-project/clang/lib/Sema/TreeTransform.h:15190
#4  0x000000000a5a51c1 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformCXXOperatorCallExpr (this=0x7fffffff7430, E=0x165ab880)
    at /source/llorg/llvm-project/clang/lib/Sema/TreeTransform.h:11974
#5  0x000000000a595d09 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformExpr (this=0x7fffffff7430, E=0x165ab880)
    at /source/llorg/llvm-project/debug/tools/clang/include/clang/AST/StmtNodes.inc:959
#6  0x000000000a59c83e in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformInitializer (this=0x7fffffff7430, Init=0x165ab880, NotCopyInit=false)
    at /source/llorg/llvm-project/clang/lib/Sema/TreeTransform.h:4058

I'm new to templates instantiation, but I have a gut feeling that we shouldn't be rebuilding the whole operator call in this case, especially after we decided to mark original DeclRefExpr as referenced.
When I make struct A a template parameter - the code is accepted https://godbolt.org/z/a593xsar3 normally, so there is no mismatch like I explained above, probably because rebuild is likely required.
There is also no "double-referencing" situation like this for regular functions (not operators).

I'll keep digging, but cc @erichkeane in case my understanding regarding template instantiation process is wrong.

@erichkeane
Copy link
Collaborator

I'm not very familiar with the ReferenceToConsteval array unfortunately... BUT I wonder if the adding to that collection needs to do the work to ensure that the decl isn't already referenced before putting it in the array? I think we still need both of those cases to be marking the decl referenced (in case it ISN'T via decl ref that the operator is referenced), but putting it in 2x is probably the mistake.

@Fznamznon
Copy link
Contributor

I think we still need both of those cases to be marking the decl referenced (in case it ISN'T via decl ref that the operator is referenced)

That is the decl ref via which the operator is referenced. This one:
DeclRefExpr 0x165fa5c8 'A (A)' lvalue Function 0x165dd5d0 'operator~' 'A (A)'

But my question is, why the original decl ref coming from the function template is first marked as referenced (likely in a context of a new function created, if I understand correctly the comment in here -

// Mark it referenced in the new context regardless.
), but then the whole operator call (including a decl ref referencing the operator) is rebuilt even though no template parameters participate in the call expression? Is that how normally templates work?

@Fznamznon
Copy link
Contributor

Proposed https://reviews.llvm.org/D151553

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++20 clang:frontend Language frontend issues, e.g. anything involving "Sema" consteval C++20 consteval rejects-valid
Projects
Status: Done
Development

No branches or pull requests

4 participants