Skip to content
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

[Clang][Sema] Retain the expanding index for unevaluated type constraints #109518

Merged
merged 4 commits into from
Oct 1, 2024
Merged
Show file tree
Hide file tree
Changes from 3 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
2 changes: 2 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,8 @@ Bug Fixes to C++ Support
- Clang now uses the correct set of template argument lists when comparing the constraints of
out-of-line definitions and member templates explicitly specialized for a given implicit instantiation of
a class template. (#GH102320)
- Fixed an issue in constraint evaluation, where type constraints on the lambda expression
containing outer unexpanded parameters were not correctly expanded. (#GH101754)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -11252,6 +11252,7 @@ class Sema final : public SemaBase {
ConceptDecl *NamedConcept, NamedDecl *FoundDecl,
const TemplateArgumentListInfo *TemplateArgs,
TemplateTypeParmDecl *ConstrainedParameter,
QualType ConstrainedType,
SourceLocation EllipsisLoc);

bool AttachTypeConstraint(AutoTypeLoc TL,
Expand Down
6 changes: 4 additions & 2 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1134,7 +1134,8 @@ bool Sema::BuildTypeConstraint(const CXXScopeSpec &SS,
SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc(),
ConceptName, CD, /*FoundDecl=*/USD ? cast<NamedDecl>(USD) : CD,
TypeConstr->LAngleLoc.isValid() ? &TemplateArgs : nullptr,
ConstrainedParameter, EllipsisLoc);
ConstrainedParameter, Context.getTypeDeclType(ConstrainedParameter),
EllipsisLoc);
}

