Skip to content

Commit

Permalink
[Clang][Sema] Retain the expanding index for unevaluated type constra…
Browse files Browse the repository at this point in the history
…ints
  • Loading branch information
Younan Zhang authored and zyn0217 committed Sep 21, 2024
1 parent c28e268 commit 1deb74f
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 7 deletions.
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
115 changes: 112 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,46 @@ 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;
}

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 +3119,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 +3190,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

0 comments on commit 1deb74f

Please sign in to comment.