Skip to content

Commit babdef2

Browse files
author
Erich Keane
committed
Re-apply "Deferred Concept Instantiation Implementation"
This reverts commit 95d94a6. This implements the deferred concepts instantiation, which should allow the libstdc++ ranges to properly compile, and for the CRTP to work for constrained functions. Since the last attempt, this has fixed the issues from @wlei and @mordante. Differential Revision: https://reviews.llvm.org/D126907
1 parent e0cdafe commit babdef2

31 files changed

+1816
-243
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,10 @@ C++20 Feature Support
345345
`Issue 50455 <https://github.com/llvm/llvm-project/issues/50455>`_,
346346
`Issue 54872 <https://github.com/llvm/llvm-project/issues/54872>`_,
347347
`Issue 54587 <https://github.com/llvm/llvm-project/issues/54587>`_.
348+
- Clang now correctly delays the instantiation of function constraints until
349+
the time of checking, which should now allow the libstdc++ ranges implementation
350+
to work for at least trivial examples. This fixes
351+
`Issue 44178 <https://github.com/llvm/llvm-project/issues/44178>`_.
348352

349353
C++2b Feature Support
350354
^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/AST/ASTContext.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2669,6 +2669,11 @@ class ASTContext : public RefCountedBase<ASTContext> {
26692669
/// template.
26702670
bool hasSameTemplateName(const TemplateName &X, const TemplateName &Y) const;
26712671

2672+
/// Determine whether two Friend functions are different because constraints
2673+
/// that refer to an enclosing template, according to [temp.friend] p9.
2674+
bool FriendsDifferByConstraints(const FunctionDecl *X,
2675+
const FunctionDecl *Y) const;
2676+
26722677
/// Determine whether the two declarations refer to the same entity.
26732678
bool isSameEntity(const NamedDecl *X, const NamedDecl *Y) const;
26742679

clang/include/clang/AST/Decl.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2474,6 +2474,19 @@ class FunctionDecl : public DeclaratorDecl,
24742474
getCanonicalDecl()->FunctionDeclBits.IsMultiVersion = V;
24752475
}
24762476

2477+
// Sets that this is a constrained friend where the constraint refers to an
2478+
// enclosing template.
2479+
void setFriendConstraintRefersToEnclosingTemplate(bool V = true) {
2480+
getCanonicalDecl()
2481+
->FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate = V;
2482+
}
2483+
// Indicates this function is a constrained friend, where the constraint
2484+
// refers to an enclosing template for hte purposes of [temp.friend]p9.
2485+
bool FriendConstraintRefersToEnclosingTemplate() const {
2486+
return getCanonicalDecl()
2487+
->FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate;
2488+
}
2489+
24772490
/// Gets the kind of multiversioning attribute this declaration has. Note that
24782491
/// this can return a value even if the function is not multiversion, such as
24792492
/// the case of 'target'.

clang/include/clang/AST/DeclBase.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1664,10 +1664,14 @@ class DeclContext {
16641664

16651665
/// Indicates if the function uses Floating Point Constrained Intrinsics
16661666
uint64_t UsesFPIntrin : 1;
1667+
1668+
// Indicates this function is a constrained friend, where the constraint
1669+
// refers to an enclosing template for hte purposes of [temp.friend]p9.
1670+
uint64_t FriendConstraintRefersToEnclosingTemplate : 1;
16671671
};
16681672

16691673
/// Number of non-inherited bits in FunctionDeclBitfields.
1670-
enum { NumFunctionDeclBits = 28 };
1674+
enum { NumFunctionDeclBits = 29 };
16711675