template <typename ArgumentLocAppender>
Expand Down Expand Up @@ -1191,6 +1192,7 @@ bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS,
ConceptDecl *NamedConcept, NamedDecl *FoundDecl,
const TemplateArgumentListInfo *TemplateArgs,
TemplateTypeParmDecl *ConstrainedParameter,
QualType ConstrainedType,
SourceLocation EllipsisLoc) {
// C++2a [temp.param]p4:
// [...] If Q is of the form C<A1, ..., An>, then let E' be
Expand All @@ -1199,7 +1201,7 @@ bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS,
TemplateArgs ? ASTTemplateArgumentListInfo::Create(Context,
*TemplateArgs) : nullptr;

QualType ParamAsArgument(ConstrainedParameter->getTypeForDecl(), 0);
QualType ParamAsArgument = ConstrainedType;

ExprResult ImmediatelyDeclaredConstraint = formImmediatelyDeclaredConstraint(
*this, NS, NameInfo, NamedConcept, FoundDecl,
Expand Down
127 changes: 124 additions & 3 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1646,6 +1646,21 @@ namespace {
SubstTemplateTypeParmPackTypeLoc TL,
bool SuppressObjCLifetime);

QualType
TransformSubstTemplateTypeParmType(TypeLocBuilder &TLB,
SubstTemplateTypeParmTypeLoc TL) {
if (SemaRef.CodeSynthesisContexts.back().Kind !=
Sema::CodeSynthesisContext::ConstraintSubstitution)
return inherited::TransformSubstTemplateTypeParmType(TLB, TL);

auto PackIndex = TL.getTypePtr()->getPackIndex();
std::optional<Sema::ArgumentPackSubstitutionIndexRAII> SubstIndex;
if (SemaRef.ArgumentPackSubstitutionIndex == -1 && PackIndex)
SubstIndex.emplace(SemaRef, *PackIndex);

return inherited::TransformSubstTemplateTypeParmType(TLB, TL);
}

CXXRecordDecl::LambdaDependencyKind
ComputeLambdaDependency(LambdaScopeInfo *LSI) {
if (auto TypeAlias = getEnclosingTypeAliasTemplateDecl(getSema());
Expand Down Expand Up @@ -3056,6 +3071,58 @@ namespace {

} // namespace

namespace {

struct ExpandPackedTypeConstraints
: TreeTransform<ExpandPackedTypeConstraints> {

using inherited = TreeTransform<ExpandPackedTypeConstraints>;

ExpandPackedTypeConstraints(Sema &SemaRef) : inherited(SemaRef) {}

using inherited::TransformTemplateTypeParmType;

QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
TemplateTypeParmTypeLoc TL, bool) {
const TemplateTypeParmType *T = TL.getTypePtr();
if (!T->isParameterPack()) {
TemplateTypeParmTypeLoc NewTL =
TLB.push<TemplateTypeParmTypeLoc>(TL.getType());
NewTL.setNameLoc(TL.getNameLoc());
return TL.getType();
}

assert(SemaRef.ArgumentPackSubstitutionIndex != -1);

QualType Result = SemaRef.Context.getSubstTemplateTypeParmType(
TL.getType(), T->getDecl(), T->getIndex(),
SemaRef.ArgumentPackSubstitutionIndex);
SubstTemplateTypeParmTypeLoc NewTL =
TLB.push<SubstTemplateTypeParmTypeLoc>(Result);
NewTL.setNameLoc(TL.getNameLoc());
return Result;
}

QualType TransformSubstTemplateTypeParmType(TypeLocBuilder &TLB,
SubstTemplateTypeParmTypeLoc TL) {
const SubstTemplateTypeParmType *T = TL.getTypePtr();
if (T->getPackIndex()) {
SubstTemplateTypeParmTypeLoc TypeLoc =
TLB.push<SubstTemplateTypeParmTypeLoc>(TL.getType());
TypeLoc.setNameLoc(TL.getNameLoc());
return TypeLoc.getType();
}
return inherited::TransformSubstTemplateTypeParmType(TLB, TL);
}

bool SubstTemplateArguments(ArrayRef<TemplateArgumentLoc> Args,
TemplateArgumentListInfo &Out) {
return inherited::TransformTemplateArguments(Args.begin(), Args.end(), Out);
}
};

} // namespace

bool Sema::SubstTypeConstraint(
TemplateTypeParmDecl *Inst, const TypeConstraint *TC,
const MultiLevelTemplateArgumentList &TemplateArgs,
Expand All @@ -3064,9 +3131,62 @@ bool Sema::SubstTypeConstraint(
TC->getTemplateArgsAsWritten();

if (!EvaluateConstraints) {
Inst->setTypeConstraint(TC->getConceptReference(),
TC->getImmediatelyDeclaredConstraint());
return false;
bool ShouldExpandExplicitTemplateArgs =
TemplArgInfo && ArgumentPackSubstitutionIndex != -1 &&
llvm::any_of(TemplArgInfo->arguments(), [](auto &Arg) {
return Arg.getArgument().containsUnexpandedParameterPack();
});

// We want to transform the packs into Subst* nodes for type constraints
// inside a pack expansion. For example,
//
// template <class... Ts> void foo() {
// bar([](C<Ts> auto value) {}...);
// }
//
// As we expand Ts in the process of instantiating foo(), and retain
// the original template depths of Ts until the constraint evaluation, we
// would otherwise have no chance to expand Ts by the time of evaluating
// C<auto, Ts>.
//
// So we form a Subst* node for Ts along with a proper substitution index
// here, and substitute the node with a complete MLTAL later in evaluation.
if (ShouldExpandExplicitTemplateArgs) {
TemplateArgumentListInfo InstArgs;
InstArgs.setLAngleLoc(TemplArgInfo->LAngleLoc);
InstArgs.setRAngleLoc(TemplArgInfo->RAngleLoc);
if (ExpandPackedTypeConstraints(*this).SubstTemplateArguments(
TemplArgInfo->arguments(), InstArgs))
return true;

// The type of the original parameter.
auto *ConstraintExpr = TC->getImmediatelyDeclaredConstraint();
QualType ConstrainedType;

if (auto *FE = dyn_cast<CXXFoldExpr>(ConstraintExpr)) {
assert(FE->getLHS());
ConstraintExpr = FE->getLHS();
}
auto *CSE = cast<ConceptSpecializationExpr>(ConstraintExpr);
assert(!CSE->getTemplateArguments().empty() &&
"Empty template arguments?");
ConstrainedType = CSE->getTemplateArguments()[0].getAsType();
assert(!ConstrainedType.isNull() &&
"Failed to extract the original ConstrainedType?");

return AttachTypeConstraint(
TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(),
TC->getNamedConcept(),
/*FoundDecl=*/TC->getConceptReference()->getFoundDecl(), &InstArgs,
Inst, ConstrainedType,
Inst->isParameterPack()
? cast<CXXFoldExpr>(TC->getImmediatelyDeclaredConstraint())
->getEllipsisLoc()
: SourceLocation());
}
Inst->setTypeConstraint(TC->getConceptReference(),
TC->getImmediatelyDeclaredConstraint());
return false;
}

TemplateArgumentListInfo InstArgs;
Expand All @@ -3082,6 +3202,7 @@ bool Sema::SubstTypeConstraint(
TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(),
TC->getNamedConcept(),
/*FoundDecl=*/TC->getConceptReference()->getFoundDecl(), &InstArgs, Inst,
Context.getTypeDeclType(Inst),
Inst->isParameterPack()
? cast<CXXFoldExpr>(TC->getImmediatelyDeclaredConstraint())
->getEllipsisLoc()
Expand Down
8 changes: 6 additions & 2 deletions clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3035,7 +3035,9 @@ InventTemplateParameter(TypeProcessingState &state, QualType T,
AutoLoc.getNestedNameSpecifierLoc(), AutoLoc.getConceptNameInfo(),
AutoLoc.getNamedConcept(), /*FoundDecl=*/AutoLoc.getFoundDecl(),
AutoLoc.hasExplicitTemplateArgs() ? &TAL : nullptr,
InventedTemplateParam, D.getEllipsisLoc());
InventedTemplateParam,
S.Context.getTypeDeclType(InventedTemplateParam),
D.getEllipsisLoc());
}
} else {
// The 'auto' appears in the decl-specifiers; we've not finished forming
Expand Down Expand Up @@ -3072,7 +3074,9 @@ InventTemplateParameter(TypeProcessingState &state, QualType T,
/*FoundDecl=*/
USD ? cast<NamedDecl>(USD) : CD,
TemplateId->LAngleLoc.isValid() ? &TemplateArgsInfo : nullptr,
InventedTemplateParam, D.getEllipsisLoc());
InventedTemplateParam,
S.Context.getTypeDeclType(InventedTemplateParam),
D.getEllipsisLoc());
}
}
}
Expand Down
54 changes: 54 additions & 0 deletions clang/test/SemaCXX/fold_lambda_with_variadics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,57 @@ void foo() {
}

} // namespace GH99877

