Skip to content

[Clang] Bugfixes and improved support for AttributedTypes in lambdas #85325

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
15 changes: 13 additions & 2 deletions clang/include/clang/AST/ASTContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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,
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
91 changes: 63 additions & 28 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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,
Expand Down Expand Up @@ -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);

Expand Down
17 changes: 14 additions & 3 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
Expand Down
113 changes: 40 additions & 73 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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();

Expand All @@ -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.
Expand All @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down
Loading