class template methods

Hi,

Is there a way to ask clang whether a method for an instantiation of a class template would be an error to call? (I only need yes/no)

For example:

std::pair has assignment operators defined, but for the instantiation std::pair<int const, int const> calling the assignment operators is an error, because you can't assign to int const.

If clang can't tell me this, I'm looking at generating a translation unit with a call to the method, and then checking whether there was an error compiling it. Given that I will need to do this once per method, per instantiation, per template, (which looks to be in the thousands based on a test I just did) what is the best way to do this?

Sounds a lot like a SFINAE test. Assuming you already have an AST with the class template instantiation, then you’d attempt to instantiate the assignment operator inside a SFINAETrap. Instantiation will either pass or fail and you’ll get your answer, and you should be able to continue compilation or further queries normally.

Sounds a lot like a SFINAE test. Assuming you already have an AST with the
class template instantiation, then you'd attempt to instantiate the
assignment operator inside a SFINAETrap. Instantiation will either pass or
fail and you'll get your answer, and you should be able to continue
compilation or further queries normally.

I think the above is about the best that you can do.

One note: if an error occurs outside of the immediate context of the
substitution, a SFINAETrap will not suppress it (which is actually a good
thing in this case, because you cannot continue compilation normally after
such an error -- any part of the AST that failed may be marked invalid,
suppressing further errors in other contexts).

This does sound promising, but I'm not sure how to make use of it.

At the moment my walker (which is based on clang::ASTConsumer) has a handler for class templates that looks like:

void Walker::TraverseClassTemplate(clang::ClassTemplateDecl const* d) {
   TRACE(1, "walk template " + getDeclMetadataString(d));

   bool was = inInstantiation;
   inInstantiation = true;

   for(auto decl : d->specializations()) {
     visitor->RequestCompleteType(decl);
     TraverseCXXRecord(decl);
   }

   inInstantiation = was;
}

Visitor::RequestCompleteType() calls Sema::RequireCompleteType() (which IMO has the wrong name, it returns false on what I would consider, from the name, success).

So, I don't construct the specializations for methods directly in my code, I assume that some of the instantiation is performed by RequireCompleteType(), but surely if I checked for SFINAE at that level then I would lose EVERY methods for std::pair<int const, int const> and not just the ones that involve mutation?

Anyone?

Visitor::RequestCompleteType() calls Sema::RequireCompleteType() (which
IMO has the wrong name, it returns false on what I would consider, from the
name, success).

Much has been made about the sense of boolean return values in Clang. It
isn't consistent or sane, but such is life. =/

So, I don't construct the specializations for methods directly in my code,
I assume that some of the instantiation is performed by
RequireCompleteType(), but surely if I checked for SFINAE at that level
then I would lose EVERY methods for std::pair<int const, int const> and not
just the ones that involve mutation?

I think RequireCompleteType won't instantiate all the member functions,
like operator=, but you can look it up, and ask for an instantiation of it.

Perhaps I misunderstand what you mean by ‘instantiate’ but this doesn’t seem to be the case. When I walk the CXXRecordDecl for ‘std::pair<int const, int const>’ there ARE declarations for the methods that require mutation. My tool scans the AST for functions and methods and outputs a file containing functions that invoke the ones it finds, which is then compiled into a shared library, so what I want is protection against functions that can’t be called. How do I go about asking for an instantiation of a method on its own?

Right, instantiating the class will create decls of all the member
functions, but the bodies will be empty because C++ essentially requires
that they be parsed lazily. I'd have to lookup the detailed mechanics, but
you should be able to look at the method decl, figure out that it needs
instantiation, instantiate it, and put the error trap around that.

