diff options
author | Younan Zhang <zyn7109@gmail.com> | 2024-04-05 11:09:33 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-05 11:09:33 +0800 |
commit | 843cc474faefad1d639f4c44c1cf3ad7dbda76c8 (patch) | |
tree | 3ba3400da0d4d3da08b169f457c10e19f085e9c0 | |
parent | ff1e72d68d1224271801ff5192a8c14fbd3be83b (diff) | |
download | llvm-843cc474faefad1d639f4c44c1cf3ad7dbda76c8.zip llvm-843cc474faefad1d639f4c44c1cf3ad7dbda76c8.tar.gz llvm-843cc474faefad1d639f4c44c1cf3ad7dbda76c8.tar.bz2 |
[Clang][Sema] Fix the lambda call expression inside of a type alias declaration (#82310)
This patch attempts to fix the lambda call expression inside of a type
alias declaration from two aspects:
1. Defer the lambda call expression building until after we have
sufficient template arguments. This avoids the overeager (and often
wrong) semantic checking before the type alias instantiation.
2. Properly obtain template arguments involving a template type alias
for constraint checking.
It is unfortunate that a `TypeAliasTemplateDecl` (or a `TypeAliasDecl`)
is never a `DeclContext`, nor does it have an associated specialization
Decl from which we could collect these template arguments. Thus, I added
a new CodeSynthesisContext to record template arguments for alias
declarations.
Fixes https://github.com/llvm/llvm-project/issues/70601
Fixes https://github.com/llvm/llvm-project/issues/76674
Fixes https://github.com/llvm/llvm-project/issues/79555
Fixes https://github.com/llvm/llvm-project/issues/81145
Fixes https://github.com/llvm/llvm-project/issues/82104
Note that this doesn't involve the fix for
https://github.com/llvm/llvm-project/issues/28461. That seems different,
and I'd like to leave it as a follow-up.
-rw-r--r-- | clang/docs/ReleaseNotes.rst | 2 | ||||
-rw-r--r-- | clang/include/clang/AST/DeclCXX.h | 4 | ||||
-rw-r--r-- | clang/include/clang/Sema/Sema.h | 9 | ||||
-rw-r--r-- | clang/lib/Frontend/FrontendActions.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Sema/SemaConcept.cpp | 10 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplate.cpp | 5 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiate.cpp | 160 | ||||
-rw-r--r-- | clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 7 | ||||
-rw-r--r-- | clang/lib/Sema/TreeTransform.h | 46 | ||||
-rw-r--r-- | clang/test/SemaTemplate/alias-template-with-lambdas.cpp | 105 |
10 files changed, 338 insertions, 12 deletions
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 4e87bd0..b11a811 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -480,6 +480,8 @@ Bug Fixes to C++ Support when one of the function had more specialized templates. Fixes (`#82509 <https://github.com/llvm/llvm-project/issues/82509>`_) and (`#74494 <https://github.com/llvm/llvm-project/issues/74494>`_) +- Clang now supports direct lambda calls inside of a type alias template declarations. + This addresses (#GH70601), (#GH76674), (#GH79555), (#GH81145) and (#GH82104). - Allow access to a public template alias declaration that refers to friend's private nested type. (#GH25708). - Fixed a crash in constant evaluation when trying to access a diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h index 9cebaff..7aed4d5 100644 --- a/clang/include/clang/AST/DeclCXX.h +++ b/clang/include/clang/AST/DeclCXX.h @@ -1869,6 +1869,10 @@ public: DL.MethodTyInfo = TS; } + void setLambdaDependencyKind(unsigned Kind) { + getLambdaData().DependencyKind = Kind; + } + void setLambdaIsGeneric(bool IsGeneric) { assert(DefinitionData && DefinitionData->IsLambda && "setting lambda property of non-lambda class"); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 8c98d8c..149b268 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -10170,6 +10170,9 @@ public: /// We are building deduction guides for a class. BuildingDeductionGuides, + + /// We are instantiating a type alias template declaration. + TypeAliasTemplateInstantiation, } Kind; /// Was the enclosing context a non-instantiation SFINAE context? @@ -10259,6 +10262,12 @@ public: FunctionDecl *Entity, ExceptionSpecification, SourceRange InstantiationRange = SourceRange()); + /// Note that we are instantiating a type alias template declaration. + InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, + TypeAliasTemplateDecl *Template, + ArrayRef<TemplateArgument> TemplateArgs, + SourceRange InstantiationRange = SourceRange()); + /// Note that we are instantiating a default argument in a /// template-id. InstantiatingTemplate(Sema &SemaRef, SourceLocation PointOfInstantiation, diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp index 0bc26b6..7ee6ccf 100644 --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -453,6 +453,8 @@ private: return "BuildingBuiltinDumpStructCall"; case CodeSynthesisContext::BuildingDeductionGuides: return "BuildingDeductionGuides"; + case Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation: + return "TypeAliasTemplateInstantiation"; } return ""; } diff --git a/clang/lib/Sema/SemaConcept.cpp b/clang/lib/Sema/SemaConcept.cpp index b2986c5..e00c972 100644 --- a/clang/lib/Sema/SemaConcept.cpp +++ b/clang/lib/Sema/SemaConcept.cpp @@ -615,10 +615,12 @@ bool Sema::SetupConstraintScope( // reference the original primary template. // We walk up the instantiated template chain so that nested lambdas get // handled properly. - for (FunctionTemplateDecl *FromMemTempl = - PrimaryTemplate->getInstantiatedFromMemberTemplate(); - FromMemTempl; - FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate()) { + // We should only collect instantiated parameters from the primary template. + // Otherwise, we may have mismatched template parameter depth! + if (FunctionTemplateDecl *FromMemTempl = + PrimaryTemplate->getInstantiatedFromMemberTemplate()) { + while (FromMemTempl->getInstantiatedFromMemberTemplate()) + FromMemTempl = FromMemTempl->getInstantiatedFromMemberTemplate(); if (addInstantiatedParametersToScope(FD, FromMemTempl->getTemplatedDecl(), Scope, MLTAL)) return true; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index d3def13..0b75f4f 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4383,6 +4383,11 @@ QualType Sema::CheckTemplateIdType(TemplateName Name, if (Inst.isInvalid()) return QualType(); + InstantiatingTemplate InstTemplate( + *this, /*PointOfInstantiation=*/AliasTemplate->getBeginLoc(), + /*Template=*/AliasTemplate, + /*TemplateArgs=*/TemplateArgLists.getInnermost()); + std::optional<ContextRAII> SavedContext; if (!AliasTemplate->getDeclContext()->isFileContext()) SavedContext.emplace(*this, AliasTemplate->getDeclContext()); diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 5e05cb4..d7b7291 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -80,6 +80,81 @@ struct Response { return R; } }; + +// Retrieve the primary template for a lambda call operator. It's +// unfortunate that we only have the mappings of call operators rather +// than lambda classes. +const FunctionDecl * +getPrimaryTemplateOfGenericLambda(const FunctionDecl *LambdaCallOperator) { + while (true) { + if (auto *FTD = dyn_cast_if_present<FunctionTemplateDecl>( + LambdaCallOperator->getDescribedTemplate()); + FTD && FTD->getInstantiatedFromMemberTemplate()) { + LambdaCallOperator = + FTD->getInstantiatedFromMemberTemplate()->getTemplatedDecl(); + } else if (auto *Prev = cast<CXXMethodDecl>(LambdaCallOperator) + ->getInstantiatedFromMemberFunction()) + LambdaCallOperator = Prev; + else + break; + } + return LambdaCallOperator; +} + +struct EnclosingTypeAliasTemplateDetails { + TypeAliasTemplateDecl *Template = nullptr; + TypeAliasTemplateDecl *PrimaryTypeAliasDecl = nullptr; + ArrayRef<TemplateArgument> AssociatedTemplateArguments; + + explicit operator bool() noexcept { return Template; } +}; + +// Find the enclosing type alias template Decl from CodeSynthesisContexts, as +// well as its primary template and instantiating template arguments. +EnclosingTypeAliasTemplateDetails +getEnclosingTypeAliasTemplateDecl(Sema &SemaRef) { + for (auto &CSC : llvm::reverse(SemaRef.CodeSynthesisContexts)) { + if (CSC.Kind != Sema::CodeSynthesisContext::SynthesisKind:: + TypeAliasTemplateInstantiation) + continue; + EnclosingTypeAliasTemplateDetails Result; + auto *TATD = cast<TypeAliasTemplateDecl>(CSC.Entity), + *Next = TATD->getInstantiatedFromMemberTemplate(); + Result = { + /*Template=*/TATD, + /*PrimaryTypeAliasDecl=*/TATD, + /*AssociatedTemplateArguments=*/CSC.template_arguments(), + }; + while (Next) { + Result.PrimaryTypeAliasDecl = Next; + Next = Next->getInstantiatedFromMemberTemplate(); + } + return Result; + } + return {}; +} + +// Check if we are currently inside of a lambda expression that is +// surrounded by a using alias declaration. e.g. +// template <class> using type = decltype([](auto) { ^ }()); +// By checking if: +// 1. The lambda expression and the using alias declaration share the +// same declaration context. +// 2. They have the same template depth. +// We have to do so since a TypeAliasTemplateDecl (or a TypeAliasDecl) is never +// a DeclContext, nor does it have an associated specialization Decl from which +// we could collect these template arguments. +bool isLambdaEnclosedByTypeAliasDecl( + const FunctionDecl *PrimaryLambdaCallOperator, + const TypeAliasTemplateDecl *PrimaryTypeAliasDecl) { + return cast<CXXRecordDecl>(PrimaryLambdaCallOperator->getDeclContext()) + ->getTemplateDepth() == + PrimaryTypeAliasDecl->getTemplateDepth() && + getLambdaAwareParentOfDeclContext( + const_cast<FunctionDecl *>(PrimaryLambdaCallOperator)) == + PrimaryTypeAliasDecl->getDeclContext(); +} + // Add template arguments from a variable template instantiation. Response HandleVarTemplateSpec(const VarTemplateSpecializationDecl *VarTemplSpec, @@ -176,7 +251,7 @@ HandleClassTemplateSpec(const ClassTemplateSpecializationDecl *ClassTemplSpec, return Response::UseNextDecl(ClassTemplSpec); } -Response HandleFunction(const FunctionDecl *Function, +Response HandleFunction(Sema &SemaRef, const FunctionDecl *Function, MultiLevelTemplateArgumentList &Result, const FunctionDecl *Pattern, bool RelativeToPrimary, bool ForConstraintInstantiation) { @@ -207,8 +282,23 @@ Response HandleFunction(const FunctionDecl *Function, // If this function is a generic lambda specialization, we are done. if (!ForConstraintInstantiation && - isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) + isGenericLambdaCallOperatorOrStaticInvokerSpecialization(Function)) { + // TypeAliasTemplateDecls should be taken into account, e.g. + // when we're deducing the return type of a lambda. + // + // template <class> int Value = 0; + // template <class T> + // using T = decltype([]<int U = 0>() { return Value<T>; }()); + // + if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef)) { + if (isLambdaEnclosedByTypeAliasDecl( + /*PrimaryLambdaCallOperator=*/getPrimaryTemplateOfGenericLambda( + Function), + /*PrimaryTypeAliasDecl=*/TypeAlias.PrimaryTypeAliasDecl)) + return Response::UseNextDecl(Function); + } return Response::Done(); + } } else if (Function->getDescribedFunctionTemplate()) { assert( @@ -283,7 +373,7 @@ Response HandleFunctionTemplateDecl(const FunctionTemplateDecl *FTD, return Response::ChangeDecl(FTD->getLexicalDeclContext()); } -Response HandleRecordDecl(const CXXRecordDecl *Rec, +Response HandleRecordDecl(Sema &SemaRef, const CXXRecordDecl *Rec, MultiLevelTemplateArgumentList &Result, ASTContext &Context, bool ForConstraintInstantiation) { @@ -312,11 +402,39 @@ Response HandleRecordDecl(const CXXRecordDecl *Rec, return Response::ChangeDecl(Rec->getLexicalDeclContext()); } - // This is to make sure we pick up the VarTemplateSpecializationDecl that this - // lambda is defined inside of. - if (Rec->isLambda()) + // This is to make sure we pick up the VarTemplateSpecializationDecl or the + // TypeAliasTemplateDecl that this lambda is defined inside of. + if (Rec->isLambda()) { if (const Decl *LCD = Rec->getLambdaContextDecl()) return Response::ChangeDecl(LCD); + // Retrieve the template arguments for a using alias declaration. + // This is necessary for constraint checking, since we always keep + // constraints relative to the primary template. + if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(SemaRef)) { + const FunctionDecl *PrimaryLambdaCallOperator = + getPrimaryTemplateOfGenericLambda(Rec->getLambdaCallOperator()); + if (isLambdaEnclosedByTypeAliasDecl(PrimaryLambdaCallOperator, + TypeAlias.PrimaryTypeAliasDecl)) { + Result.addOuterTemplateArguments(TypeAlias.Template, + TypeAlias.AssociatedTemplateArguments, + /*Final=*/false); + // Visit the parent of the current type alias declaration rather than + // the lambda thereof. + // E.g., in the following example: + // struct S { + // template <class> using T = decltype([]<Concept> {} ()); + // }; + // void foo() { + // S::T var; + // } + // The instantiated lambda expression (which we're visiting at 'var') + // has a function DeclContext 'foo' rather than the Record DeclContext + // S. This seems to be an oversight to me that we may want to set a + // Sema Context from the CXXScopeSpec before substituting into T. + return Response::ChangeDecl(TypeAlias.Template->getDeclContext()); + } + } + } return Response::UseNextDecl(Rec); } @@ -410,10 +528,11 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs( R = HandleClassTemplateSpec(ClassTemplSpec, Result, SkipForSpecialization); } else if (const auto *Function = dyn_cast<FunctionDecl>(CurDecl)) { - R = HandleFunction(Function, Result, Pattern, RelativeToPrimary, + R = HandleFunction(*this, Function, Result, Pattern, RelativeToPrimary, ForConstraintInstantiation); } else if (const auto *Rec = dyn_cast<CXXRecordDecl>(CurDecl)) { - R = HandleRecordDecl(Rec, Result, Context, ForConstraintInstantiation); + R = HandleRecordDecl(*this, Rec, Result, Context, + ForConstraintInstantiation); } else if (const auto *CSD = dyn_cast<ImplicitConceptSpecializationDecl>(CurDecl)) { R = HandleImplicitConceptSpecializationDecl(CSD, Result); @@ -470,6 +589,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const { case BuildingBuiltinDumpStructCall: case LambdaExpressionSubstitution: case BuildingDeductionGuides: + case TypeAliasTemplateInstantiation: return false; // This function should never be called when Kind's value is Memoization. @@ -616,6 +736,15 @@ Sema::InstantiatingTemplate::InstantiatingTemplate( TemplateArgs) {} Sema::InstantiatingTemplate::InstantiatingTemplate( + Sema &SemaRef, SourceLocation PointOfInstantiation, + TypeAliasTemplateDecl *Template, ArrayRef<TemplateArgument> TemplateArgs, + SourceRange InstantiationRange) + : InstantiatingTemplate( + SemaRef, Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation, + PointOfInstantiation, InstantiationRange, /*Entity=*/Template, + /*Template=*/nullptr, TemplateArgs) {} + +Sema::InstantiatingTemplate::InstantiatingTemplate( Sema &SemaRef, SourceLocation PointOfInstantiation, TemplateDecl *Template, NamedDecl *Param, ArrayRef<TemplateArgument> TemplateArgs, SourceRange InstantiationRange) @@ -1132,6 +1261,8 @@ void Sema::PrintInstantiationStack() { Diags.Report(Active->PointOfInstantiation, diag::note_building_deduction_guide_here); break; + case CodeSynthesisContext::TypeAliasTemplateInstantiation: + break; } } } @@ -1209,6 +1340,7 @@ std::optional<TemplateDeductionInfo *> Sema::isSFINAEContext() const { break; case CodeSynthesisContext::Memoization: + case CodeSynthesisContext::TypeAliasTemplateInstantiation: break; } @@ -1534,6 +1666,18 @@ namespace { SubstTemplateTypeParmPackTypeLoc TL, bool SuppressObjCLifetime); + CXXRecordDecl::LambdaDependencyKind + ComputeLambdaDependency(LambdaScopeInfo *LSI) { + auto &CCS = SemaRef.CodeSynthesisContexts.back(); + if (CCS.Kind == + Sema::CodeSynthesisContext::TypeAliasTemplateInstantiation) { + unsigned TypeAliasDeclDepth = CCS.Entity->getTemplateDepth(); + if (TypeAliasDeclDepth >= TemplateArgs.getNumSubstitutedLevels()) + return CXXRecordDecl::LambdaDependencyKind::LDK_AlwaysDependent; + } + return inherited::ComputeLambdaDependency(LSI); + } + ExprResult TransformLambdaExpr(LambdaExpr *E) { LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true); Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this); diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index dc97201..1cb071e 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1112,6 +1112,13 @@ TemplateDeclInstantiator::VisitTypeAliasTemplateDecl(TypeAliasTemplateDecl *D) { return nullptr; TypeAliasDecl *Pattern = D->getTemplatedDecl(); + Sema::InstantiatingTemplate InstTemplate( + SemaRef, D->getBeginLoc(), D, + D->getTemplateDepth() >= TemplateArgs.getNumLevels() + ? ArrayRef<TemplateArgument>() + : (TemplateArgs.begin() + TemplateArgs.getNumLevels() - 1 - + D->getTemplateDepth()) + ->Args); TypeAliasTemplateDecl *PrevAliasTemplate = nullptr; if (getPreviousDeclForInstantiation<TypedefNameDecl>(Pattern)) { diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h index a2568ad..7df352c 100644 --- a/clang/lib/Sema/TreeTransform.h +++ b/clang/lib/Sema/TreeTransform.h @@ -768,6 +768,12 @@ public: /// the body. StmtResult SkipLambdaBody(LambdaExpr *E, Stmt *Body); + CXXRecordDecl::LambdaDependencyKind + ComputeLambdaDependency(LambdaScopeInfo *LSI) { + return static_cast<CXXRecordDecl::LambdaDependencyKind>( + LSI->Lambda->getLambdaDependencyKind()); + } + QualType TransformReferenceType(TypeLocBuilder &TLB, ReferenceTypeLoc TL); StmtResult TransformCompoundStmt(CompoundStmt *S, bool IsStmtExpr); @@ -13992,6 +13998,46 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) { /*IsInstantiation*/ true); SavedContext.pop(); + // Recompute the dependency of the lambda so that we can defer the lambda call + // construction until after we have all the necessary template arguments. For + // example, given + // + // template <class> struct S { + // template <class U> + // using Type = decltype([](U){}(42.0)); + // }; + // void foo() { + // using T = S<int>::Type<float>; + // ^~~~~~ + // } + // + // We would end up here from instantiating S<int> when ensuring its + // completeness. That would transform the lambda call expression regardless of + // the absence of the corresponding argument for U. + // + // Going ahead with unsubstituted type U makes things worse: we would soon + // compare the argument type (which is float) against the parameter U + // somewhere in Sema::BuildCallExpr. Then we would quickly run into a bogus + // error suggesting unmatched types 'U' and 'float'! + // + // That said, everything will be fine if we defer that semantic checking. + // Fortunately, we have such a mechanism that bypasses it if the CallExpr is + // dependent. Since the CallExpr's dependency boils down to the lambda's + // dependency in this case, we can harness that by recomputing the dependency + // from the instantiation arguments. + // + // FIXME: Creating the type of a lambda requires us to have a dependency + // value, which happens before its substitution. We update its dependency + // *after* the substitution in case we can't decide the dependency + // so early, e.g. because we want to see if any of the *substituted* + // parameters are dependent. + DependencyKind = getDerived().ComputeLambdaDependency(&LSICopy); + Class->setLambdaDependencyKind(DependencyKind); + // Clean up the type cache created previously. Then, we re-create a type for + // such Decl with the new DependencyKind. + Class->setTypeForDecl(nullptr); + getSema().Context.getTypeDeclType(Class); + return getSema().BuildLambdaExpr(E->getBeginLoc(), Body.get()->getEndLoc(), &LSICopy); } diff --git a/clang/test/SemaTemplate/alias-template-with-lambdas.cpp b/clang/test/SemaTemplate/alias-template-with-lambdas.cpp new file mode 100644 index 0000000..ff94031 --- /dev/null +++ b/clang/test/SemaTemplate/alias-template-with-lambdas.cpp @@ -0,0 +1,105 @@ +// RUN: %clang_cc1 -std=c++2c -fsyntax-only -verify %s +namespace lambda_calls { + +template <class> +concept True = true; + +template <class> +concept False = false; // #False + +template <class T> struct S { + template <class... U> using type = decltype([](U...) {}(U()...)); + template <class U> using type2 = decltype([](auto) {}(1)); + template <class U> using type3 = decltype([](True auto) {}(1)); + template <class> + using type4 = decltype([](auto... pack) { return sizeof...(pack); }(1, 2)); + + template <class U> using type5 = decltype([](False auto...) {}(1)); // #Type5 + + template <class U> + using type6 = decltype([]<True> {}.template operator()<char>()); + template <class U> + using type7 = decltype([]<False> {}.template operator()<char>()); // #Type7 + + template <class U> + using type8 = decltype([]() // #Type8 + requires(sizeof(U) == 32) // #Type8-requirement + {}()); + + template <class... U> + using type9 = decltype([]<True>(U...) {}.template operator()<char>(U()...)); + // https://github.com/llvm/llvm-project/issues/76674 + template <class U> + using type10 = decltype([]<class V> { return V(); }.template operator()<U>()); + + template <class U> using type11 = decltype([] { return U{}; }); +}; + +template <class> using Meow = decltype([]<True> {}.template operator()<int>()); + +template <class... U> +using MeowMeow = decltype([]<True>(U...) {}.template operator()<char>(U()...)); + +// https://github.com/llvm/llvm-project/issues/70601 +template <class> using U = decltype([]<True> {}.template operator()<int>()); + +U<int> foo(); + +void bar() { + using T = S<int>::type<int, int, int>; + using T2 = S<int>::type2<int>; + using T3 = S<int>::type3<char>; + using T4 = S<int>::type4<void>; + using T5 = S<int>::type5<void>; // #T5 + // expected-error@#Type5 {{no matching function for call}} + // expected-note@#T5 {{type alias 'type5' requested here}} + // expected-note@#Type5 {{constraints not satisfied [with auto:1 = <int>]}} + // expected-note@#Type5 {{because 'int' does not satisfy 'False'}} + // expected-note@#False {{because 'false' evaluated to false}} + + using T6 = S<int>::type6<void>; + using T7 = S<int>::type7<void>; // #T7 + // expected-error@#Type7 {{no matching member function for call}} + // expected-note@#T7 {{type alias 'type7' requested here}} + // expected-note@#Type7 {{constraints not satisfied [with $0 = char]}} + // expected-note@#Type7 {{because 'char' does not satisfy 'False'}} + // expected-note@#False {{because 'false' evaluated to false}} + + using T8 = S<int>::type8<char>; // #T8 + // expected-error@#Type8 {{no matching function for call}} + // expected-note@#T8 {{type alias 'type8' requested here}} + // expected-note@#Type8 {{constraints not satisfied}} + // expected-note@#Type8-requirement {{because 'sizeof(char) == 32' (1 == 32) evaluated to false}} + + using T9 = S<int>::type9<long, long, char>; + using T10 = S<int>::type10<int>; + using T11 = S<int>::type11<int>; + int x = T11()(); + using T12 = Meow<int>; + using T13 = MeowMeow<char, int, long, unsigned>; + + static_assert(__is_same(T, void)); + static_assert(__is_same(T2, void)); + static_assert(__is_same(T3, void)); + static_assert(__is_same(T4, decltype(sizeof(0)))); + static_assert(__is_same(T6, void)); + static_assert(__is_same(T9, void)); + static_assert(__is_same(T10, int)); + static_assert(__is_same(T12, void)); + static_assert(__is_same(T13, void)); +} + +namespace GH82104 { + +template <typename, typename...> int Zero = 0; + +template <typename T, typename...U> +using T14 = decltype([]<int V = 0>() { return Zero<T, U...>; }()); + +template <typename T> using T15 = T14<T, T>; + +static_assert(__is_same(T15<char>, int)); + +} // namespace GH82104 + +} // namespace lambda_calls |