Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -1295,6 +1295,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
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;

Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@ class Attr : public AttributeCommonInfo {
// Pretty print this attribute.
void printPretty(raw_ostream &OS, const PrintingPolicy &Policy) const;

void dump() const;

static StringRef getDocumentation(attr::Kind);
};

Expand Down
14 changes: 12 additions & 2 deletions clang/include/clang/Sema/Template.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}

Expand Down Expand Up @@ -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
Expand Down
28 changes: 25 additions & 3 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3140,13 +3140,35 @@ const FunctionType *ASTContext::adjustFunctionType(const FunctionType *T,
return cast<FunctionType>(Result.getTypePtr());
}

QualType ASTContext::adjustFunctionResultType(QualType FunctionType,
QualType ResultType) {
// Might be wrapped in a macro qualified type.
if (const auto *MQT = dyn_cast<MacroQualifiedType>(FunctionType)) {
return getMacroQualifiedType(
adjustFunctionResultType(MQT->getUnderlyingType(), ResultType),
MQT->getMacroIdentifier());
}

// Might have a calling-convention attribute.
if (const auto *AT = dyn_cast<AttributedType>(FunctionType)) {
return getAttributedType(
AT->getAttrKind(),
adjustFunctionResultType(AT->getModifiedType(), ResultType),
adjustFunctionResultType(AT->getEquivalentType(), ResultType));
}

// Anything else must be a function type. Rebuild it with the new return
// value.
const auto *FPT = FunctionType->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
Expand Down
8 changes: 8 additions & 0 deletions clang/lib/AST/ASTDumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -361,3 +361,11 @@ LLVM_DUMP_METHOD void ConceptReference::dump(raw_ostream &OS) const {
ASTDumper P(OS, Ctx, Ctx.getDiagnostics().getShowColors());
P.Visit(this);
}

//===----------------------------------------------------------------------===//
// Attr method implementations
//===----------------------------------------------------------------------===//
LLVM_DUMP_METHOD void Attr::dump() const {
ASTDumper P(llvm::errs(), /*ShowColors=*/false);
P.Visit(this);
}
13 changes: 10 additions & 3 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1535,7 +1535,8 @@ namespace {
bool SuppressObjCLifetime);

ExprResult TransformLambdaExpr(LambdaExpr *E) {
LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true);
LocalInstantiationScope Scope(SemaRef, /*CombineWithOuterScope=*/true,
/*InstantiatingLambda=*/true);
Sema::ConstraintEvalRAII<TemplateInstantiator> RAII(*this);

ExprResult Result = inherited::TransformLambdaExpr(E);
Expand Down Expand Up @@ -2276,8 +2277,14 @@ 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, then TemplateInstantiator::TransformLambdaExpr will
// have already pushed a scope for this prototype, so don't create a second
// one. Otherwise, push a new instantiation scope.
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);
}
Expand Down
108 changes: 38 additions & 70 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -675,10 +675,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,
Expand Down Expand Up @@ -7212,11 +7208,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();

Expand All @@ -7231,12 +7226,16 @@ 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();
// Do not transform the equivalent type if it is equal to the modified type.
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.
Expand All @@ -7260,15 +7259,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>::TransformBTFTagAttributedType(
TypeLocBuilder &TLB, BTFTagAttributedTypeLoc TL) {
Expand Down Expand Up @@ -13830,62 +13820,40 @@ TreeTransform<Derived>::TransformLambdaExpr(LambdaExpr *E) {
getSema().AddTemplateParametersToLambdaCallOperator(NewCallOperator, Class,
TPL);

// 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);
}
auto OldCallOpTypeLoc =
E->getCallOperator()->getTypeSourceInfo()->getTypeLoc();

if (NewCallOpType.isNull())
return ExprError();
NewCallOpTSI =
NewCallOpTLBuilder.getTypeSourceInfo(getSema().Context, NewCallOpType);
}
QualType NewCallOpType;
TypeLocBuilder NewCallOpTLBuilder;

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();
}
NewCallOpType =
getDerived().TransformType(NewCallOpTLBuilder, OldCallOpTypeLoc);
if (NewCallOpType.isNull())
return ExprError();
NewCallOpTSI =
NewCallOpTLBuilder.getTypeSourceInfo(getSema().Context, NewCallOpType);

auto ExtractParams = [](TypeLoc TL) {
auto Impl = [](auto Self, TypeLoc TL) -> ArrayRef<ParmVarDecl *> {
if (auto FPTL = TL.getAs<FunctionProtoTypeLoc>())
return FPTL.getParams();
if (auto ATL = TL.getAs<AttributedTypeLoc>())
return Self(Self, ATL.getModifiedLoc());
if (auto MQTL = TL.getAs<MacroQualifiedTypeLoc>())
return Self(Self, MQTL.getInnerLoc());
llvm_unreachable("Unhandled TypeLoc");
};
return Impl(Impl, TL);
};

getSema().CompleteLambdaCallOperator(
NewCallOperator, E->getCallOperator()->getLocation(),
E->getCallOperator()->getInnerLocStart(),
E->getCallOperator()->getTrailingRequiresClause(), NewCallOpTSI,
E->getCallOperator()->getConstexprKind(),
E->getCallOperator()->getStorageClass(), Params,
E->hasExplicitResultType());
E->getCallOperator()->getStorageClass(),
ExtractParams(NewCallOpTSI->getTypeLoc()), E->hasExplicitResultType());

getDerived().transformAttrs(E->getCallOperator(), NewCallOperator);
getDerived().transformedLocalDecl(E->getCallOperator(), {NewCallOperator});
Expand Down
62 changes: 62 additions & 0 deletions clang/test/SemaCXX/lambda-attributes.cpp
Original file line number Diff line number Diff line change
@@ -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>();
}
10 changes: 5 additions & 5 deletions clang/test/SemaCXX/lambda-conversion-op-cc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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))'
Expand Down