[clang] f4fa16f - [Clang] Bugfixes and improved support for `AttributedType`s in lambdas (#85325)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Sep 25 18:24:57 PDT 2024
Author: Sirraide
Date: 2024-09-26T03:24:53+02:00
New Revision: f4fa16f14b3ee7da244687e4a138a5a6df3e1a48
URL: https://github.com/llvm/llvm-project/commit/f4fa16f14b3ee7da244687e4a138a5a6df3e1a48
DIFF: https://github.com/llvm/llvm-project/commit/f4fa16f14b3ee7da244687e4a138a5a6df3e1a48.diff
LOG: [Clang] Bugfixes and improved support for `AttributedType`s in lambdas (#85325)
This fixes a crash when we attempt to instantiate a lambda with an
`AnnotatedType`, refactors the code that handles transforming the
function type of a lambda, and improves source fidelity for lambda
function types.
This fixes #85120 and fixes #85154.
---------
Co-authored-by: Yuxuan Chen <ych at meta.com>
Co-authored-by: Doug Wyatt <dwyatt at apple.com>
Added:
clang/test/SemaCXX/lambda-attributes.cpp
Modified:
clang/include/clang/AST/ASTContext.h
clang/include/clang/Sema/Template.h
clang/lib/AST/ASTContext.cpp
clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/lib/Sema/TreeTransform.h
clang/test/SemaCXX/lambda-conversion-op-cc.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index 1984310df0442e..fbf38ab4da6c8c 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -253,7 +253,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
mutable llvm::FoldingSet<BitIntType> BitIntTypes;
mutable llvm::ContextualFoldingSet<DependentBitIntType, ASTContext &>
DependentBitIntTypes;
- llvm::FoldingSet<BTFTagAttributedType> BTFTagAttributedTypes;
+ mutable llvm::FoldingSet<BTFTagAttributedType> BTFTagAttributedTypes;
llvm::FoldingSet<HLSLAttributedResourceType> HLSLAttributedResourceTypes;
mutable llvm::FoldingSet<CountAttributedType> CountAttributedTypes;
@@ -1369,10 +1369,21 @@ class ASTContext : public RefCountedBase<ASTContext> {
/// calling T.withConst().
QualType getConstType(QualType T) const { return T.withConst(); }
+ /// Rebuild a type, preserving any existing type sugar. For function types,
+ /// you probably want to just use \c adjustFunctionResultType and friends
+ /// instead.
+ QualType adjustType(QualType OldType,
+ llvm::function_ref<QualType(QualType)> Adjust) const;
+
/// Change the ExtInfo on a function type.
const FunctionType *adjustFunctionType(const FunctionType *Fn,
FunctionType::ExtInfo EInfo);
+ /// Change the result type of a function type, preserving sugar such as
+ /// attributed types.
+ QualType adjustFunctionResultType(QualType FunctionType,
+ QualType NewResultType);
+
/// Adjust the given function result type.
CanQualType getCanonicalFunctionResultType(QualType ResultType) const;
@@ -1702,7 +1713,7 @@ class ASTContext : public RefCountedBase<ASTContext> {
QualType equivalentType) const;
QualType getBTFTagAttributedType(const BTFTypeTagAttr *BTFAttr,
- QualType Wrapped);
+ QualType Wrapped) const;
QualType getHLSLAttributedResourceType(
QualType Wrapped, QualType Contained,
diff --git a/clang/include/clang/Sema/Template.h b/clang/include/clang/Sema/Template.h
index 0340c23fd170d6..fe27290efdbfc5 100644
--- a/clang/include/clang/Sema/Template.h
+++ b/clang/include/clang/Sema/Template.h
@@ -411,6 +411,11 @@ enum class TemplateSubstitutionKind : char {
/// lookup will search our outer scope.
bool CombineWithOuterScope;
+ /// Whether this scope is being used to instantiate a lambda expression,
+ /// in which case it should be reused for instantiating the lambda's
+ /// FunctionProtoType.
+ bool InstantiatingLambda = false;
+
/// If non-NULL, the template parameter pack that has been
/// partially substituted per C++0x [temp.arg.explicit]p9.
NamedDecl *PartiallySubstitutedPack = nullptr;
@@ -425,9 +430,11 @@ enum class TemplateSubstitutionKind : char {
unsigned NumArgsInPartiallySubstitutedPack;
public:
- LocalInstantiationScope(Sema &SemaRef, bool CombineWithOuterScope = false)
+ LocalInstantiationScope(Sema &SemaRef, bool CombineWithOuterScope = false,
+ bool InstantiatingLambda = false)
: SemaRef(SemaRef), Outer(SemaRef.CurrentInstantiationScope),
- CombineWithOuterScope(CombineWithOuterScope) {
+ CombineWithOuterScope(CombineWithOuterScope),
+ InstantiatingLambda(InstantiatingLambda) {
SemaRef.CurrentInstantiationScope = this;
}
@@ -553,6 +560,9 @@ enum class TemplateSubstitutionKind : char {
/// Determine whether D is a pack expansion created in this scope.
bool isLocalPackExpansion(const Decl *D);
+
+ /// Determine whether this scope is for instantiating a lambda.
+ bool isLambda() const { return InstantiatingLambda; }
};
class TemplateDeclInstantiator
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index fd8aa8de79b49f..cda8b02cc8499a 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -3535,6 +3535,50 @@ QualType ASTContext::getCountAttributedType(
return QualType(CATy, 0);
}
+QualType
+ASTContext::adjustType(QualType Orig,
+ llvm::function_ref<QualType(QualType)> Adjust) const {
+ switch (Orig->getTypeClass()) {
+ case Type::Attributed: {
+ const auto *AT = dyn_cast<AttributedType>(Orig);
+ return getAttributedType(AT->getAttrKind(),
+ adjustType(AT->getModifiedType(), Adjust),
+ adjustType(AT->getEquivalentType(), Adjust));
+ }
+
+ case Type::BTFTagAttributed: {
+ const auto *BTFT = dyn_cast<BTFTagAttributedType>(Orig);
+ return getBTFTagAttributedType(BTFT->getAttr(),
+ adjustType(BTFT->getWrappedType(), Adjust));
+ }
+
+ case Type::Elaborated: {
+ const auto *ET = cast<ElaboratedType>(Orig);
+ return getElaboratedType(ET->getKeyword(), ET->getQualifier(),
+ adjustType(ET->getNamedType(), Adjust));
+ }
+
+ case Type::Paren:
+ return getParenType(
+ adjustType(cast<ParenType>(Orig)->getInnerType(), Adjust));
+
+ case Type::Adjusted: {
+ const auto *AT = cast<AdjustedType>(Orig);
+ return getAdjustedType(AT->getOriginalType(),
+ adjustType(AT->getAdjustedType(), Adjust));
+ }
+
+ case Type::MacroQualified: {
+ const auto *MQT = cast<MacroQualifiedType>(Orig);
+ return getMacroQualifiedType(adjustType(MQT->getUnderlyingType(), Adjust),
+ MQT->getMacroIdentifier());
+ }
+
+ default:
+ return Adjust(Orig);
+ }
+}
+
const FunctionType *ASTContext::adjustFunctionType(const FunctionType *T,
FunctionType::ExtInfo Info) {
if (T->getExtInfo() == Info)
@@ -3553,13 +3597,23 @@ const FunctionType *ASTContext::adjustFunctionType(const FunctionType *T,
return cast<FunctionType>(Result.getTypePtr());
}
+QualType ASTContext::adjustFunctionResultType(QualType FunctionType,
+ QualType ResultType) {
+ return adjustType(FunctionType, [&](QualType Orig) {
+ if (const auto *FNPT = Orig->getAs<FunctionNoProtoType>())
+ return getFunctionNoProtoType(ResultType, FNPT->getExtInfo());
+
+ const auto *FPT = Orig->castAs<FunctionProtoType>();
+ return getFunctionType(ResultType, FPT->getParamTypes(),
+ FPT->getExtProtoInfo());
+ });
+}
+
void ASTContext::adjustDeducedFunctionResultType(FunctionDecl *FD,
QualType ResultType) {
FD = FD->getMostRecentDecl();
while (true) {
- const auto *FPT = FD->getType()->castAs<FunctionProtoType>();
- FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo();
- FD->setType(getFunctionType(ResultType, FPT->getParamTypes(), EPI));
+ FD->setType(adjustFunctionResultType(FD->getType(), ResultType));
if (FunctionDecl *Next = FD->getPreviousDecl())
FD = Next;
else
@@ -3575,30 +3629,11 @@ void ASTContext::adjustDeducedFunctionResultType(FunctionDecl *FD,
/// and preserved. Other type sugar (for instance, typedefs) is not.
QualType ASTContext::getFunctionTypeWithExceptionSpec(
QualType Orig, const FunctionProtoType::ExceptionSpecInfo &ESI) const {
- // Might have some parens.
- if (const auto *PT = dyn_cast<ParenType>(Orig))
- return getParenType(
- getFunctionTypeWithExceptionSpec(PT->getInnerType(), ESI));
-
- // Might be wrapped in a macro qualified type.
- if (const auto *MQT = dyn_cast<MacroQualifiedType>(Orig))
- return getMacroQualifiedType(
- getFunctionTypeWithExceptionSpec(MQT->getUnderlyingType(), ESI),
- MQT->getMacroIdentifier());
-
- // Might have a calling-convention attribute.
- if (const auto *AT = dyn_cast<AttributedType>(Orig))
- return getAttributedType(
- AT->getAttrKind(),
- getFunctionTypeWithExceptionSpec(AT->getModifiedType(), ESI),
- getFunctionTypeWithExceptionSpec(AT->getEquivalentType(), ESI));
-
- // Anything else must be a function type. Rebuild it with the new exception
- // specification.
- const auto *Proto = Orig->castAs<FunctionProtoType>();
- return getFunctionType(
- Proto->getReturnType(), Proto->getParamTypes(),
- Proto->getExtProtoInfo().withExceptionSpec(ESI));
+ return adjustType(Orig, [&](QualType Ty) {
+ const auto *Proto = Ty->castAs<FunctionProtoType>();
+ return getFunctionType(Proto->getReturnType(), Proto->getParamTypes(),
+ Proto->getExtProtoInfo().withExceptionSpec(ESI));
+ });
}
bool ASTContext::hasSameFunctionTypeIgnoringExceptionSpec(QualType T,
@@ -5165,7 +5200,7 @@ QualType ASTContext::getAttributedType(attr::Kind attrKind,
}
QualType ASTContext::getBTFTagAttributedType(const BTFTypeTagAttr *BTFAttr,
- QualType Wrapped) {
+ QualType Wrapped) const {
llvm::FoldingSetNodeID ID;
BTFTagAttributedType::Profile(ID, Wrapped, BTFAttr);
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 55f38743e2768e..fd51fa4afcacbf 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1677,7 +1677,8 @@ namespace {
// Lambdas have already been processed inside their eval contexts.
if (SemaRef.RebuildingImmediateInvocation)
return E;
- LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
+ LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true,
+ /*InstantiatingLambda=*/true);
Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);
return inherited::TransformLambdaExpr(E);
@@ -2432,8 +2433,18 @@ QualType TemplateInstantiator::TransformFunctionProtoType(TypeLocBuilder &TLB,
CXXRecordDecl *ThisContext,
Qualifiers ThisTypeQuals,
Fn TransformExceptionSpec) {
- // We need a local instantiation scope for this function prototype.
- LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
+ // If this is a lambda, the transformation MUST be done in the
+ // CurrentInstantiationScope since it introduces a mapping of
+ // the original to the newly created transformed parameters.
+ //
+ // In that case, TemplateInstantiator::TransformLambdaExpr will
+ // have already pushed a scope for this prototype, so don't create
+ // a second one.
+ LocalInstantiationScope *Current = getSema().CurrentInstantiationScope;
+ std::optional<LocalInstantiationScope> Scope;
+ if (!Current || !Current->isLambda())
+ Scope.emplace(SemaRef, /*CombineWithOuterScope=*/true);
+
return inherited::TransformFunctionProtoType(
TLB, TL, ThisContext, ThisTypeQuals, TransformExceptionSpec);
}
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 95ded5e59a9fa7..91cb980ee26b26 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -684,10 +684,6 @@ class TreeTransform {
Qualifiers ThisTypeQuals,
Fn TransformExceptionSpec);
- template <typename Fn>
- QualType TransformAttributedType(TypeLocBuilder &TLB, AttributedTypeLoc TL,
- Fn TransformModifiedType);
-
bool TransformExceptionSpec(SourceLocation Loc,
FunctionProtoType::ExceptionSpecInfo &ESI,
SmallVectorImpl<QualType> &Exceptions,
@@ -7373,11 +7369,10 @@ TreeTransform<Derived>::TransformElaboratedType(TypeLocBuilder &TLB,
}
template <typename Derived>
-template <typename Fn>
-QualType TreeTransform<Derived>::TransformAttributedType(
- TypeLocBuilder &TLB, AttributedTypeLoc TL, Fn TransformModifiedTypeFn) {
+QualType TreeTransform<Derived>::TransformAttributedType(TypeLocBuilder &TLB,
+ AttributedTypeLoc TL) {
const AttributedType *oldType = TL.getTypePtr();
- QualType modifiedType = TransformModifiedTypeFn(TLB, TL.getModifiedLoc());
+ QualType modifiedType = getDerived().TransformType(TLB, TL.getModifiedLoc());
if (modifiedType.isNull())
return QualType();
@@ -7392,12 +7387,27 @@ QualType TreeTransform<Derived>::TransformAttributedType(
// FIXME: dependent operand expressions?
if (getDerived().AlwaysRebuild() ||
modifiedType != oldType->getModifiedType()) {
- TypeLocBuilder AuxiliaryTLB;
- AuxiliaryTLB.reserve(TL.getFullDataSize());
- QualType equivalentType =
- getDerived().TransformType(AuxiliaryTLB, TL.getEquivalentTypeLoc());
- if (equivalentType.isNull())
- return QualType();
+ // If the equivalent type is equal to the modified type, we don't want to
+ // transform it as well because:
+ //
+ // 1. The transformation would yield the same result and is therefore
+ // superfluous, and
+ //
+ // 2. Transforming the same type twice can cause problems, e.g. if it
+ // is a FunctionProtoType, we may end up instantiating the function
+ // parameters twice, which causes an assertion since the parameters
+ // are already bound to their counterparts in the template for this
+ // instantiation.
+ //
+ QualType equivalentType = modifiedType;
+ if (TL.getModifiedLoc().getType() != TL.getEquivalentTypeLoc().getType()) {
+ TypeLocBuilder AuxiliaryTLB;
+ AuxiliaryTLB.reserve(TL.getFullDataSize());
+ equivalentType =
+ getDerived().TransformType(AuxiliaryTLB, TL.getEquivalentTypeLoc());
+ if (equivalentType.isNull())
+ return QualType();
+ }
// Check whether we can add nullability; it is only represented as
// type sugar, and therefore cannot be diagnosed in any other way.
@@ -7421,15 +7431,6 @@ QualType TreeTransform<Derived>::TransformAttributedType(
return result;
}
-template <typename Derived>
-QualType TreeTransform<Derived>::TransformAttributedType(TypeLocBuilder &TLB,
- AttributedTypeLoc TL) {
- return getDerived().TransformAttributedType(
- TLB, TL, [&](TypeLocBuilder &TLB, TypeLoc ModifiedLoc) -> QualType {
- return getDerived().TransformType(TLB, ModifiedLoc);
- });
-}
-
template <typename Derived>
QualType TreeTransform<Derived>::TransformCountAttributedType(
TypeLocBuilder &TLB, CountAttributedTypeLoc TL) {
@@ -14774,63 +14775,29 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
TPL->containsUnexpandedParameterPack();
}
- // Transform the type of the original lambda's call operator.
- // The transformation MUST be done in the CurrentInstantiationScope since
- // it introduces a mapping of the original to the newly created
- // transformed parameters.
- TypeSourceInfo *NewCallOpTSI = nullptr;
- {
- auto OldCallOpTypeLoc =
- E->getCallOperator()->getTypeSourceInfo()->getTypeLoc();
-
- auto TransformFunctionProtoTypeLoc =
- [this](TypeLocBuilder &TLB, FunctionProtoTypeLoc FPTL) -> QualType {
- SmallVector<QualType, 4> ExceptionStorage;
- return this->TransformFunctionProtoType(
- TLB, FPTL, nullptr, Qualifiers(),
- [&](FunctionProtoType::ExceptionSpecInfo &ESI, bool &Changed) {
- return TransformExceptionSpec(FPTL.getBeginLoc(), ESI,
- ExceptionStorage, Changed);
- });
- };
-
- QualType NewCallOpType;
- TypeLocBuilder NewCallOpTLBuilder;
-
- if (auto ATL = OldCallOpTypeLoc.getAs<AttributedTypeLoc>()) {
- NewCallOpType = this->TransformAttributedType(
- NewCallOpTLBuilder, ATL,
- [&](TypeLocBuilder &TLB, TypeLoc TL) -> QualType {
- return TransformFunctionProtoTypeLoc(
- TLB, TL.castAs<FunctionProtoTypeLoc>());
- });
- } else {
- auto FPTL = OldCallOpTypeLoc.castAs<FunctionProtoTypeLoc>();
- NewCallOpType = TransformFunctionProtoTypeLoc(NewCallOpTLBuilder, FPTL);
- }
-
- if (NewCallOpType.isNull())
- return ExprError();
- LSI->ContainsUnexpandedParameterPack |=
- NewCallOpType->containsUnexpandedParameterPack();
- NewCallOpTSI =
- NewCallOpTLBuilder.getTypeSourceInfo(getSema().Context, NewCallOpType);
- }
+ TypeLocBuilder NewCallOpTLBuilder;
+ TypeLoc OldCallOpTypeLoc =
+ E->getCallOperator()->getTypeSourceInfo()->getTypeLoc();
+ QualType NewCallOpType =
+ getDerived().TransformType(NewCallOpTLBuilder, OldCallOpTypeLoc);
+ if (NewCallOpType.isNull())
+ return ExprError();
+ LSI->ContainsUnexpandedParameterPack |=
+ NewCallOpType->containsUnexpandedParameterPack();
+ TypeSourceInfo *NewCallOpTSI =
+ NewCallOpTLBuilder.getTypeSourceInfo(getSema().Context, NewCallOpType);
- ArrayRef<ParmVarDecl *> Params;
- if (auto ATL = NewCallOpTSI->getTypeLoc().getAs<AttributedTypeLoc>()) {
- Params = ATL.getModifiedLoc().castAs<FunctionProtoTypeLoc>().getParams();
- } else {
- auto FPTL = NewCallOpTSI->getTypeLoc().castAs<FunctionProtoTypeLoc>();
- Params = FPTL.getParams();
- }
+ // The type may be an AttributedType or some other kind of sugar;
+ // get the actual underlying FunctionProtoType.
+ auto FPTL = NewCallOpTSI->getTypeLoc().getAsAdjusted<FunctionProtoTypeLoc>();
+ assert(FPTL && "Not a FunctionProtoType?");
getSema().CompleteLambdaCallOperator(
NewCallOperator, E->getCallOperator()->getLocation(),
E->getCallOperator()->getInnerLocStart(),
E->getCallOperator()->getTrailingRequiresClause(), NewCallOpTSI,
E->getCallOperator()->getConstexprKind(),
- E->getCallOperator()->getStorageClass(), Params,
+ E->getCallOperator()->getStorageClass(), FPTL.getParams(),
E->hasExplicitResultType());
getDerived().transformAttrs(E->getCallOperator(), NewCallOperator);
diff --git a/clang/test/SemaCXX/lambda-attributes.cpp b/clang/test/SemaCXX/lambda-attributes.cpp
new file mode 100644
index 00000000000000..799649719cf42b
--- /dev/null
+++ b/clang/test/SemaCXX/lambda-attributes.cpp
@@ -0,0 +1,62 @@
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -ast-dump %s | FileCheck %s
+// RUN: %clang_cc1 -std=c++23 -triple x86_64-pc-linux -emit-pch -o %t %s
+// RUN: %clang_cc1 -x c++ -std=c++23 -triple x86_64-pc-linux -include-pch %t -ast-dump-all /dev/null | FileCheck %s
+// expected-no-diagnostics
+
+// Check that we both don't crash on transforming FunctionProtoType's
+// wrapped in type sugar and that we don't drop it when performing
+// instantiations either.
+
+#define PRESERVE __attribute__((preserve_most))
+
+// Skip to the instantiation of f().
+// CHECK: FunctionDecl {{.*}} f 'void ()' implicit_instantiation
+template <typename T>
+void f() {
+ // CHECK: CXXMethodDecl {{.*}} operator() 'void (int) const __attribute__((preserve_most))':'void (int) __attribute__((preserve_most)) const' implicit_instantiation
+ (void) [] (T) __attribute__((preserve_most)) { };
+
+ // CHECK: CXXMethodDecl {{.*}} operator() 'void (int) const {{\[}}[clang::annotate_type(...)]]':'void (int) const' implicit_instantiation
+ (void) [] (T) [[clang::annotate_type("foo")]] { };
+
+ // CHECK: CXXMethodDecl {{.*}} operator() 'void (int) const {{\[}}[clang::annotate_type(...)]] {{\[}}[clang::annotate_type(...)]] {{\[}}[clang::annotate_type(...)]]':'void (int) const' implicit_instantiation
+ (void) [] (T) [[clang::annotate_type("foo")]]
+ [[clang::annotate_type("foo")]]
+ [[clang::annotate_type("foo")]] { };
+
+ // CHECK: CXXMethodDecl {{.*}} operator() 'void (int) const __attribute__((preserve_most)) {{\[}}[clang::annotate_type(...)]]':'void (int) __attribute__((preserve_most)) const' implicit_instantiation
+ (void) [] (T) __attribute__((preserve_most))
+ [[clang::annotate_type("foo")]] { };
+
+ // CHECK: CXXMethodDecl {{.*}} operator() 'void (int) const __attribute__((cdecl)) {{\[}}[clang::annotate_type(...)]]':'void (int) const' implicit_instantiation
+ (void) [] (T) __attribute__((cdecl))
+ [[clang::annotate_type("foo")]] { };
+
+ // CHECK: CXXMethodDecl {{.*}} operator() 'void (int) const {{\[}}[clang::annotate_type(...)]]':'void (int) const' implicit_instantiation
+ (void) [] (T t) [[clang::annotate_type("foo", t)]] { };
+
+ // CHECK: CXXMethodDecl {{.*}} operator() 'void (int) const __attribute__((preserve_most)) {{\[}}[clang::annotate_type(...)]]':'void (int) __attribute__((preserve_most)) const' implicit_instantiation
+ (void) [] (T t) __attribute__((preserve_most))
+ [[clang::annotate_type("foo", t, t, t, t)]] { };
+
+ // Check that the MacroQualifiedType is preserved.
+ // CHECK: CXXMethodDecl {{.*}} operator() 'PRESERVE void (int) __attribute__((preserve_most)) const':'void (int) __attribute__((preserve_most)) const' implicit_instantiation
+ (void) [] (T) PRESERVE { };
+
+ // CHECK: CXXMethodDecl {{.*}} operator() 'PRESERVE void (int) __attribute__((preserve_most)) const {{\[}}[clang::annotate_type(...)]]':'void (int) __attribute__((preserve_most)) const' implicit_instantiation
+ (void) [] (T) PRESERVE [[clang::annotate_type("foo")]] { };
+
+ // CHECK: CXXMethodDecl {{.*}} operator() 'void (int) const {{\[}}[clang::annotate_type(...)]]':'void (int) const' implicit_instantiation
+ (void) [] (T) [[clang::annotate_type("foo")]] {
+ // CHECK: CXXMethodDecl {{.*}} operator() 'PRESERVE void (int) __attribute__((preserve_most)) const {{\[}}[clang::annotate_type(...)]]':'void (int) __attribute__((preserve_most)) const' implicit_instantiation
+ auto l = []<typename U = T> (U u = {}) PRESERVE [[clang::annotate_type("foo", u)]] { };
+
+ // CHECK: DeclRefExpr {{.*}} 'PRESERVE void (int) __attribute__((preserve_most)) const {{\[}}[clang::annotate_type(...)]]':'void (int) __attribute__((preserve_most)) const' lvalue CXXMethod
+ l();
+ };
+}
+
+void g() {
+ f<int>();
+}
diff --git a/clang/test/SemaCXX/lambda-conversion-op-cc.cpp b/clang/test/SemaCXX/lambda-conversion-op-cc.cpp
index 3632f8c8c80aaa..6d9f5d702e1329 100644
--- a/clang/test/SemaCXX/lambda-conversion-op-cc.cpp
+++ b/clang/test/SemaCXX/lambda-conversion-op-cc.cpp
@@ -44,19 +44,19 @@ void useage() {
// CHECK: VarDecl {{.*}} vectorcall '
// CHECK: LambdaExpr
- // CHECK: CXXMethodDecl {{.*}} operator() 'void (int, float, double) __attribute__((vectorcall)) const'
+ // CHECK: CXXMethodDecl {{.*}} operator() 'void (int, float, double) const __attribute__((vectorcall))':'void (int, float, double) __attribute__((vectorcall)) const'
// CHECK: CXXConversionDecl {{.*}} operator void (*)(int, float, double) __attribute__((vectorcall)) 'void
// CHECK: CXXMethodDecl {{.*}} __invoke 'void (int, float, double) __attribute__((vectorcall))' static inline
// WIN32: VarDecl {{.*}} thiscall '
// WIN32: LambdaExpr
- // WIN32: CXXMethodDecl {{.*}} operator() 'void (int, float, double) __attribute__((thiscall)) const'
+ // WIN32: CXXMethodDecl {{.*}} operator() 'void (int, float, double) const __attribute__((thiscall))':'void (int, float, double) __attribute__((thiscall)) const'
// WIN32: CXXConversionDecl {{.*}} operator void (*)(int, float, double) 'void
// WIN32: CXXMethodDecl {{.*}} __invoke 'void (int, float, double)' static inline
// CHECK: VarDecl {{.*}} cdecl '
// CHECK: LambdaExpr
- // CHECK: CXXMethodDecl {{.*}} operator() 'void (int, float, double) const'
+ // CHECK: CXXMethodDecl {{.*}} operator() 'void (int, float, double) const __attribute__((cdecl))':'void (int, float, double) const'
// NODEF: CXXConversionDecl {{.*}} operator void (*)(int, float, double) 'void
// NODEF: CXXMethodDecl {{.*}} __invoke 'void (int, float, double)' static inline
// VECTDEF: CXXConversionDecl {{.*}} operator void (*)(int, float, double) __attribute__((vectorcall)) 'void
@@ -108,8 +108,8 @@ void useage() {
// CHECK: LambdaExpr
// CHECK: FunctionTemplateDecl {{.*}} operator()
// CHECK: CXXMethodDecl {{.*}} operator() 'auto (auto) __attribute__((vectorcall)) const' inline
- // CHECK: CXXMethodDecl {{.*}} operator() 'void (char) __attribute__((vectorcall)) const' implicit_instantiation inline
- // CHECK: CXXMethodDecl {{.*}} operator() 'void (int) __attribute__((vectorcall)) const' implicit_instantiation inline
+ // CHECK: CXXMethodDecl {{.*}} operator() 'void (char) const __attribute__((vectorcall))':'void (char) __attribute__((vectorcall)) const' implicit_instantiation inline
+ // CHECK: CXXMethodDecl {{.*}} operator() 'void (int) const __attribute__((vectorcall))':'void (int) __attribute__((vectorcall)) const' implicit_instantiation inline
// CHECK: FunctionTemplateDecl {{.*}} operator auto (*)(type-parameter-0-0) __attribute__((vectorcall))
// LIN64: CXXConversionDecl {{.*}} operator auto (*)(type-parameter-0-0) __attribute__((vectorcall)) 'auto (*() const noexcept)(auto) __attribute__((vectorcall))'
// LIN64: CXXConversionDecl {{.*}} operator auto (*)(char) __attribute__((vectorcall)) 'void (*() const noexcept)(char) __attribute__((vectorcall))'
More information about the cfe-commits
mailing list