This is an archive of the discontinued LLVM Phabricator instance.

[clang] p2085 out-of-class comparison operator defaulting
ClosedPublic

Authored by urnathan on Jun 17 2021, 11:21 AM.

Details

Summary

This implements p2085, allowing out-of-class defaulting of comparison operators, primarily so they need not be inline, IIUC intent. this was mostly straigh forward, but required reimplementing Sema::CheckExplicitlyDefaultedComparison, as now there's a case where we have no a priori clue as to what class a defaulted comparison may be for. We have to inspect the parameter types to find out. Eg:

class X { ... };
bool operator==(X, X) = default;

Thus reimplemented the parameter type checking, and added 'is this a friend' functionality for the above case.

AFAICT, this is affecting code Richard authored.

Diff Detail

Event Timeline

urnathan requested review of this revision.Jun 17 2021, 11:21 AM
urnathan created this revision.
urnathan updated this revision to Diff 371650.Sep 9 2021, 10:26 AM

updated against current HEAD. ping?

urnathan updated this revision to Diff 379010.Oct 12 2021, 6:30 AM

updated to trunk. ping?

urnathan updated this revision to Diff 379336.Oct 13 2021, 4:14 AM

Correct the cxx_status clang version number. Let's not let this roll over again!

Hello! Thanks for this, just a few small comments, I'll try to give it a more careful look later!

clang/lib/Sema/SemaDeclCXX.cpp
8437

The curly braces are not needed.

8452–8455

Our style guidelines dictate that the braces are all or nothing :)

8468

Our style says to construct with curlies only simple aggregate classes.

8504–8514

If you would like to make this a bit shorter.

urnathan updated this revision to Diff 379994.Oct 15 2021, 7:16 AM
This comment was removed by urnathan.
urnathan updated this revision to Diff 379999.Oct 15 2021, 7:28 AM
urnathan marked 3 inline comments as done.

Let's try that again. Thanks for the formatting guide and pointing at find_if. I think this addresses all those points.

urnathan marked an inline comment as done.Oct 15 2021, 7:28 AM
urnathan added inline comments.
clang/lib/Sema/SemaDeclCXX.cpp
8452–8455

FWIW, this doesnt seem adhered to very consistently. I did not deduce that from the existing code.

mizvekov added inline comments.Oct 18 2021, 8:06 AM
clang/lib/Sema/SemaDeclCXX.cpp
8431–8432

I think this might not be correct and we will miss diagnosing a case like:

template <class T> struct A {
  bool operator==(T const &) const;
};
template <class T> bool A<T>::operator==(T const &) const = default;
template struct A<int>;
8436

The whole logic involving this Ok is a little bit hard to follow, but I see that it's not straightforward to simplify.
The main thing here I think is that we are checking many different things and so it's hard to give it a less generic name.
Just a note on my part though, I don't have specific suggestions :)

8468–8469

Looking at this more carefully, we did not even need to set PlainTy to null in the first place, in that IsMethod==true case it will just be ignored in the diagnostics.

mizvekov added inline comments.Oct 18 2021, 1:48 PM
clang/lib/Sema/SemaDeclCXX.cpp
8431–8432

So I just downloaded the patch to try this out, there are indeed problems.

This test case is accepted:

template <class T> struct S6 {
  bool operator==(T const &) const;
};
template <class T> bool S6<T>::operator==(T const &) const = default;

// template struct S6<int>;

void f() {
  S6<int> a;
  (void)(a == 0);
}

If you uncomment the line, it crashes clang instead, with this backtrace:

PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash backtrace, preprocessed source, and associated run script.
Stack dump:
0.      Program arguments: llvm\\bin\\clang.exe -cc1 -internal-isystem llvm\\lib\\clang\\14.0.0\\include -nostdsysteminc -std=c++2a -verify clang\\test\\CXX\\class\\class.compare\\class.compare.default\\p1.cpp
1.      clang\test\CXX\class\class.compare\class.compare.default\p1.cpp:174:16: current parser token ')'
2.      clang\test\CXX\class\class.compare\class.compare.default\p1.cpp:129:1: parsing namespace 'p2085'
3.      clang\test\CXX\class\class.compare\class.compare.default\p1.cpp:172:10: parsing function body 'p2085::f'
4.      clang\test\CXX\class\class.compare\class.compare.default\p1.cpp:172:10: in compound statement ('{}')
 #0 0x00007ff7c6c86dcb clang::Redeclarable<class clang::TagDecl>::getFirstDecl(void) clang\include\clang\AST\Redeclarable.h:216:0
 #1 0x00007ff7c6c8a7b4 clang::Redeclarable<class clang::TagDecl>::getMostRecentDecl(void) clang\include\clang\AST\Redeclarable.h:227:0
 #2 0x00007ff7c6c8a89b clang::RecordDecl::getMostRecentDecl(void) clang\include\clang\AST\Decl.h:3907:0
 #3 0x00007ff7c6c8a814 clang::CXXRecordDecl::getMostRecentDecl(void) clang\include\clang\AST\DeclCXX.h:512:0
 #4 0x00007ff7c6c8a844 clang::CXXRecordDecl::getMostRecentDecl(void) const clang\include\clang\AST\DeclCXX.h:518:0
 #5 0x00007ff7c6c57d74 clang::CXXRecordDecl::dataPtr(void) const clang\include\clang\AST\DeclCXX.h:430:0
 #6 0x00007ff7c6c57c84 clang::CXXRecordDecl::data(void) const clang\include\clang\AST\DeclCXX.h:434:0
 #7 0x00007ff7c6c4cfb4 clang::CXXRecordDecl::bases_begin(void) clang\include\clang\AST\DeclCXX.h:596:0
 #8 0x00007ff7c6c4cfe4 clang::CXXRecordDecl::bases_end(void) clang\include\clang\AST\DeclCXX.h:598:0
 #9 0x00007ff7c6c4cf59 clang::CXXRecordDecl::bases(void) clang\include\clang\AST\DeclCXX.h:590:0