namespace GH101754 {

template <typename... Ts> struct Overloaded : Ts... {
using Ts::operator()...;
};

template <typename... Ts> Overloaded(Ts...) -> Overloaded<Ts...>;

template <class T, class U>
concept same_as = __is_same(T, U); // #same_as

template <typename... Ts> constexpr auto foo() {
return Overloaded{[](same_as<Ts> auto value) { return value; }...}; // #lambda
}

static_assert(foo<int, double>()(123) == 123);
static_assert(foo<int, double>()(2.718) == 2.718);

static_assert(foo<int, double>()('c'));
// expected-error@-1 {{no matching function}}

// expected-note@#lambda {{constraints not satisfied}}
// expected-note@#lambda {{'same_as<char, int>' evaluated to false}}
// expected-note@#same_as {{evaluated to false}}

// expected-note@#lambda {{constraints not satisfied}}
// expected-note@#lambda {{'same_as<char, double>' evaluated to false}}
// expected-note@#same_as {{evaluated to false}}

template <class T, class U, class V>
concept C = same_as<T, U> && same_as<U, V>; // #C

template <typename... Ts> constexpr auto bar() {
return ([]<class Up>() {
return Overloaded{[](C<Up, Ts> auto value) { // #bar
return value;
}...};
}.template operator()<Ts>(), ...);
}
static_assert(bar<int, float>()(3.14f)); // OK, bar() returns the last overload i.e. <float>.

static_assert(bar<int, float>()(123));
// expected-error@-1 {{no matching function}}
// expected-note@#bar {{constraints not satisfied}}
// expected-note@#bar {{'C<int, float, int>' evaluated to false}}
// expected-note@#C {{evaluated to false}}

// expected-note@#bar {{constraints not satisfied}}
// expected-note@#bar {{'C<int, float, float>' evaluated to false}}
// expected-note@#C {{evaluated to false}}
// expected-note@#same_as 2{{evaluated to false}}

} // namespace GH101754
Loading