16721676
/// Stores the bits used by CXXConstructorDecl. If modified
16731677
/// NumCXXConstructorDeclBits and the accessor
@@ -1679,12 +1683,12 @@ class DeclContext {
16791683
/// For the bits in FunctionDeclBitfields.
16801684
uint64_t : NumFunctionDeclBits;
16811685

1682-
/// 23 bits to fit in the remaining available space.
1686+
/// 22 bits to fit in the remaining available space.
16831687
/// Note that this makes CXXConstructorDeclBitfields take
16841688
/// exactly 64 bits and thus the width of NumCtorInitializers
16851689
/// will need to be shrunk if some bit is added to NumDeclContextBitfields,
16861690
/// NumFunctionDeclBitfields or CXXConstructorDeclBitfields.
1687-
uint64_t NumCtorInitializers : 20;
1691+
uint64_t NumCtorInitializers : 19;
16881692
uint64_t IsInheritingConstructor : 1;
16891693

16901694
/// Whether this constructor has a trail-allocated explicit specifier.

clang/include/clang/Sema/Sema.h

Lines changed: 120 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3673,6 +3673,31 @@ class Sema final {
36733673
bool ConsiderCudaAttrs = true,
36743674
bool ConsiderRequiresClauses = true);
36753675

3676+
// Calculates whether the expression Constraint depends on an enclosing
3677+
// template, for the purposes of [temp.friend] p9.
3678+
// TemplateDepth is the 'depth' of the friend function, which is used to
3679+
// compare whether a declaration reference is referring to a containing
3680+
// template, or just the current friend function. A 'lower' TemplateDepth in
3681+
// the AST refers to a 'containing' template. As the constraint is
3682+
// uninstantiated, this is relative to the 'top' of the TU.
3683+
bool ConstraintExpressionDependsOnEnclosingTemplate(unsigned TemplateDepth,
3684+
const Expr *Constraint);
3685+
3686+
// Calculates whether the friend function depends on an enclosing template for
3687+
// the purposes of [temp.friend] p9.
3688+
bool FriendConstraintsDependOnEnclosingTemplate(const FunctionDecl *FD);
3689+
3690+
// Calculates whether two constraint expressions are equal irrespective of a
3691+
// difference in 'depth'. This takes a pair of optional 'NamedDecl's 'Old' and
3692+
// 'New', which are the "source" of the constraint, since this is necessary
3693+
// for figuring out the relative 'depth' of the constraint. The depth of the
3694+
// 'primary template' and the 'instantiated from' templates aren't necessarily
3695+
// the same, such as a case when one is a 'friend' defined in a class.
3696+
bool AreConstraintExpressionsEqual(const NamedDecl *Old,
3697+
const Expr *OldConstr,
3698+
const NamedDecl *New,
3699+
const Expr *NewConstr);
3700+
36763701
enum class AllowedExplicit {
36773702
/// Allow no explicit functions to be used.
36783703
None,
@@ -7152,6 +7177,21 @@ class Sema final {
71527177
LocalInstantiationScope &Scope,
71537178
const MultiLevelTemplateArgumentList &TemplateArgs);
71547179

7180+
/// used by SetupConstraintCheckingTemplateArgumentsAndScope to recursively(in
7181+
/// the case of lambdas) set up the LocalInstantiationScope of the current
7182+
/// function.
7183+
bool SetupConstraintScope(
7184+
FunctionDecl *FD, llvm::Optional<ArrayRef<TemplateArgument>> TemplateArgs,
7185+
MultiLevelTemplateArgumentList MLTAL, LocalInstantiationScope &Scope);
7186+
7187+
/// Used during constraint checking, sets up the constraint template arguemnt
7188+
/// lists, and calls SetupConstraintScope to set up the
7189+
/// LocalInstantiationScope to have the proper set of ParVarDecls configured.
7190+
llvm::Optional<MultiLevelTemplateArgumentList>
7191+
SetupConstraintCheckingTemplateArgumentsAndScope(
7192+
FunctionDecl *FD, llvm::Optional<ArrayRef<TemplateArgument>> TemplateArgs,
7193+
LocalInstantiationScope &Scope);
7194+
71557195
public:
71567196
const NormalizedConstraint *
71577197
getNormalizedAssociatedConstraints(
@@ -7194,6 +7234,39 @@ class Sema final {
71947234
bool CheckConstraintSatisfaction(
71957235
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
71967236
const MultiLevelTemplateArgumentList &TemplateArgLists,
7237+
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction) {
7238+
llvm::SmallVector<Expr *, 4> Converted;
7239+
return CheckConstraintSatisfaction(Template, ConstraintExprs, Converted,
7240+
TemplateArgLists, TemplateIDRange,
7241+
Satisfaction);
7242+
}
7243+
7244+
/// \brief Check whether the given list of constraint expressions are
7245+
/// satisfied (as if in a 'conjunction') given template arguments.
7246+
/// Additionally, takes an empty list of Expressions which is populated with
7247+
/// the instantiated versions of the ConstraintExprs.
7248+
/// \param Template the template-like entity that triggered the constraints
7249+
/// check (either a concept or a constrained entity).
7250+
/// \param ConstraintExprs a list of constraint expressions, treated as if
7251+
/// they were 'AND'ed together.
7252+
/// \param ConvertedConstraints a out parameter that will get populated with
7253+
/// the instantiated version of the ConstraintExprs if we successfully checked
7254+
/// satisfaction.
7255+
/// \param TemplateArgList the multi-level list of template arguments to
7256+
/// substitute into the constraint expression. This should be relative to the
7257+
/// top-level (hence multi-level), since we need to instantiate fully at the
7258+
/// time of checking.
7259+
/// \param TemplateIDRange The source range of the template id that
7260+
/// caused the constraints check.
7261+
/// \param Satisfaction if true is returned, will contain details of the
7262+
/// satisfaction, with enough information to diagnose an unsatisfied
7263+
/// expression.
7264+
/// \returns true if an error occurred and satisfaction could not be checked,
7265+
/// false otherwise.
7266+
bool CheckConstraintSatisfaction(
7267+
const NamedDecl *Template, ArrayRef<const Expr *> ConstraintExprs,
7268+
llvm::SmallVectorImpl<Expr *> &ConvertedConstraints,
7269+
const MultiLevelTemplateArgumentList &TemplateArgList,
71977270
SourceRange TemplateIDRange, ConstraintSatisfaction &Satisfaction);
71987271

71997272
/// \brief Check whether the given non-dependent constraint expression is
@@ -7213,8 +7286,8 @@ class Sema final {
72137286
/// \returns true if an error occurred, false otherwise.
72147287
bool CheckFunctionConstraints(const FunctionDecl *FD,
72157288
ConstraintSatisfaction &Satisfaction,
7216-
SourceLocation UsageLoc = SourceLocation());
7217-
7289+
SourceLocation UsageLoc = SourceLocation(),
7290+
bool ForOverloadResolution = false);
72187291

72197292
/// \brief Ensure that the given template arguments satisfy the constraints
72207293
/// associated with the given template, emitting a diagnostic if they do not.
@@ -8222,12 +8295,19 @@ class Sema final {
82228295
TPL_TemplateTemplateArgumentMatch
82238296
};
82248297

8225-
bool TemplateParameterListsAreEqual(TemplateParameterList *New,
8226-
TemplateParameterList *Old,
8227-
bool Complain,
8228-
TemplateParameterListEqualKind Kind,
8229-
SourceLocation TemplateArgLoc
8230-
= SourceLocation());
8298+
bool TemplateParameterListsAreEqual(
8299+
const NamedDecl *NewInstFrom, TemplateParameterList *New,
8300+
const NamedDecl *OldInstFrom, TemplateParameterList *Old, bool Complain,
8301+
TemplateParameterListEqualKind Kind,
8302+
SourceLocation TemplateArgLoc = SourceLocation());
8303+
8304+
bool TemplateParameterListsAreEqual(
8305+
TemplateParameterList *New, TemplateParameterList *Old, bool Complain,
8306+
TemplateParameterListEqualKind Kind,
8307+
SourceLocation TemplateArgLoc = SourceLocation()) {
8308+
return TemplateParameterListsAreEqual(nullptr, New, nullptr, Old, Complain,
8309+
Kind, TemplateArgLoc);
8310+
}
82318311

82328312
bool CheckTemplateDeclScope(Scope *S, TemplateParameterList *TemplateParams);
82338313

@@ -8962,7 +9042,8 @@ class Sema final {
89629042

89639043
MultiLevelTemplateArgumentList getTemplateInstantiationArgs(
89649044
const NamedDecl *D, const TemplateArgumentList *Innermost = nullptr,
8965-
bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr);
9045+
bool RelativeToPrimary = false, const FunctionDecl *Pattern = nullptr,
9046+
bool LookBeyondLambda = false, bool IncludeContainingStruct = false);
89669047

89679048
/// A context in which code is being synthesized (where a source location
89689049
/// alone is not sufficient to identify the context). This covers template
@@ -9670,23 +9751,21 @@ class Sema final {
96709751
const MultiLevelTemplateArgumentList &TemplateArgs,
96719752
SourceLocation Loc, DeclarationName Entity);
96729753

9673-
TypeSourceInfo *SubstFunctionDeclType(TypeSourceInfo *T,
9674-
const MultiLevelTemplateArgumentList &TemplateArgs,
9675-
SourceLocation Loc,
9676-
DeclarationName Entity,
9677-
CXXRecordDecl *ThisContext,
9678-
Qualifiers ThisTypeQuals);
9754+
TypeSourceInfo *SubstFunctionDeclType(
9755+
TypeSourceInfo *T, const MultiLevelTemplateArgumentList &TemplateArgs,
9756+
SourceLocation Loc, DeclarationName Entity, CXXRecordDecl *ThisContext,
9757+
Qualifiers ThisTypeQuals, bool EvaluateConstraints = true);
96799758
void SubstExceptionSpec(FunctionDecl *New, const FunctionProtoType *Proto,
96809759
const MultiLevelTemplateArgumentList &Args);
96819760
bool SubstExceptionSpec(SourceLocation Loc,
96829761
FunctionProtoType::ExceptionSpecInfo &ESI,
96839762
SmallVectorImpl<QualType> &ExceptionStorage,
96849763
const MultiLevelTemplateArgumentList &Args);
9685-
ParmVarDecl *SubstParmVarDecl(ParmVarDecl *D,
9686-
const MultiLevelTemplateArgumentList &TemplateArgs,
9687-
int indexAdjustment,
9688-
Optional<unsigned> NumExpansions,
9689-
bool ExpectParameterPack);
9764+
ParmVarDecl *
9765+
SubstParmVarDecl(ParmVarDecl *D,
9766+
const MultiLevelTemplateArgumentList &TemplateArgs,
9767+
int indexAdjustment, Optional<unsigned> NumExpansions,
9768+
bool ExpectParameterPack, bool EvaluateConstraints = true);
96909769
bool SubstParmTypes(SourceLocation Loc, ArrayRef<ParmVarDecl *> Params,
96919770
const FunctionProtoType::ExtParameterInfo *ExtParamInfos,
96929771
const MultiLevelTemplateArgumentList &TemplateArgs,
@@ -9696,6 +9775,25 @@ class Sema final {
96969775
ExprResult SubstExpr(Expr *E,
96979776
const MultiLevelTemplateArgumentList &TemplateArgs);
96989777

9778+
// A RAII type used by the TemplateDeclInstantiator and TemplateInstantiator
9779+
// to disable constraint evaluation, then restore the state.
9780+
template <typename InstTy> struct ConstraintEvalRAII {
9781+
InstTy &TI;
9782+
bool OldValue;
9783+
9784+
ConstraintEvalRAII(InstTy &TI)
9785+
: TI(TI), OldValue(TI.getEvaluateConstraints()) {
9786+
TI.setEvaluateConstraints(false);
9787+
}
9788+
~ConstraintEvalRAII() { TI.setEvaluateConstraints(OldValue); }
9789+
};
9790+
9791+
// Unlike the above, this evaluates constraints, which should only happen at
9792+
// 'constraint checking' time.
9793+
ExprResult
9794+
SubstConstraintExpr(Expr *E,
9795+
const MultiLevelTemplateArgumentList &TemplateArgs);
9796+
96999797
/// Substitute the given template arguments into a list of
97009798
/// expressions, expanding pack expansions if required.
97019799
///
@@ -9725,7 +9823,6 @@ class Sema final {
97259823
const MultiLevelTemplateArgumentList &TemplateArgs,
97269824
TemplateArgumentListInfo &Outputs);
97279825

9728-
97299826
Decl *SubstDecl(Decl *D, DeclContext *Owner,
97309827
const MultiLevelTemplateArgumentList &TemplateArgs);
97319828

@@ -9816,7 +9913,8 @@ class Sema final {
98169913
const MultiLevelTemplateArgumentList &TemplateArgs);
98179914

98189915
bool SubstTypeConstraint(TemplateTypeParmDecl *Inst, const TypeConstraint *TC,
9819-
const MultiLevelTemplateArgumentList &TemplateArgs);
9916+
const MultiLevelTemplateArgumentList &TemplateArgs,
9917+
bool EvaluateConstraint);
98209918

98219919
bool InstantiateDefaultArgument(SourceLocation CallLoc, FunctionDecl *FD,
98229920
ParmVarDecl *Param);

clang/include/clang/Sema/Template.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,7 @@ enum class TemplateSubstitutionKind : char {
503503
const MultiLevelTemplateArgumentList &TemplateArgs;
504504
Sema::LateInstantiatedAttrVec* LateAttrs = nullptr;
505505
LocalInstantiationScope *StartingScope = nullptr;
506+
bool EvaluateConstraints = true;
506507

507508
/// A list of out-of-line class template partial
508509
/// specializations that will need to be instantiated after the
@@ -526,6 +527,13 @@ enum class TemplateSubstitutionKind : char {
526527
SubstIndex(SemaRef, SemaRef.ArgumentPackSubstitutionIndex),
527528
Owner(Owner), TemplateArgs(TemplateArgs) {}
528529

530+
void setEvaluateConstraints(bool B) {
531+
EvaluateConstraints = B;
532+
}
533+
bool getEvaluateConstraints() {
534+
return EvaluateConstraints;
535+
}
536+
529537
// Define all the decl visitors using DeclNodes.inc
530538
#define DECL(DERIVED, BASE) \
531539
Decl *Visit ## DERIVED ## Decl(DERIVED ## Decl *D);

clang/lib/AST/ASTContext.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6452,6 +6452,31 @@ static bool hasSameOverloadableAttrs(const FunctionDecl *A,
64526452
return true;
64536453
}
64546454

6455+
bool ASTContext::FriendsDifferByConstraints(const FunctionDecl *X,
6456+
const FunctionDecl *Y) const {
6457+
// If these aren't friends, then they aren't friends that differ by
6458+
// constraints.
6459+
if (!X->getFriendObjectKind() || !Y->getFriendObjectKind())
6460+
return false;
6461+
6462+
// If the the two functions share lexical declaration context, they are not in
6463+
// separate instantations, and thus in the same scope.
6464+
if (X->getLexicalDeclContext() == Y->getLexicalDeclContext())
6465+
return false;
6466+
6467+
if (!X->getDescribedFunctionTemplate()) {
6468+
assert(!Y->getDescribedFunctionTemplate() &&
6469+
"How would these be the same if they aren't both templates?");
6470+
6471+
// If these friends don't have constraints, they aren't constrained, and
6472+
// thus don't fall under temp.friend p9. Else the simple presence of a
6473+
// constraint makes them unique.
6474+
return X->getTrailingRequiresClause();
6475+
}
6476+
6477+
return X->FriendConstraintRefersToEnclosingTemplate();
6478+
}
6479+
64556480
bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
64566481
if (X == Y)
64576482
return true;
@@ -6532,6 +6557,10 @@ bool ASTContext::isSameEntity(const NamedDecl *X, const NamedDecl *Y) const {
65326557
FuncY->getTrailingRequiresClause()))
65336558
return false;
65346559

6560+
// Constrained friends are different in certain cases, see: [temp.friend]p9.
6561+
if (FriendsDifferByConstraints(FuncX, FuncY))
6562+
return false;
6563+
65356564
auto GetTypeAsWritten = [](const FunctionDecl *FD) {
65366565
// Map to the first declaration that we've already merged into this one.
65376566
// The TSI of redeclarations might not match (due to calling conventions

clang/lib/AST/ASTImporter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3701,6 +3701,8 @@ ExpectedDecl ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
37013701
ToFunction->setDefaulted(D->isDefaulted());
37023702
ToFunction->setExplicitlyDefaulted(D->isExplicitlyDefaulted());
37033703
ToFunction->setDeletedAsWritten(D->isDeletedAsWritten());
3704+
ToFunction->setFriendConstraintRefersToEnclosingTemplate(
3705+
D->FriendConstraintRefersToEnclosingTemplate());
37043706
ToFunction->setRangeEnd(ToEndLoc);
37053707

37063708
// Set the parameters.

clang/lib/AST/Decl.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2970,6 +2970,7 @@ FunctionDecl::FunctionDecl(Kind DK, ASTContext &C, DeclContext *DC,
29702970
FunctionDeclBits.IsMultiVersion = false;
29712971
FunctionDeclBits.IsCopyDeductionCandidate = false;
29722972
FunctionDeclBits.HasODRHash = false;
2973+
FunctionDeclBits.FriendConstraintRefersToEnclosingTemplate = false;
29732974
if (TrailingRequiresClause)
29742975
setTrailingRequiresClause(TrailingRequiresClause);
29752976
}

0 commit comments

Comments
 (0)