#10 0x00007ff7cb0e31a8 `anonymous namespace'::DefaultedComparisonVisitor<`anonymous namespace'::DefaultedComparisonSynthesizer,A0xf8d43d5f::StmtListResult,clang::ActionResult<clang::Stmt *,1>,std::pair<clang::ActionResult<clang::Expr *,1>,clang::ActionResult<clang::Expr *,1> > >::visitSubobjects clang\lib\Sema\SemaDeclCXX.cpp:7626:0
#11 0x00007ff7cb0e302e `anonymous namespace'::DefaultedComparisonVisitor<`anonymous namespace'::DefaultedComparisonSynthesizer,A0xf8d43d5f::StmtListResult,clang::ActionResult<clang::Stmt *,1>,std::pair<clang::ActionResult<clang::Expr *,1>,clang::ActionResult<clang::Expr *,1> > >::visit clang\lib\Sema\SemaDeclCXX.cpp:7604:0
#12 0x00007ff7cb0e3738 `anonymous namespace'::DefaultedComparisonSynthesizer::build clang\lib\Sema\SemaDeclCXX.cpp:8061:0
#13 0x00007ff7cb0bc39c clang::Sema::DefineDefaultedComparison(class clang::SourceLocation, class clang::FunctionDecl *, enum clang::Sema::DefaultedComparisonKind) clang\lib\Sema\SemaDeclCXX.cpp:8739:0
#14 0x00007ff7cb933160 <lambda_1975556f3529fa94ad6319a5b3e731e2>::operator() clang\lib\Sema\SemaExpr.cpp:17223:0
#15 0x00007ff7cb9a4ed4 llvm::function_ref<void __cdecl(void)>::callback_fn<<lambda_1975556f3529fa94ad6319a5b3e731e2> > llvm\include\llvm\ADT\STLExtras.h:179:0
#16 0x00007ff7c655009a llvm::function_ref<(void)>::operator()(void) const llvm\include\llvm\ADT\STLExtras.h:201:0
#17 0x00007ff7c3fc207f clang::runWithSufficientStackSpace(class llvm::function_ref<(void)>, class llvm::function_ref<(void)>) clang\include\clang\Basic\Stack.h:52:0
#18 0x00007ff7cae65393 clang::Sema::runWithSufficientStackSpace(class clang::SourceLocation, class llvm::function_ref<(void)>) clang\lib\Sema\Sema.cpp:489:0
#19 0x00007ff7cb8b0b38 clang::Sema::MarkFunctionReferenced(class clang::SourceLocation, class clang::FunctionDecl *, bool) clang\lib\Sema\SemaExpr.cpp:17281:0
#20 0x00007ff7cb8b079d clang::Sema::MarkAnyDeclReferenced(class clang::SourceLocation, class clang::Decl *, bool) clang\lib\Sema\SemaExpr.cpp:18819:0
#21 0x00007ff7cb937e39 MarkExprReferenced clang\lib\Sema\SemaExpr.cpp:18736:0
#22 0x00007ff7cb8b10e3 clang::Sema::MarkDeclRefReferenced(class clang::DeclRefExpr *, class clang::Expr const *) clang\lib\Sema\SemaExpr.cpp:18776:0
#23 0x00007ff7cc0bd380 CreateFunctionRefExpr clang\lib\Sema\SemaOverload.cpp:73:0
#24 0x00007ff7cc0b2cec clang::Sema::CreateOverloadedBinOp(class clang::SourceLocation, enum clang::BinaryOperatorKind, class clang::UnresolvedSetImpl const &, class clang::Expr *, class clang::Expr *, bool, bool, class clang::FunctionDecl *) clang\lib\Sema\SemaOverload.cpp:13759:0
#25 0x00007ff7cb90b61f BuildOverloadedBinOp clang\lib\Sema\SemaExpr.cpp:14645:0
#26 0x00007ff7cb8d1a0f clang::Sema::BuildBinOp(class clang::Scope *, class clang::SourceLocation, enum clang::BinaryOperatorKind, class clang::Expr *, class clang::Expr *) clang\lib\Sema\SemaExpr.cpp:14747:0
#27 0x00007ff7cb8d1093 clang::Sema::ActOnBinOp(class clang::Scope *, class clang::SourceLocation, enum clang::tok::TokenKind, class clang::Expr *, class clang::Expr *) clang\lib\Sema\SemaExpr.cpp:14604:0
#28 0x00007ff7cada2e98 clang::Parser::ParseRHSOfBinaryExpression(class clang::ActionResult<class clang::Expr *, 1>, enum clang::prec::Level) clang\lib\Parse\ParseExpr.cpp:631:0
#29 0x00007ff7cada155a clang::Parser::ParseAssignmentExpression(enum clang::Parser::TypeCastState) clang\lib\Parse\ParseExpr.cpp:176:0
#30 0x00007ff7cadae36e clang::Parser::ParseSimpleExpressionList(class llvm::SmallVectorImpl<class clang::Expr *> &, class llvm::SmallVectorImpl<class clang::SourceLocation> &) clang\lib\Parse\ParseExpr.cpp:3422:0
#31 0x00007ff7cadafd37 clang::Parser::ParseParenExpression(enum clang::Parser::ParenParseOption &, bool, bool, class clang::OpaquePtr<class clang::QualType> &, class clang::SourceLocation &) clang\lib\Parse\ParseExpr.cpp:3058:0
#32 0x00007ff7cada36e3 clang::Parser::ParseCastExpression(enum clang::Parser::CastParseKind, bool, bool &, enum clang::Parser::TypeCastState, bool, bool *) clang\lib\Parse\ParseExpr.cpp:957:0
#33 0x00007ff7cada8b26 clang::Parser::ParseCastExpression(enum clang::Parser::CastParseKind, bool, enum clang::Parser::TypeCastState, bool, bool *) clang\lib\Parse\ParseExpr.cpp:687:0
#34 0x00007ff7cadafa77 clang::Parser::ParseParenExpression(enum clang::Parser::ParenParseOption &, bool, bool, class clang::OpaquePtr<class clang::QualType> &, class clang::SourceLocation &) clang\lib\Parse\ParseExpr.cpp:3033:0
#35 0x00007ff7cada36e3 clang::Parser::ParseCastExpression(enum clang::Parser::CastParseKind, bool, bool &, enum clang::Parser::TypeCastState, bool, bool *) clang\lib\Parse\ParseExpr.cpp:957:0
#36 0x00007ff7cada8b26 clang::Parser::ParseCastExpression(enum clang::Parser::CastParseKind, bool, enum clang::Parser::TypeCastState, bool, bool *) clang\lib\Parse\ParseExpr.cpp:687:0
#37 0x00007ff7cada153a clang::Parser::ParseAssignmentExpression(enum clang::Parser::TypeCastState) clang\lib\Parse\ParseExpr.cpp:176:0
#38 0x00007ff7cada06ee clang::Parser::ParseExpression(enum clang::Parser::TypeCastState) clang\lib\Parse\ParseExpr.cpp:125:0
#39 0x00007ff7caddca7f clang::Parser::ParseExprStatement(enum clang::Parser::ParsedStmtContext) clang\lib\Parse\ParseStmt.cpp:465:0
#40 0x00007ff7caddb8e8 clang::Parser::ParseStatementOrDeclarationAfterAttributes(class llvm::SmallVector<class clang::Stmt *, 32> &, enum clang::Parser::ParsedStmtContext, class clang::SourceLocation *, struct clang::ParsedAttributesWithRange &) clang\lib\Parse\ParseStmt.cpp:243:0
#41 0x00007ff7caddb1a8 clang::Parser::ParseStatementOrDeclaration(class llvm::SmallVector<class clang::Stmt *, 32> &, enum clang::Parser::ParsedStmtContext, class clang::SourceLocation *) clang\lib\Parse\ParseStmt.cpp:115:0
#42 0x00007ff7caddeb49 clang::Parser::ParseCompoundStatementBody(bool) clang\lib\Parse\ParseStmt.cpp:1111:0
#43 0x00007ff7cade55b2 clang::Parser::ParseFunctionStatementBody(class clang::Decl *, class clang::Parser::ParseScope &) clang\lib\Parse\ParseStmt.cpp:2346:0
#44 0x00007ff7caceec07 clang::Parser::ParseFunctionDefinition(class clang::ParsingDeclarator &, struct clang::Parser::ParsedTemplateInfo const &, class clang::Parser::LateParsedAttrList *) clang\lib\Parse\Parser.cpp:1378:0
#45 0x00007ff7cad33d02 clang::Parser::ParseDeclGroup(class clang::ParsingDeclSpec &, enum clang::DeclaratorContext, class clang::SourceLocation *, struct clang::Parser::ForRangeInit *) clang\lib\Parse\ParseDecl.cpp:2041:0
#46 0x00007ff7caced8f7 clang::Parser::ParseDeclOrFunctionDefInternal(struct clang::ParsedAttributesWithRange &, class clang::ParsingDeclSpec &, enum clang::AccessSpecifier) clang\lib\Parse\Parser.cpp:1138:0
#47 0x00007ff7caced1a2 clang::Parser::ParseDeclarationOrFunctionDefinition(struct clang::ParsedAttributesWithRange &, class clang::ParsingDeclSpec *, enum clang::AccessSpecifier) clang\lib\Parse\Parser.cpp:1154:0
#48 0x00007ff7cacecbb0 clang::Parser::ParseExternalDeclaration(struct clang::ParsedAttributesWithRange &, class clang::ParsingDeclSpec *) clang\lib\Parse\Parser.cpp:976:0
#49 0x00007ff7cadf9278 clang::Parser::ParseInnerNamespace(class llvm::SmallVector<struct clang::Parser::InnerNamespaceInfo, 4> const &, unsigned int, class clang::SourceLocation &, class clang::ParsedAttributes &, class clang::BalancedDelimiterTracker &) clang\lib\Parse\ParseDeclCXX.cpp:247:0
#50 0x00007ff7cadf9060 clang::Parser::ParseNamespace(enum clang::DeclaratorContext, class clang::SourceLocation &, class clang::SourceLocation) clang\lib\Parse\ParseDeclCXX.cpp:227:0
#51 0x00007ff7cad32d4a clang::Parser::ParseDeclaration(enum clang::DeclaratorContext, class clang::SourceLocation &, struct clang::ParsedAttributesWithRange &, class clang::SourceLocation *) clang\lib\Parse\ParseDecl.cpp:1742:0
#52 0x00007ff7cacec629 clang::Parser::ParseExternalDeclaration(struct clang::ParsedAttributesWithRange &, class clang::ParsingDeclSpec *) clang\lib\Parse\Parser.cpp:910:0
#53 0x00007ff7cace7384 clang::Parser::ParseTopLevelDecl(class clang::OpaquePtr<class clang::DeclGroupRef> &, bool) clang\lib\Parse\Parser.cpp:720:0
#54 0x00007ff7cace36a8 clang::ParseAST(class clang::Sema &, bool, bool) clang\lib\Parse\ParseAST.cpp:158:0
#55 0x00007ff7c7b7e1e7 clang::ASTFrontendAction::ExecuteAction(void) clang\lib\Frontend\FrontendAction.cpp:1066:0
#56 0x00007ff7c7b7db9e clang::FrontendAction::Execute(void) clang\lib\Frontend\FrontendAction.cpp:961:0
#57 0x00007ff7c7afbb96 clang::CompilerInstance::ExecuteAction(class clang::FrontendAction &) clang\lib\Frontend\CompilerInstance.cpp:1027:0
#58 0x00007ff7c7d68ee8 clang::ExecuteCompilerInvocation(class clang::CompilerInstance *) clang\lib\FrontendTool\ExecuteCompilerInvocation.cpp:261:0
#59 0x00007ff7c3d8e6b4 cc1_main(class llvm::ArrayRef<char const *>, char const *, void *) clang\tools\driver\cc1_main.cpp:246:0
urnathan updated this revision to Diff 382979.Oct 28 2021, 3:58 AM