I added the following test: if(inInstantiation) { clang::Sema::SFINAETrap trap(sema); sema.InstantiateFunctionDefinition(d->getParent()->getLocation(), d, true, true); if(trap.hasErrorOccurred()) { return false; } } Now I’m hitting a seg-fault (null pointer) deep inside clang: Program received signal SIGSEGV, Segmentation fault. clang::Scope::getEntity (this=0x0) at /home/peter/Programming/llvm/llvm/tools/clang/include/clang/Sema/Scope.h:310 310 DeclContext *getEntity() const { return Entity; } (gdb) bt #0 clang::Scope::getEntity (this=0x0) at /home/peter/Programming/llvm/llvm/tools/clang/include/clang/Sema/Scope.h:310 #1 0x00007ffff535cdaf in clang::Sema::PushOnScopeChains (this=0x6b6e00, D=0x38392c8, S=0x0, AddToContext=true) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaDecl.cpp:1190 #2 0x00007ffff535ef9a in clang::Sema::LazilyCreateBuiltin (this=0x6b6e00, II=0x69f638, ID=370, S=0x0, ForRedeclaration=false, Loc=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaDecl.cpp:1778 #3 0x00007ffff572247e in LookupBuiltin (S=…, R=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaLookup.cpp:544 #4 0x00007ffff5722240 in clang::Sema::LookupName (this=0x6b6e00, R=…, S=0x0, AllowBuiltinCreation=true) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaLookup.cpp:1426 #5 0x00007ffff572362c in clang::Sema::LookupParsedName (this=0x6b6e00, R=…, S=0x0, SS=0x7ffffffea958, AllowBuiltinCreation=true, EnteringContext=false) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaLookup.cpp:1868 #6 0x00007ffff5524491 in clang::Sema::ActOnIdExpression (this=0x6b6e00, S=0x0, SS=…, TemplateKWLoc=…, Id=…, HasTrailingLParen=true, IsAddressOfOperand=false, CCC=std::unique_ptrclang::CorrectionCandidateCallback containing 0x0, IsInlineAsmIdentifier=false, KeywordReplacement=0x0) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaExpr.cpp:2114 #7 0x00007ffff5523d96 in clang::Sema::DefaultVariadicArgumentPromotion (this=0x6b6e00, E=0x38383f0, CT=clang::Sema::VariadicConstructor, FDecl=0x878b20) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaExpr.cpp:936 #8 0x00007ffff55367c6 in clang::Sema::GatherArgumentsForCall (this=0x6b6e00, CallLoc=…, FDecl=0x878b20, Proto=0x878ad0, FirstParam=0, Args=…, AllArgs=…, CallType=clang::Sema::VariadicConstructor, AllowExplicit=false, IsListInitialization=false) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaExpr.cpp:4477 #9 0x00007ffff5426487 in clang::Sema::CompleteConstructorCall (this=0x6b6e00, Constructor=0x878b20, ArgsPtr=…, Loc=…, ConvertedArgs=…, AllowExplicit=false, IsListInitialization=false) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaDeclCXX.cpp:11427 #10 0x00007ffff56f4184 in clang::InitializationSequence::Perform (this=0x7ffffffebff0, S=…, Entity=…, Kind=…, Args=…, ResultType=0x0) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaInit.cpp:6189 #11 0x00007ffff56fe47b in clang::Sema::PerformCopyInitialization (this=0x6b6e00, Entity=…, EqualLoc=…, Init=…, TopLevelOfInitList=false, AllowExplicit=false) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaInit.cpp:7495 #12 0x00007ffff55364e9 in clang::Sema::GatherArgumentsForCall (this=0x6b6e00, CallLoc=…, FDecl=0x3839080, Proto=0x3839010, FirstParam=0, Args=…, AllArgs=…, CallType=clang::Sema::VariadicDoesNotApply, AllowExplicit=false, IsListInitialization=false) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaExpr.cpp:4433 #13 0x00007ffff5535c19 in clang::Sema::ConvertArgumentsForCall (this=0x6b6e00, Call=0x3839240, Fn=0x3839228, FDecl=0x3839080, Proto=0x3839010, Args=…, RParenLoc=…, IsExecConfig=false) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaExpr.cpp:4378 #14 0x00007ffff5538209 in clang::Sema::BuildResolvedCallExpr (this=0x6b6e00, Fn=0x3839228, NDecl=0x3839080, LParenLoc=…, Args=…, RParenLoc=…, Config=0x0, IsExecConfig=false) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaExpr.cpp:4964 #15 0x00007ffff57b4ad2 in FinishOverloadedCallExpr (SemaRef=…, S=0x0, Fn=0x3839190, ULE=0x3837ab8, LParenLoc=…, Args=…, RParenLoc=…, ExecConfig=0x0, CandidateSet=0x7ffffffee548, Best=0x7ffffffee510, OverloadResult=clang::OR_Success, AllowTypoCorrection=true) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaOverload.cpp:10798 #16 0x00007ffff57b47cd in clang::Sema::BuildOverloadedCallExpr (this=0x6b6e00, S=0x0, Fn=0x3837ab8, ULE=0x3837ab8, LParenLoc=…, Args=…, RParenLoc=…, ExecConfig=0x0, AllowTypoCorrection=true) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaOverload.cpp:10871 #17 0x00007ffff5525999 in clang::Sema::ActOnCallExpr (this=0x6b6e00, S=0x0, Fn=0x3837ab8, LParenLoc=…, ArgExprs=…, RParenLoc=…, ExecConfig=0x0, IsExecConfig=false) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaExpr.cpp:4768 #18 0x00007ffff5975fee in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::RebuildCallExpr (this=0x7fffffff2738, Callee=0x3837ab8, LParenLoc=…, Args=…, RParenLoc=…, ExecConfig=0x0) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:1833 #19 0x00007ffff5975f26 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformCallExpr (this=0x7fffffff2738, E=0x8d8a08) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:7617 #20 0x00007ffff5967982 in (anonymous namespace)::TemplateInstantiator::TransformCallExpr (this=0x7fffffff2738, CE=0x8d8a08) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaTemplateInstantiate.cpp:823 #21 0x00007ffff595e10a in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformExpr (this=0x7fffffff2738, E=0x8d8a08) at tools/clang/include/clang/AST/StmtNodes.inc:299 #22 0x00007ffff597fbb2 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformDecltypeType (this=0x7fffffff2738, TLB=…, TL=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:4922 #23 0x00007ffff5954aa6 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformType (this=0x7fffffff2738, TLB=…, T=…) at /home/peter/Programming/llvm/llvm/tools/clang/include/clang/AST/TypeNodes.def:88 #24 0x00007ffff59539bf in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformType (this=0x7fffffff2738, DI=0x8d8aa8) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:3706 #25 0x00007ffff597404d in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformTemplateArgument (this=0x7fffffff2738, Input=…, Output=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:3441 #26 0x00007ffff597332a in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformTemplateArguments<clang::TemplateArgumentLocContainerIteratorclang::TemplateSpecializationTypeLoc > (this=0x7fffffff2738, First=…, Last=…, Outputs=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:3661 #27 0x00007ffff5971f79 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformTemplateSpecializationType (this=0x7fffffff2738, TLB=…, TL=…, Template=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:5213 #28 0x00007ffff5971c34 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformTSIInObjectScope (this=0x7fffffff2738, TL=…, ObjectType=…, UnqualLookup=0x0, SS=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:3854 #29 0x00007ffff5990278 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformTypeInObjectScope (this=0x7fffffff2738, TL=…, ObjectType=…, UnqualLookup=0x0, SS=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:3813 #30 0x00007ffff59604c8 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformNestedNameSpecifierLoc (this=0x7fffffff2738, NNS=…, ObjectType=…, FirstQualifierInScope=0x0) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:3189 #31 0x00007ffff59817be in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformDependentNameType (this=0x7fffffff2738, TLB=…, TL=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:5434 #32 0x00007ffff5954f1a in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformType (this=0x7fffffff2738, TLB=…, T=…) at /home/peter/Programming/llvm/llvm/tools/clang/include/clang/AST/TypeNodes.def:101 #33 0x00007ffff59539bf in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformType (this=0x7fffffff2738, DI=0x8d8cc0) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:3706 #34 0x00007ffff59537ff in clang::Sema::SubstType (this=0x6b6e00, T=0x8d8cc0, Args=…, Loc=…, Entity=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaTemplateInstantiate.cpp:1472 #35 0x00007ffff59581e7 in clang::Sema::SubstBaseSpecifiers (this=0x6b6e00, Instantiation=0x3837850, Pattern=0x8d8440, TemplateArgs=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaTemplateInstantiate.cpp:1810 #36 0x00007ffff5958675 in clang::Sema::InstantiateClass (this=0x6b6e00, PointOfInstantiation=…, Instantiation=0x3837850, Pattern=0x8d8440, TemplateArgs=…, TSK=clang::TSK_ImplicitInstantiation, Complain=true) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaTemplateInstantiate.cpp:1971 #37 0x00007ffff595a361 in clang::Sema::InstantiateClassTemplateSpecialization (this=0x6b6e00, PointOfInstantiation=…, ClassTemplateSpec=0x3837850, TSK=clang::TSK_ImplicitInstantiation, Complain=true) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaTemplateInstantiate.cpp:2427 #38 0x00007ffff5a157c1 in clang::Sema::RequireCompleteTypeImpl (this=0x6b6e00, Loc=…, T=…, Diagnoser=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaType.cpp:5290 #39 0x00007ffff5a150f6 in clang::Sema::RequireCompleteType (this=0x6b6e00, Loc=…, T=…, Diagnoser=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaType.cpp:5117 #40 0x00007ffff52aa25a in clang::Sema::RequireCompleteTypeclang::SourceRange (this=0x6b6e00, Loc=…, T=…, DiagID=2256, Args=…) at /home/peter/Programming/llvm/llvm/tools/clang/include/clang/Sema/Sema.h:1303 #41 0x00007ffff53ff108 in clang::Sema::CheckBaseSpecifier (this=0x6b6e00, Class=0x38362d8, SpecifierRange=…, Virtual=false, Access=clang::AS_public, TInfo=0x38379e0, EllipsisLoc=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaDeclCXX.cpp:1454 #42 0x00007ffff595828a in clang::Sema::SubstBaseSpecifiers (this=0x6b6e00, Instantiation=0x38362d8, Pattern=0x8d94d8, TemplateArgs=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaTemplateInstantiate.cpp:1822 #43 0x00007ffff5958675 in clang::Sema::InstantiateClass (this=0x6b6e00, PointOfInstantiation=…, Instantiation=0x38362d8, Pattern=0x8d94d8, TemplateArgs=…, TSK=clang::TSK_ImplicitInstantiation, Complain=true) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaTemplateInstantiate.cpp:1971 #44 0x00007ffff595a361 in clang::Sema::InstantiateClassTemplateSpecialization (this=0x6b6e00, PointOfInstantiation=…, ClassTemplateSpec=0x38362d8, TSK=clang::TSK_ImplicitInstantiation, Complain=true) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaTemplateInstantiate.cpp:2427 #45 0x00007ffff5a157c1 in clang::Sema::RequireCompleteTypeImpl (this=0x6b6e00, Loc=…, T=…, Diagnoser=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaType.cpp:5290 #46 0x00007ffff5a150f6 in clang::Sema::RequireCompleteType (this=0x6b6e00, Loc=…, T=…, Diagnoser=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaType.cpp:5117 #47 0x00007ffff52aa25a in clang::Sema::RequireCompleteTypeclang::SourceRange (this=0x6b6e00, Loc=…, T=…, DiagID=2259, Args=…) at /home/peter/Programming/llvm/llvm/tools/clang/include/clang/Sema/Sema.h:1303 #48 0x00007ffff52a5c38 in clang::Sema::RequireCompleteDeclContext (this=0x6b6e00, SS=…, DC=0x3836310) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaCXXScopeSpec.cpp:212 #49 0x00007ffff552c8ec in clang::Sema::BuildQualifiedDeclarationNameExpr (this=0x6b6e00, SS=…, NameInfo=…, IsAddressOfOperand=false, RecoveryTSI=0x0) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaExpr.cpp:2294 #50 0x00007ffff5976d14 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::RebuildDependentScopeDeclRefExpr (this=0x7fffffff75b8, QualifierLoc=…, TemplateKWLoc=…, NameInfo=…, TemplateArgs=0x0, IsAddressOfOperand=false, RecoveryTSI=0x0) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:2402 #51 0x00007ffff5976ac0 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformDependentScopeDeclRefExpr (this=0x7fffffff75b8, E=0x7d1a50, IsAddressOfOperand=false, RecoveryTSI=0x0) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:8997 #52 0x00007ffff5969433 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformDependentScopeDeclRefExpr (this=0x7fffffff75b8, E=0x7d1a50) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:8963 #53 0x00007ffff595e47c in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformExpr (this=0x7fffffff75b8, E=0x7d1a50) at tools/clang/include/clang/AST/StmtNodes.inc:433 #54 0x00007ffff59743e4 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformTemplateArgument (this=0x7fffffff75b8, Input=…, Output=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:3480 #55 0x00007ffff597332a in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformTemplateArguments<clang::TemplateArgumentLocContainerIteratorclang::TemplateSpecializationTypeLoc > (this=0x7fffffff75b8, First=…, Last=…, Outputs=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:3661 #56 0x00007ffff5971f79 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformTemplateSpecializationType (this=0x7fffffff75b8, TLB=…, TL=…, Template=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:5213 #57 0x00007ffff598143b in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformTemplateSpecializationType (this=0x7fffffff75b8, TLB=…, TL=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:5112 #58 0x00007ffff5954dfd in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformType (this=0x7fffffff75b8, TLB=…, T=…) at /home/peter/Programming/llvm/llvm/tools/clang/include/clang/AST/TypeNodes.def:98 #59 0x00007ffff59539bf in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformType (this=0x7fffffff75b8, DI=0x7d1c48) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:3706 #60 0x00007ffff59537ff in clang::Sema::SubstType (this=0x6b6e00, T=0x7d1c48, Args=…, Loc=…, Entity=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaTemplateInstantiate.cpp:1472 #61 0x00007ffff59581e7 in clang::Sema::SubstBaseSpecifiers (this=0x6b6e00, Instantiation=0x3836030, Pattern=0x7d1578, TemplateArgs=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaTemplateInstantiate.cpp:1810 #62 0x00007ffff5958675 in clang::Sema::InstantiateClass (this=0x6b6e00, PointOfInstantiation=…, Instantiation=0x3836030, Pattern=0x7d1578, TemplateArgs=…, TSK=clang::TSK_ImplicitInstantiation, Complain=true) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaTemplateInstantiate.cpp:1971 #63 0x00007ffff595a361 in clang::Sema::InstantiateClassTemplateSpecialization (this=0x6b6e00, PointOfInstantiation=…, ClassTemplateSpec=0x3836030, TSK=clang::TSK_ImplicitInstantiation, Complain=true) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaTemplateInstantiate.cpp:2427 #64 0x00007ffff5a157c1 in clang::Sema::RequireCompleteTypeImpl (this=0x6b6e00, Loc=…, T=…, Diagnoser=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaType.cpp:5290 #65 0x00007ffff5a150f6 in clang::Sema::RequireCompleteType (this=0x6b6e00, Loc=…, T=…, Diagnoser=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaType.cpp:5117 #66 0x00007ffff52aa25a in clang::Sema::RequireCompleteTypeclang::SourceRange (this=0x6b6e00, Loc=…, T=…, DiagID=2256, Args=…) at /home/peter/Programming/llvm/llvm/tools/clang/include/clang/Sema/Sema.h:1303 #67 0x00007ffff53ff108 in clang::Sema::CheckBaseSpecifier (this=0x6b6e00, Class=0x3835810, SpecifierRange=…, Virtual=false, Access=clang::AS_public, TInfo=0x38361b0, EllipsisLoc=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaDeclCXX.cpp:1454 #68 0x00007ffff595828a in clang::Sema::SubstBaseSpecifiers (this=0x6b6e00, Instantiation=0x3835810, Pattern=0x7d1e20, TemplateArgs=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaTemplateInstantiate.cpp:1822 #69 0x00007ffff5958675 in clang::Sema::InstantiateClass (this=0x6b6e00, PointOfInstantiation=…, Instantiation=0x3835810, Pattern=0x7d1e20, TemplateArgs=…, TSK=clang::TSK_ImplicitInstantiation, Complain=true) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaTemplateInstantiate.cpp:1971 #70 0x00007ffff595a361 in clang::Sema::InstantiateClassTemplateSpecialization (this=0x6b6e00, PointOfInstantiation=…, ClassTemplateSpec=0x3835810, TSK=clang::TSK_ImplicitInstantiation, Complain=true) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaTemplateInstantiate.cpp:2427 #71 0x00007ffff5a157c1 in clang::Sema::RequireCompleteTypeImpl (this=0x6b6e00, Loc=…, T=…, Diagnoser=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaType.cpp:5290 #72 0x00007ffff5a150f6 in clang::Sema::RequireCompleteType (this=0x6b6e00, Loc=…, T=…, Diagnoser=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaType.cpp:5117 #73 0x00007ffff52aa25a in clang::Sema::RequireCompleteTypeclang::SourceRange (this=0x6b6e00, Loc=…, T=…, DiagID=2259, Args=…) at /home/peter/Programming/llvm/llvm/tools/clang/include/clang/Sema/Sema.h:1303 #74 0x00007ffff52a5c38 in clang::Sema::RequireCompleteDeclContext (this=0x6b6e00, SS=…, DC=0x3835848) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaCXXScopeSpec.cpp:212 #75 0x00007ffff552c8ec in clang::Sema::BuildQualifiedDeclarationNameExpr (this=0x6b6e00, SS=…, NameInfo=…, IsAddressOfOperand=false, RecoveryTSI=0x0) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaExpr.cpp:2294 #76 0x00007ffff5976d14 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::RebuildDependentScopeDeclRefExpr (this=0x7fffffffbac8, QualifierLoc=…, TemplateKWLoc=…, NameInfo=…, TemplateArgs=0x0, IsAddressOfOperand=false, RecoveryTSI=0x0) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:2402 #77 0x00007ffff5976ac0 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformDependentScopeDeclRefExpr (this=0x7fffffffbac8, E=0x9a8628, IsAddressOfOperand=false, RecoveryTSI=0x0) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:8997 #78 0x00007ffff5969433 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformDependentScopeDeclRefExpr (this=0x7fffffffbac8, E=0x9a8628) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:8963 #79 0x00007ffff595e47c in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformExpr (this=0x7fffffffbac8, E=0x9a8628) at tools/clang/include/clang/AST/StmtNodes.inc:433 #80 0x00007ffff596339c in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformBinaryOperator (this=0x7fffffffbac8, E=0x9a8818) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:7704 #81 0x00007ffff595dca3 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformExpr (this=0x7fffffffbac8, E=0x9a8818) at tools/clang/include/clang/AST/StmtNodes.inc:157 #82 0x00007ffff5955af2 in clang::TreeTransform<(anonymous namespace)::TemplateInstantiator>::TransformExceptionSpec (this=0x7fffffffbac8, Loc=…, ESI=…, Exceptions=…, Changed=@0x7fffffffba7f: false) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/TreeTransform.h:4697 #83 0x00007ffff59559ea in clang::Sema::SubstExceptionSpec (this=0x6b6e00, New=0x7ffff0b023a8, Proto=0x9a8840, Args=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaTemplateInstantiate.cpp:1611 #84 0x00007ffff59aca2b in clang::Sema::InstantiateExceptionSpec (this=0x6b6e00, PointOfInstantiation=…, Decl=0x7ffff0b023a8) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp:3193 #85 0x00007ffff5518b88 in clang::Sema::ResolveExceptionSpec (this=0x6b6e00, Loc=…, FPT=0x7ffff0b02470) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaExceptionSpec.cpp:162 #86 0x00007ffff53839f5 in clang::Sema::ActOnStartOfFunctionDef (this=0x6b6e00, FnBodyScope=0x0, D=0x7ffff0b023a8) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaDecl.cpp:10475 #87 0x00007ffff59ad779 in clang::Sema::InstantiateFunctionDefinition (this=0x6b6e00, PointOfInstantiation=…, Function=0x7ffff0b023a8, Recursive=true, DefinitionRequired=true) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp:3449 #88 0x0000000000428f79 in QuaffVisitor::CanBeSafeToHandle (this=0x694b30, d=0x7ffff0b023a8, inInstantiation=true) at /home/peter/Programming/llvm/llvm/tools/clang/tools/extra/quaff/quaff-visitor.cc:179 #89 0x000000000042a46f in QuaffVisitor::VisitCXXMethod (this=0x694b30, d=0x7ffff0b023a8, instantiation=true) at /home/peter/Programming/llvm/llvm/tools/clang/tools/extra/quaff/quaff-visitor.cc:388 #90 0x000000000040dd50 in Walker::Traverse (this=0x694c40, d=0x7ffff0b023a8) at /home/peter/Programming/llvm/llvm/tools/clang/tools/extra/quaff/walker.cc:64 #91 0x000000000040e28d in Walker::TraverseCXXRecord (this=0x694c40, d=0x7ffff0bf1218) at /home/peter/Programming/llvm/llvm/tools/clang/tools/extra/quaff/walker.cc:96 #92 0x000000000040df33 in Walker::TraverseClassTemplate (this=0x694c40, d=0x96d750) at /home/peter/Programming/llvm/llvm/tools/clang/tools/extra/quaff/walker.cc:109 #93 0x000000000040dbce in Walker::Traverse (this=0x694c40, d=0x96d750) at /home/peter/Programming/llvm/llvm/tools/clang/tools/extra/quaff/walker.cc:45 #94 0x000000000040e0b2 in Walker::TraverseNamespace (this=0x694c40, d=0x95ea20) at /home/peter/Programming/llvm/llvm/tools/clang/tools/extra/quaff/walker.cc:119 #95 0x000000000040dc25 in Walker::Traverse (this=0x694c40, d=0x95ea20) at /home/peter/Programming/llvm/llvm/tools/clang/tools/extra/quaff/walker.cc:51 #96 0x000000000040e0b2 in Walker::TraverseNamespace (this=0x694c40, d=0x95e9b8) at /home/peter/Programming/llvm/llvm/tools/clang/tools/extra/quaff/walker.cc:119 #97 0x000000000040dc25 in Walker::Traverse (this=0x694c40, d=0x95e9b8) at /home/peter/Programming/llvm/llvm/tools/clang/tools/extra/quaff/walker.cc:51 #98 0x000000000040d8d2 in Walker::TraverseTranslationUnit (this=0x694c40, d=0x693bd0) at /home/peter/Programming/llvm/llvm/tools/clang/tools/extra/quaff/walker.cc:17 #99 0x000000000040d054 in Consumer<Options, QuaffVisitor>::HandleTranslationUnit (this=0x694b20, context=…) at /home/peter/Programming/llvm/llvm/tools/clang/tools/extra/quaff/consumer.hh:21 #100 0x00007ffff1f9dab6 in clang::ParseAST (S=…, PrintStats=false, SkipFunctionBodies=false) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Parse/ParseAST.cpp:151 #101 0x00007ffff610824f in clang::ASTFrontendAction::ExecuteAction (this=0x660f50) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Frontend/FrontendAction.cpp:536 #102 0x00007ffff6107d50 in clang::FrontendAction::Execute (this=0x660f50) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Frontend/FrontendAction.cpp:439 #103 0x00007ffff60b4f5f in clang::CompilerInstance::ExecuteAction (this=0x7fffffffd368, Act=…) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Frontend/CompilerInstance.cpp:814 #104 0x00007ffff77bca6e in clang::tooling::FrontendActionFactory::runInvocation (this=0x7fffffffde88, Invocation=0x6612a0, Files=0x65f4f0, DiagConsumer=0x0) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Tooling/Tooling.cpp:272 #105 0x00007ffff77bc979 in clang::tooling::ToolInvocation::runInvocation (this=0x7fffffffdb80, BinaryName=0x65fec8 “/home/peter/Programming/llvm/clang/bin/quaff”, Compilation=0x65aae0, Invocation=0x6612a0) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Tooling/Tooling.cpp:249 #106 0x00007ffff77bc211 in clang::tooling::ToolInvocation::run (this=0x7fffffffdb80) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Tooling/Tooling.cpp:235 #107 0x00007ffff77bd3aa in clang::tooling::ClangTool::run (this=0x7fffffffdea8, Action=0x7fffffffde88) at /home/peter/Programming/llvm/llvm/tools/clang/lib/Tooling/Tooling.cpp:364 #108 0x000000000040873d in main (argc=5, argv=0x7fffffffe178) at /home/peter/Programming/llvm/llvm/tools/clang/tools/extra/quaff/main.cc:149