thanks for the testcase. The problem is not that we're not checking dependent types at that point, but that we don't check later when we have an instantiation. the 'evil' testcase shows why one cannot check earlier -- though I hope no one writes that! The instantiation code was 'optimized' with the knowledge that comparison operators can only be defaulted in-class, which is of course no longer true with p2085.

Am I correct in thinking that instantiating a function definition doesn't create a new FunctionDecl, but attaches the definition to the already-instantiated FunctionDecl instance? See the code where I update the instantiation's location in the out-of-class defaulting case. Without that change, the errors for bad default parameter types point at the in-class declaration, which is not helpful.

urnathan marked 2 inline comments as done.Oct 28 2021, 4:02 AM
urnathan updated this revision to Diff 384766.Nov 4 2021, 8:34 AM
urnathan marked an inline comment as done.

remove unneeded PlainTy clearing

urnathan marked an inline comment as done.Nov 4 2021, 8:35 AM
mizvekov added a comment.EditedNov 4 2021, 4:37 PM

Am I correct in thinking that instantiating a function definition doesn't create a new FunctionDecl, but attaches the definition to the already-instantiated FunctionDecl instance? See the code where I update the instantiation's location in the out-of-class defaulting case. Without that change, the errors for bad default parameter types point at the in-class declaration, which is not helpful.