I forgot to mention that I'm also spamming each test as it's performed, the last few lines of which are:

std::__1::__tuple_like<std::__1::pair<TagLib::String, TagLib::StringList> const>::operator =
std::__1::pair<unsigned long, unsigned long>::operator =
std::__1::pair<unsigned long, unsigned long>::swap
std::__1::pair<TagLib::String const, TagLib::StringList>::operator =

So it appears to be crashing on the first instantiation of std::pair::operator= with a const type

Trying this again...

Using this code:

   if(inInstantiation) {
     std::cerr << wrapGreen("testing ", colour) << declToString(d) << "(";
     bool seen = false;
     for(auto param : d->params()) {
       if(seen) {
         std::cerr << ", ";
       }
       seen = true;
       std::cerr << wrapGreen(typeToString(param->getType()), colour);
     }
     std::cerr << ")\n";
     clang::Sema::SFINAETrap trap(sema);
sema.InstantiateFunctionDefinition(d->getParent()->getLocation(), d, true, true);
     if(trap.hasErrorOccurred()) {
       std::cerr << wrapRed("test failed\n", colour);
       return false;
     }
     else {
       std::cerr << wrapGreen("test passed\n", colour);
     }
   }

I was hoping to find out if there were instantiation errors with methods. However on my test TU it crashes with the backtrace in my previous email. In a simpler TU it doesn't crash, but it also doesn't work! Here is the last page of its output:

testing std::__1::pair<int const, int const>::swap(std::__1::pair<int const, int const>&)
In file included from /home/peter/Programming/llvm/extra-dir/quaff/test.cc:1:
In file included from /home/peter/Programming/llvm/ninja/bin/../include/c++/v1/utility:157:
In file included from /home/peter/Programming/llvm/ninja/bin/../include/c++/v1/__tuple:16:
/home/peter/Programming/llvm/ninja/bin/../include/c++/v1/type_traits:3592:38: error: no matching function for call to 'swap'
_NOEXCEPT_(_NOEXCEPT_(swap(*_VSTD::declval<_ForwardIterator1>(),
                                      ^~~~
/home/peter/Programming/llvm/ninja/bin/../include/c++/v1/__config:362:34: note: expanded from macro '_NOEXCEPT_'
# define _NOEXCEPT_(x) noexcept(x)
                                  ^
/home/peter/Programming/llvm/ninja/bin/../include/c++/v1/__config:362:34: note: expanded from macro '_NOEXCEPT_'
# define _NOEXCEPT_(x) noexcept(x)
                                  ^
/home/peter/Programming/llvm/ninja/bin/../include/c++/v1/utility:391:16: note: in instantiation of exception specification for 'iter_swap<const int *, const int *>' requested here
         _VSTD::iter_swap(&first, &__p.first);
                ^
/home/peter/Programming/llvm/ninja/bin/../include/c++/v1/utility:253:30: note: in instantiation of member function 'std::__1::pair<const int, const int>::swap' requested here
struct _LIBCPP_TYPE_VIS_ONLY pair
                              ^
/home/peter/Programming/llvm/ninja/bin/../include/c++/v1/type_traits:3573:5: note: candidate template ignored: disabled by 'enable_if' [with _Tp = const int]
     is_move_constructible<_Tp>::value &&
     ^
/home/peter/Programming/llvm/ninja/bin/../include/c++/v1/type_traits:3595:5: error: no matching function for call to 'swap'
     swap(*__a, *__b);
     ^~~~
/home/peter/Programming/llvm/ninja/bin/../include/c++/v1/utility:391:16: note: in instantiation of function template specialization 'std::__1::iter_swap<const int *, const int *>' requested here
         _VSTD::iter_swap(&first, &__p.first);
                ^
/home/peter/Programming/llvm/ninja/bin/../include/c++/v1/utility:253:30: note: in instantiation of member function 'std::__1::pair<const int, const int>::swap' requested here
struct _LIBCPP_TYPE_VIS_ONLY pair
                              ^
/home/peter/Programming/llvm/ninja/bin/../include/c++/v1/type_traits:3573:5: note: candidate template ignored: disabled by 'enable_if' [with _Tp = const int]
     is_move_constructible<_Tp>::value &&
     ^
test passed
testing std::__1::pair<int const, int const>::~pair()
test passed
testing std::__1::pair<int const, int const>::~pair()
test passed
Handled 517 functions
Handled 120 functions that didn't require a wrapper
Skipped 264 functions due to unsafe types
Excluded 2 functions, due to function or type name blacklist
Skipped 4 deleted functions
Handled 429 instantiated template methods
sending output to /home/peter/Programming/llvm/extra-dir/quaff/test.cc.json
Saw 281 type(s) and 364 qual-type(s)
6 errors generated.
Error while processing /home/peter/Programming/llvm/extra-dir/quaff/test.cc.

So, the SFINAETrap is returning false from hasErrorOccurred() even though, clearly, errors are happening. How am I supposed to use it?

I've done a bit of debugging to find the source of the crash:

Sema::LazilyCreateBuiltin() calls Sema::PushOnScopeChains() passing Sema.TUScope, and PushOnScopeChains() doesn't test for nullptr.

It appears that TUScope is always null (or at least by the time of the first check for error-free instantiation it is null). Is this expected?

I've done a bit of debugging to find the source of the crash:

Sema::LazilyCreateBuiltin() calls Sema::PushOnScopeChains() passing
Sema.TUScope, and PushOnScopeChains() doesn't test for nullptr.

It appears that TUScope is always null (or at least by the time of the
first check for error-free instantiation it is null). Is this expected?

Yes, the TUScope only lives until the end of the TU. If you want to do more
template instantiation after parsing, you can call
Preprocessor::enableIncrementalProcessing() to let Clang know that the end
of the main source file isn't the end of the TU.

Thanks! Adding a call to that from the constructor of my ASTConsumer stops the crash.

Now for my next mole!

Calling InstantiateFunction() is triggering diagnostics (irksome) and (it appears) causes the tool to return a non-zero exit status (which is more problematic for my use case).

I tried creating a SFINAETrap on the stack, but it appears to do nothing, I assume that this is because I somehow have to tell my Sema that it shouldn't emit errors? I've also tried using a DiagnosticErrorTrap, and that does notice the errors, but it also doesn't prevent the diagnostics being output, or the exit status.

It feels like I'm getting closer. :slight_smile:

Thanks! Adding a call to that from the constructor of my ASTConsumer
stops the crash.

Now for my next mole!

Calling InstantiateFunction() is triggering diagnostics (irksome) and (it
appears) causes the tool to return a non-zero exit status (which is more
problematic for my use case).

I tried creating a SFINAETrap on the stack, but it appears to do nothing,
I assume that this is because I somehow have to tell my Sema that it
shouldn't emit errors? I've also tried using a DiagnosticErrorTrap, and
that does notice the errors, but it also doesn't prevent the diagnostics
being output, or the exit status.

This is probably because the diagnostics are not in the "immediate context"
of the substitution (that is, they're not diagnostics that a SFINAE check
can detect, they're hard errors). We don't have a way to suppress those
diagnostics and return to a state as if the instantiation never happened
(the C++ language doesn't require such a mechanism). After such a
diagnostic occurs, parts of your AST might be marked as "invalid", and we
might suppress further diagnostics (possibly resulting in a later
instantiation succeeding when it would have failed if run from a clean
state). If you're OK with that, I think there's a flag on the
DiagnosticsEngine to suppress *all* diagnostics.