If you look at the AST dump I posted in my other comment, you have the comparison operator's CXXMethodDecl associated to the TemplateDecl, and another one associated to the TemplateSpecializationDecl.

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
5003–5006 ↗(On Diff #384766)

You can check the result of this using clang's ast-dump:

Source:

template <class T> struct S6 {
  bool operator==(T const &) const;
};
template <class T> bool S6<T>::operator==(T const &) const = default;
template struct S6<int>;

clang++ -cc1 -fsyntax-only -std=c++20 -ast-dump

AST produced with this change:

|-ClassTemplateDecl 0x150a4970e88 <..\..\..\clang-snipets\D104478\test1.cc:1:1, line:3:1> line:1:27 S6
| |-TemplateTypeParmDecl 0x150a49016b0 <col:11, col:17> col:17 referenced class depth 0 index 0 T
| |-CXXRecordDecl 0x150a4901778 <col:20, line:3:1> line:1:27 struct S6 definition
| | |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
| | | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
| | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveConstructor exists simple trivial needs_implicit
| | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveAssignment exists simple trivial needs_implicit
| | | `-Destructor simple irrelevant trivial constexpr needs_implicit
| | |-CXXRecordDecl 0x150a49710e8 <col:20, col:27> col:27 implicit struct S6
| | `-CXXMethodDecl 0x150a4971340 <line:2:3, col:30> col:8 operator== 'bool (const T &) const'
| |   `-ParmVarDecl 0x150a4971200 <col:19, col:27> col:28 'const T &'
| `-ClassTemplateSpecialization 0x150a4971810 'S6'
|-CXXMethodDecl 0x150a4971700 parent 0x150a4901778 prev 0x150a4971340 <line:4:1, col:54> col:32 operator== 'bool (const T &) const' default
| `-ParmVarDecl 0x150a4971620 <col:43, col:51> col:52 'const T &'
`-ClassTemplateSpecializationDecl 0x150a4971810 <line:5:1, col:23> col:17 struct S6 definition
  |-DefinitionData pass_in_registers empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
  | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
  | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
  | |-MoveConstructor exists simple trivial needs_implicit
  | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
  | |-MoveAssignment exists simple trivial needs_implicit
  | `-Destructor simple irrelevant trivial constexpr needs_implicit
  |-TemplateArgument type 'int'
  | `-BuiltinType 0x150a4900950 'int'
  |-CXXRecordDecl 0x150a4971a10 prev 0x150a4971810 <line:1:20, col:27> col:27 implicit struct S6
  `-CXXMethodDecl 0x150a4971c40 <line:4:20, line:2:30> line:4:32 invalid operator== 'bool (const int &) const' default
    `-ParmVarDecl 0x150a4971b40 <line:2:19, col:27> col:28 'const int &'

Second to last line, you have the CXXMethodDecl with the adjusted location, but it's ParmVarDecl's location still refers to the one in the original declaration.

I don't think you are supposed to do this kind of adjustment, it's more likely there is a bug / missing change somewhere else.

urnathan updated this revision to Diff 387283.Nov 15 2021, 8:37 AM

When we instantiate a template definition, we don't instantiate a new declaration. That's fine when the introducing declaration is the definition. But when we have a forward declaration, we can run into problems. For instance, regard:

template<typename T> struct S {
  void frob (T const &a);
};

template<typename T> void S<T>::frob (T const &bad)
{ bad = 5; }

template struct S<int>;

devvm3900:55>build/bin/clang++ -c mem.ii

mem.ii:6:7: error: cannot assign to variable 'bad' with const-qualified type 'const int &'
{ bad = 5; }
  ~~~ ^
mem.ii:8:17: note: in instantiation of member function 'S<int>::frob' requested here
template struct S<int>;
                ^
mem.ii:2:23: note: variable 'bad' declared const here
  void frob (T const &a);
             ~~~~~~~~~^

notice the location of where it claims 'bad' is!

So let's just avoid working around existing issue with template definitions not being the introducing decl, and leave that as an orthogonal problem.

mizvekov accepted this revision.EditedNov 16 2021, 3:34 PM

From my POV this looks good.

I would have perhaps added some AST tests here.

ping @rsmith any thoughts / suggestions / improvements here?

This revision is now accepted and ready to land.Nov 16 2021, 3:34 PM

When we instantiate a template definition, we don't instantiate a new declaration. That's fine when the introducing declaration is the definition. But when we have a forward declaration, we can run into problems. For instance, regard:

I think the -ast-dump of that example shows that we do instantiate a new declaration:

|-ClassTemplateDecl 0x1a28478a968 <test.cc:1:1, line:3:1> line:1:29 S
| |-TemplateTypeParmDecl 0x1a28471af50 <col:10, col:19> col:19 referenced typename depth 0 index 0 T
| |-CXXRecordDecl 0x1a28471b018 <col:22, line:3:1> line:1:29 struct S definition
| | |-DefinitionData empty aggregate standard_layout trivially_copyable pod trivial literal has_constexpr_non_copy_move_ctor can_const_default_init
| | | |-DefaultConstructor exists trivial constexpr needs_implicit defaulted_is_constexpr
| | | |-CopyConstructor simple trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveConstructor exists simple trivial needs_implicit
| | | |-CopyAssignment simple trivial has_const_param needs_implicit implicit_has_const_param
| | | |-MoveAssignment exists simple trivial needs_implicit
| | | `-Destructor simple irrelevant trivial constexpr needs_implicit
| | |-CXXRecordDecl 0x1a28478abc8 <col:22, col:29> col:29 implicit struct S
| | `-CXXMethodDecl 0x1a28478ae20 <line:2:3, col:24> col:8 frob 'void (const T &)' <=== #1
| |   `-ParmVarDecl 0x1a28478ace0 <col:14, col:23> col:23 a 'const T &'
| `-ClassTemplateSpecialization 0x1a28478b370 'S'
|-CXXMethodDecl 0x1a28478b1e0 parent 0x1a28471b018 prev 0x1a28478ae20 <line:5:1, line:6:12> line:5:33 frob 'void (const T &)' <=== #2
| |-ParmVarDecl 0x1a28478b100 <col:39, col:48> col:48 referenced bad 'const T &'
| `-CompoundStmt 0x1a28478b338 <line:6:1, col:12>
|   `-BinaryOperator 0x1a28478b318 <col:3, col:9> '<dependent type>' '='
|     |-DeclRefExpr 0x1a28478b2d0 <col:3> 'const T' lvalue ParmVar 0x1a28478b100 'bad' 'const T &'
|     `-IntegerLiteral 0x1a28478b2f0 <col:9> 'int' 5

But I agree this is a separate problem that can be solved in another patch.

Herald added a project: Restricted Project. · View Herald TranscriptDec 16 2021, 7:23 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript